PHP SoapClient call response missing parts of answer
I am having trouble with PHP parsing of a SoapClient call's response. For some types of answers, it is returning arrays of empty stdClass objects instead of initialized stdClass objects.
The server is a java webservice deployed with axis2 on tomcat6. The Java signature of the problematic service call is public Course getCourseDetails(Long courseId)
Course is a standard POJO defined as:
public class Course {
private Long id;
private List<Hole> holes;
private String name;
private String tees;
//etc...
}
Hole is a standard POJO with only primative members.
When called with PHP, the holes member is an array with the correct length, but each hole is empty.
$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
Printing out the returned XML with $soapClient->__getLastResponse()
what looks like the correct representation:
<?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:yar开发者_如何学Pythondage>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>
Why is each hole an empty stdClass? Are there known limitations to the number of levels SoapClient will parse a response?
I had a similar issue. I went through every iteration you went through. On a fluke I disabled caching "soap.wsdl_cache" either by changing the PHP.INI file or ini_set('soap.wsdl_cache', WSDL_CACHE_NONE);
and on my next request all the missing data was populated. This can easily happen because the "soap.wsdl_cache_ttl" is set to "86400", by default, which is 60 days.
What I found out was that the soap server had a code change. Creating a new wsdl. The client's cached wsdl was stale at that point. You would think that, at least, a checksum hash of some kind would go out with each request to verify that the wsdl had changed but it does not.
To resolve this issue and still use caching I created a wsdl file that I could consume locally.
$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);
Hope this helps you or anyone else that happens to stop by and have a similar problem.
Did you figure this all out by debugging or printing out the contents of the PHP object (print_r, var_dump)?
Have you tried printing out the actual SOAP response string (not the PHP object)? You can do this by creating the SoapClient with the debug option set:
$soapClient = new SoapClient( "http://your.soap.server.com/services/yourWsdl.wsdl", array("trace" => 1));
Then when you use the client to make your SOAP call, you can take a look at both the request and the response strings.
$response = $soapClient->getCourseDetails($params);
$requestAsString = $soapClient->__getLastRequest();
$responseAsString = $soapClient->__getLastResponse();
This might help you figure out what SoapClient is doing when it's converting the response to a PHP object. More info on __getLastResponse().
This appears to be a bug in PHP. http://bugs.php.net/bug.php?id=49070
Unfortunately, the bug tracker won't let me comment on it.
Here we go nearly a year and a half later...
In my recent semi-similar experience this was not a php bug. It is an issue related to the way your webservice is written and how PHP reads the output. I was experiencing a similar problem (even down to getLastResponse returning the correct XML) and came to find that it wasn't so much PHP or my SOAP function that had an issue but that the result of the "broken" function was not an explicitly defined cursor.
Example of bad cursor definition:
PROCEDURE GetBlahByBlahID(IN IN_BLAH_ID VARCHAR, IN IN_BLAHPKG VARCHAR,
OUT result CURSOR
) BEGIN ...
Example of good cursor definition:
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 ...
Apparently Java can handle the "bad"/non explicit output just fine, but PHP returns an array of null objects.
Not sure if this will help you, but defining the web service function output as the "good" way above fixed my problem.
To solve this, you can get the xml-string of the soap response and cast that into an object.
$soapClient = new \SoapClient($wsdl, ['trace' => 1]); // trace is essential to get the lastResponse string
$soapClient->__call($function_name, $arguments);
$soapResponse = $soapClient->__getLastResponse();
$filteredSoapResponse = str_ireplace("soap:", "", $soapResponse); // remove the "soap:" namespace
$responseObject = simplexml_load_string($filteredSoapResponse);
All the data should be in the $responseObject, even the data you couldn't see before.
For me the issue was a missing field in the definition of the schema.
<xsd:import schemaLocation="https://*****.svc?xsd=xsd2" namespace="http://FunctionName"/>
The data was transferred correctly but PHP did only show the fields that were listed in the remote schema.
So i had to recreate the service schema.
精彩评论