В ответе на вызов PHP SoapClient отсутствуют части ответа
Вопрос
У меня возникли проблемы с синтаксическим анализом PHP ответа на вызов SoapClient.Для некоторых типов ответов он возвращает массивы пустых объектов stdClass вместо инициализированных объектов stdClass.
Сервер представляет собой веб-сервис Java, развернутый с помощью axis2 на tomcat6.Подпись Java проблемного вызова службы public Course getCourseDetails(Long courseId)
Курс представляет собой стандартный POJO, определяемый как:
public class Course {
private Long id;
private List<Hole> holes;
private String name;
private String tees;
//etc...
}
Hole — это стандартный POJO, содержащий только примитивные члены.
При вызове из PHP элемент «holes» представляет собой массив правильной длины, но каждое отверстие пусто.
$args = array();
$args["courseId"] = $courseId;
$response = $client->getCourseDetails($args);
$course = $response->return;
//course has all of its primitive members set correctly: good
$holes = $course->holes;
//holes is an array with count = 18: good
$hole = $holes[0];
//hole is an empty stdClass: bad
Распечатка возвращенного XML с помощью $soapClient->__getLastResponse()
как выглядит правильное представление:
<?xml version='1.0' encoding='utf-8'?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<ns:getCourseDetailsResponse xmlns:ns="http://webservice.golfstats">
<ns:return xmlns:ax21="http://datastructures.server.golfstats/xsd" xmlns:ax22="http://util.java/xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ax24="http://uuid.eaio.com/xsd" xsi:type="ax21:Course">
<ax21:courseLocation>Faketown, VA</ax21:courseLocation>
<ax21:courseName>Fake Links</ax21:courseName>
<ax21:dateAdded>2003-01-02</ax21:dateAdded>
<ax21:holes><ax21:id>1</ax21:id><ax21:number>1</ax21:number><ax21:par>4</ax21:par><ax21:yardage>345</ax21:yardage></ax21:holes>
<ax21:holes><ax21:id>2</ax21:id><ax21:number>2</ax21:number><ax21:par>3</ax21:par><ax21:yardage>150</ax21:yardage></ax21:holes>
<ax21:holes><ax21:id>3</ax21:id><ax21:number>3</ax21:number><ax21:par>5</ax21:par><ax21:yardage>502</ax21:yardage></ax21:holes>
<ax21:holes><ax21:id>4</ax21:id><ax21:number>4</ax21:number><ax21:par>4</ax21:par><ax21:yardage>345</ax21:yardage></ax21:holes>
<ax21:holes><ax21:id>5</ax21:id><ax21:number>5</ax21:number><ax21:par>4</ax21:par><ax21:yardage>345</ax21:yardage></ax21:holes>
<ax21:holes><ax21:id>6</ax21:id><ax21:number>6</ax21:number><ax21:par>4</ax21:par><ax21:yardage>345</ax21:yardage></ax21:holes>
<ax21:holes><ax21:id>7</ax21:id><ax21:number>7</ax21:number><ax21:par>4</ax21:par><ax21:yardage>345</ax21:yardage></ax21:holes>
<ax21:holes><ax21:id>8</ax21:id><ax21:number>8</ax21:number><ax21:par>4</ax21:par><ax21:yardage>345</ax21:yardage></ax21:holes>
<ax21:holes><ax21:id>9</ax21:id><ax21:number>9</ax21:number><ax21:par>4</ax21:par><ax21:yardage>345</ax21:yardage></ax21:holes>
<ax21:holes><ax21:id>10</ax21:id><ax21:number>10</ax21:number><ax21:par>4</ax21:par><ax21:yardage>345</ax21:yardage></ax21:holes>
<ax21:holes><ax21:id>11</ax21:id><ax21:number>11</ax21:number><ax21:par>4</ax21:par><ax21:yardage>345</ax21:yardage></ax21:holes>
<ax21:holes><ax21:id>12</ax21:id><ax21:number>12</ax21:number><ax21:par>4</ax21:par><ax21:yardage>345</ax21:yardage></ax21:holes>
<ax21:holes><ax21:id>13</ax21:id><ax21:number>13</ax21:number><ax21:par>4</ax21:par><ax21:yardage>345</ax21:yardage></ax21:holes>
<ax21:holes><ax21:id>14</ax21:id><ax21:number>14</ax21:number><ax21:par>4</ax21:par><ax21:yardage>345</ax21:yardage></ax21:holes>
<ax21:holes><ax21:id>15</ax21:id><ax21:number>15</ax21:number><ax21:par>4</ax21:par><ax21:yardage>345</ax21:yardage></ax21:holes>
<ax21:holes><ax21:id>16</ax21:id><ax21:number>16</ax21:number><ax21:par>4</ax21:par><ax21:yardage>345</ax21:yardage></ax21:holes>
<ax21:holes><ax21:id>17</ax21:id><ax21:number>17</ax21:number><ax21:par>4</ax21:par><ax21:yardage>345</ax21:yardage></ax21:holes>
<ax21:holes><ax21:id>18</ax21:id><ax21:number>18</ax21:number><ax21:par>4</ax21:par><ax21:yardage>345</ax21:yardage></ax21:holes>
<ax21:id>1</ax21:id>
<ax21:rating>68.5</ax21:rating>
<ax21:slope>113</ax21:slope>
<ax21:tees>Blue</ax21:tees>
</ns:return>
</ns:getCourseDetailsResponse>
</soapenv:Body>
</soapenv:Envelope>
Почему каждая дырка представляет собой пустой stdClass?Известны ли ограничения на количество уровней, на которых SoapClient будет анализировать ответ?
Решение 3
Похоже, это ошибка в PHP. http://bugs.php.net/bug.php?id=49070
К сожалению, система отслеживания ошибок не позволяет мне это комментировать.
Другие советы
У меня была похожая проблема.Я прошел каждую итерацию, через которую вы прошли.По счастливой случайности я отключил кеширование "soap.wsdl_cache", либо изменив файл PHP.INI, либо ini_set('soap.wsdl_cache', WSDL_CACHE_NONE);
и по моему следующему запросу все недостающие данные были заполнены.Это может легко произойти, поскольку по умолчанию для параметра «soap.wsdl_cache_ttl» установлено значение «86400», что составляет 60 дней.
Я обнаружил, что на мыльном сервере был изменен код.Создание нового wsdl.Кэшированный клиентский wsdl на тот момент был устаревшим.Вы могли бы подумать, что, по крайней мере, с каждым запросом будет отправляться какой-то хэш контрольной суммы, чтобы убедиться, что wsdl изменился, но это не так.
Чтобы решить эту проблему и по-прежнему использовать кэширование, я создал файл wsdl, который можно было использовать локально.
$cache = Services_Utilities::getCacheResource();
if (!$cache->test(self::CACHE_KEY)) {
$data = file_get_contents($wsdl);
$cache->save($data, self::CACHE_KEY);
file_put_contents($newWsdl, $data);
if (file_exists($newWsdl)) {
$wsdl = $newWsdl;
}
} else {
if (file_exists($newWsdl)) {
$wsdl = $newWsdl;
}
}
// Remove $newWsdl when necessary
// unset($newWsdl);
Надеюсь, это поможет вам или кому-либо еще, кто случайно зайдет и столкнется с аналогичной проблемой.
Вы все это выяснили путем отладки или распечатки содержимого объекта PHP (print_r, var_dump)?
Пробовали ли вы распечатать фактическую строку ответа SOAP (а не объект PHP)?Вы можете сделать это, создав SoapClient с установленной опцией отладки:
$soapClient = new SoapClient( "http://your.soap.server.com/services/yourWsdl.wsdl", array("trace" => 1));
Затем, когда вы используете клиент для выполнения вызова SOAP, вы можете просмотреть как строки запроса, так и строки ответа.
$response = $soapClient->getCourseDetails($params);
$requestAsString = $soapClient->__getLastRequest();
$responseAsString = $soapClient->__getLastResponse();
Это может помочь вам понять, что делает SoapClient при преобразовании ответа в объект PHP. Дополнительная информация о __getLastResponse().
И вот спустя почти полтора года...
По моему недавнему полупохожему опыту, это не была ошибка PHP.Это проблема, связанная с тем, как написан ваш веб-сервис и как PHP считывает выходные данные.У меня возникла аналогичная проблема (даже когда getLastResponse возвращал правильный XML) и я обнаружил, что проблема была не столько в PHP или моей функции SOAP, сколько в том, что результат «сломанной» функции не был явно определенный курсор.
Пример неправильного определения курсора:
PROCEDURE GetBlahByBlahID(IN IN_BLAH_ID VARCHAR, IN IN_BLAHPKG VARCHAR,
OUT result CURSOR
) BEGIN ...
Пример хорошего определения курсора:
PROCEDURE GetBlahByBlahID(IN IN_BLAH_ID VARCHAR, IN IN_BLAHPKG VARCHAR,
OUT result CURSOR ( BLAH VARCHAR(250),
BLAH2 VARCHAR(250),
BLAH_DATE DATE,
BLAH3 VARCHAR(250))) BEGIN ...
Судя по всему, Java прекрасно справляется с «плохим»/неявным выводом, но PHP возвращает массив нулевых объектов.
Не уверен, что это вам поможет, но определение вывода функции веб-сервиса как «хорошего» способа, описанного выше, решило мою проблему.