I’ve been working with X-fire / ageis services in SOAP now for a while, and there’s been only one issue I’ve not been able to solve. Arrays.

When the back-end send an array with item count > 1 everything works fine. The problems start when there is only one item in the array. The array is collapsed and instead there is a object that is the only item. At first I thought this was the java back-end that screwed up, but after inspecting the SOAP messages from NGREP I found out that it’s PHP that screws up.

I’ve prepared a short example called lookupPerson which takes a username and returns a Person with his related cars.

Consider the first sample, where the person has three cars:

SOAP request:
<pre line="1" lang="xml">
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://api.service.com/temp/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ns2="urn:Service">
<SOAP-ENV:Header>
<ns2:RemoteAddress xsi:type="SOAP-ENC:string">127.0.0.1</ns2:RemoteAddress>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<ns1:lookupPerson>
<ns1:in0>john</ns1:in0>
</ns1:lookupPerson>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

</pre>

SOAP response:
<pre line="1" lang="xml">

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soap:Body>
<lookupPersonResponse xmlns="http://api.service.com/temp/">
<out xmlns="http://api.service.com/temp/">
<username xmlns="http://users.model.service.com">john</username>
<cars xmlns="http://users.model.service.com">
<Car>
<car>HUMMER</car>
</Car>
<Car>
<car>BMW</car>
</Car>
</cars>
</out>
</lookupPersonResponse>
</soap:Body>
</soap:Envelope>

</pre>

Now that looks pretty much right. Lets take a look at a var_dump of the returned object:

object(lookupPersonResponse)#571 (1) {
["out"]=> object(Person)#570 (2) {
["username"]=> string(11) "john"
["cars"]=> object(stdClass)#573 (1) {
["Car"]=> array(2) {
[0]=> object(Car)#572 (1) {
["car"]=> string(17) "BMW"
}
[1]=> object(Car)#574 (1) {
["car"]=> string(19) "HUMMER"
}
}
}
}
}

Yep. Everything is correct. The class map works as intended. Now, lets get over to the bad stuff. The following output is the same call, but this time John has lost his Hummer.

SOAP response:

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soap:Body>
<lookupPersonResponse xmlns="http://api.service.com/temp/">
<out xmlns="http://api.service.com/temp/">
<username xmlns="http://users.model.service.com">john</username>
<cars xmlns="http://users.model.service.com">
<Car>
<car>BMW</car>
</Car>
</cars>
</out>
</lookupPersonResponse>
</soap:Body>
</soap:Envelope>

Well, the SOAP response is pretty much like the first one.

PHP object:

object(lookupPersonResponse)#571 (1) {
["out"]=> object(Person)#570 (2) {
["username"]=> string(11) "john"
["cars"]=> object(stdClass)#573 (1) {
["Car"]=> object(Car)#572 (1) {
["car"]=> string(17) "BMW"
}
}
}
}

Do you see the difference here? Car is pointing to the car object instance instead of an array. This lays the path for some ass logic work for everything that returns an array.

Lets take a look at the relevant parts of the WSDL:

<xsd:complexType name="Person">
<xsd:sequence>
<xsd:element minOccurs="0" name="username" nillable="true" type="xsd:string"/>
<xsd:element minOccurs="0" name="cars" nillable="true" type="ns1:ArrayOfCar"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ArrayOfCar">
<xsd:sequence>
<xsd:element maxOccurs="unbounded" minOccurs="0" name="Car" nillable="true" type="ns1:Car"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="Car">
<xsd:sequence>
<xsd:element minOccurs="0" name="car" nillable="true" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:element name="lookupPersonResponse">
<xsd:complexType>
<xsd:sequence>
<xsd:element maxOccurs="1" minOccurs="1" name="out" nillable="true" type="ns1:Person"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>

According to WSDL standard this is legal and should work, but I dont think PHP is too happy about it. I’ve tried doing similar stuff with a service that describes arrays as :


<complexType name="ArrayOf_soapenc_string">
<complexContent>
<restriction base="soapenc:Array">
<attribute ref="soapenc:arrayType" wsdl:arrayType="soapenc:string[]"/>
</restriction>
</complexContent>
</complexType>

This way seems to work just fine, so I fear that the problem has something to do with X-Fire and ageis. I’m very interested in some hints on how to get this running properly.

8 Responses to “Array problems with X-Fire, SOAP and PHP”

  1. very interesting, but I don’t agree with you
    Idetrorce

  2. Can you please elaborate a bit? :)

  3. We run into the same problem, and found the following:

    http://bugs.php.net/bug.php?id=36226

  4. [...] 13, 2008 at 8:14 am (PHP, SOAP, Tips) This is a followup to my post about array problems with SOAP and PHP. The scenario was that when an array with only one object was returned over SOAP the array was [...]

  5. [...] is a followup to my post about array problems with SOAP and PHP. The scenario was that when an array with only one object was returned over SOAP the array was [...]

  6. [...] pm (PHP, SOAP, Tips, X-Fire) This post is pretty much a revamp of a previous post called “Working with X-Fire soap services and inheritance in PHP“. That title was a bit misleading, and not really that good when it came to providing a [...]

  7. [...] is a followup to my post about array problems with SOAP and PHP. The scenario was that when an array with only one object was returned over SOAP the array was [...]

Leave a Reply

(required)

(required)

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="" highlight="">