SOAP structures in PHP
Handling SOAP structures in PHP can sometimes be really annoying. If an interface is defined in the WSDL as returning an array I can’t be sure that I will get an array. If there is only one element in the array PHP tries to be clever and turn the wanted array into an object which, too me, isn’t really smart. I don’t know if this is a problem/limitation on the client side, server side or if it is just me doing something stupid in the wsdl.
As an example I have the below complex types, message and operation defined in the WSDL file. (The example is not complete of course.)
<complexType name="KeyValueData">
<sequence>
<element minOccurs="1" maxOccurs="1" name="id" type="string"/>
<element minOccurs="1" maxOccurs="1" name="name" type="string"/>
<element minOccurs="1" maxOccurs="1" name="data" type="string"/>
</sequence>
</complexType>
<complexType name="ArrayOfKeyValueData">
<sequence>
<element minOccurs="0" maxOccurs="unbounded"
name="keyval" type="tns:KeyValueData"/>
</sequence>
</complexType>
...
<message name="StatisticsListOutput">
<part name="data" element="tns:ArrayOfKeyValueData"/>
</message>
...
<operation name="getStatistics">
<input message="tns:StatisticsListInput"/>
<output message="tns:StatisticsListOutput"/>
</operation>
Sending this from the client is an absolute no brainer. It is a simple array containg associative arrays in every slot. When receiving this structure on the client side the original array (the member variable Map in the example) with one element turns into
Class Object
(
[Map] => stdClass Object
(
[item] => Array
(
[0] => stdClass Object
(
[key] => id
[value] => value
)
[1] => stdClass Object
(
[key] => name
[value] => value
)
where an array with multiple elements turn into
stdClass Object
(
[Map] => Array
(
[0] => stdClass Object
(
[item] => Array
(
[0] => stdClass Object
(
[key] => id
[value] => value
)
[1] => stdClass Object
(
[key] => name
[value] => value
)
This is annoying. Just imagine the situation when you have multiple arrays in arrays… Which actually does happen every once in a while when sending compounds of various elements declared as ComplexTypes in the WSDL.
Is it a feature, quirk or anti social behaviour? I don’t know. If there is another way I’d like to hear about it.
Tags: PHP, soap, web services, wsdl
October 31st, 2008 at 4:11 pm
Quite annoying indeed.
But the fault lies in xml being a child of sgml: it was not designed for storing data, but rather for storing documents.
Just think at how much money has globally been lost in the world in time spent thinking whether some piece of data was to be coded as an element attribute or a sub-element…
And xsd is the most unnecessarily bloated and complex xml definition language that could be bolted on top of it. Quite an unlucky combination for the SOAP guys.
If you want to keep mental sanity and decent execution speed, go for json or xmlrpc.
October 31st, 2008 at 4:54 pm
It’s considered a feature. Create your soap client object like this:
See also the comment in: http://no2.php.net/manual/en/f.....struct.php
October 31st, 2008 at 5:05 pm
gaetano
That would be nice to force third party data providers to use JSON, but reality is that there are a bunch of lovely folks out there who think SOAP should work as advertised.
I have to agree with Danne. We deal with a couple shops that love their complex data types. They hand coded their WSDLs taking full advantage of them. When we come across these sorts of things, PHP falls flat on it’s face.
If I could motivate anyone in the internals, I would say: who cares about PHP6, give me a fully WSDL compatible SOAP extension and a comprehensive solution for complex data types.
There are a couple PHP coded projects out there that try to make up for the shortcomings, but those things should be native and predictable. I think it is ironic that PHP’s weakest point right now is SOAP.
October 31st, 2008 at 5:42 pm
It’s considered a feature. But there’s a way to make it work better. There’s an option when creating the SOAP object called SOAP_SINGLE_ELEMENT_ARRAYS. You can google or search the PHP documentation for it.
October 31st, 2008 at 8:13 pm
Not sure if this will come thru in the comment, but here is a slightly different WSDL construction that I am fairly sure will facilitate always returning an array, even if the data structure encoded in it has 1 element.
The corresponding PHP server code to create the response would be something like
new SoapVar($arrayOfKeyValueData, SOAP_ENC_ARRAY, "ArrayOfKeyValueData", "[your target namespace]“);
October 31st, 2008 at 8:13 pm
Uh… strike 1. try again…
October 31st, 2008 at 8:15 pm
strike 2… almost done. last try…
<complexType name="ArrayOfKeyValueData">
<complexContent>
<restriction base="SOAP-ENC:Array">
<attribute ref="SOAP-ENC:arrayType"
wsdl:arrayType="tns:KeyValueData[]” />
</restriction>
</complexContent>
</complexType&grt;
November 1st, 2008 at 2:53 pm
http://bugs.php.net/bug.php?id=36226 says:
$x = new SoapClient($wsdl, array('features' =>SOAP_SINGLE_ELEMENT_ARRAYS));
November 3rd, 2008 at 8:51 pm
Havr you tried the following:
$x = new SoapClient($wsdl, array('features' =>
SOAP_SINGLE_ELEMENT_ARRAYS));
Not sure if this is documented in the manual though.
June 13th, 2009 at 12:15 am
What about the issue of if you are building your WSDLs this way, using WS-I compliant arrays, you can’t properly pass or retrieve arrays without everything wrapping in stdClass objects?
However, if he built the exact same WSDL using soap-enc:Array, this all would have returned the results in a nice package of:
array(
array(
‘id’ => xxx, ‘name’ => yyy, ‘data’ => zzz),
array(
‘id’ => aaa, ‘name’ => bbb, ‘data’ => ccc));
I have yet to find a solution for this WS-I soapenc:Array issue.