From this server:
$server = new SoapServer(null, array(
'uri' => 'http://test/',
));
$server->addFunction('test');
function test(){
$itemParams = new ArrayObject();
$items = array();
$itemParams[0]->ItemId = '1234';
$itemParams[0]->Amount = '1';
$itemParams[0]->Price = '2000';
$itemParams[0]->TranRules = array('TranRule' => 1);
$itemParams[1]->ItemId = '55555';
$itemParams[1]->Amount = '1';
$itemParams[1]->Price = '2000';
$itemParams[1]->TranRules = array('TranRule' => 2);
$items[] = new SoapVar($itemParams[0], SOAP_ENC_OBJECT);
$items[] = new SoapVar($itemParams[1], SOAP_ENC_OBJECT);
return new SoapVar($items, SOAP_ENC_OBJECT, NULL, NULL, 'TranItems');
}
$server->handle();
With this client:
$client = new SoapClient(null, array(
'location' => 'http://localhost/server.php',
'uri' => 'http://test/',
'trace' => true
));
$p = $client->test();
$resp = $client->__getLastResponse();
$dom = new DOMDocument();
$dom->formatOutput = true;
$dom->preserveWhiteSpace = false;
$dom->loadXML($resp);
print '<pre>';
var_dump($p);
print htmlspecialchars($dom->saveXML());
I'm getting this response:
object(stdClass)[2]
public 'TranItems' =>
array (size=2)
0 =>
object(stdClass)[3]
public 'ItemId' => string '1234' (length=4)
public 'Amount' => string '1' (length=1)
public 'Price' => string '2000' (length=4)
public 'TranRules' =>
array (size=1)
...
1 =>
object(stdClass)[4]
public 'ItemId' => string '55555' (length=5)
public 'Amount' => string '1' (length=1)
public 'Price' => string '2000' (length=4)
public 'TranRules' =>
array (size=1)
...
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://test/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ns2="http://xml.apache.org/xml-soap" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<ns1:testResponse>
<ns1:TranItems xsi:type="SOAP-ENC:Struct">
<TranItems xsi:type="SOAP-ENC:Struct">
<ItemId xsi:type="xsd:string">1234</ItemId>
<Amount xsi:type="xsd:string">1</Amount>
<Price xsi:type="xsd:string">2000</Price>
<TranRules xsi:type="ns2:Map">
<item>
<key xsi:type="xsd:string">TranRule</key>
<value xsi:type="xsd:int">1</value>
</item>
</TranRules>
</TranItems>
<TranItems xsi:type="SOAP-ENC:Struct">
<ItemId xsi:type="xsd:string">55555</ItemId>
<Amount xsi:type="xsd:string">1</Amount>
<Price xsi:type="xsd:string">2000</Price>
<TranRules xsi:type="ns2:Map">
<item>
<key xsi:type="xsd:string">TranRule</key>
<value xsi:type="xsd:int">2</value>
</item>
</TranRules>
</TranItems>
</ns1:TranItems>
</ns1:testResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
With minor modification:
$items[] = new SoapVar($itemParams[0], SOAP_ENC_OBJECT, 'TransItem');
$items[] = new SoapVar($itemParams[1], SOAP_ENC_OBJECT, 'TransItem');
return new SoapVar($items, SOAP_ENC_ARRAY, 'TransItem', null);
You will get:
array (size=2)
0 =>
object(stdClass)[2]
public 'ItemId' => string '1234' (length=4)
public 'Amount' => string '1' (length=1)
public 'Price' => string '2000' (length=4)
public 'TranRules' =>
array (size=1)
'TranRule' => int 1
1 =>
object(stdClass)[3]
public 'ItemId' => string '55555' (length=5)
public 'Amount' => string '1' (length=1)
public 'Price' => string '2000' (length=4)
public 'TranRules' =>
array (size=1)
'TranRule' => int 2
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://test/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ns2="http://xml.apache.org/xml-soap" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<ns1:testResponse>
<return SOAP-ENC:arrayType="TransItem[2]" xsi:type="TransItem">
<item xsi:type="TransItem">
<ItemId xsi:type="xsd:string">1234</ItemId>
<Amount xsi:type="xsd:string">1</Amount>
<Price xsi:type="xsd:string">2000</Price>
<TranRules xsi:type="ns2:Map">
<item>
<key xsi:type="xsd:string">TranRule</key>
<value xsi:type="xsd:int">1</value>
</item>
</TranRules>
</item>
<item xsi:type="TransItem">
<ItemId xsi:type="xsd:string">55555</ItemId>
<Amount xsi:type="xsd:string">1</Amount>
<Price xsi:type="xsd:string">2000</Price>
<TranRules xsi:type="ns2:Map">
<item>
<key xsi:type="xsd:string">TranRule</key>
<value xsi:type="xsd:int">2</value>
</item>
</TranRules>
</item>
</return>
</ns1:testResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Explanation:
Soap was developed as a cross language transport protocol. In most of the languages there is no such thing as associative arrays. Thats why you have to encode $itemParams[0]
as object. Most of the target languages for soap (like Java) have strong types so when you don't provide type for soap encoder it will generate one. Same goes for an array. In strongly typed language, every array can hold only objects of one kind. To sum it up: you encode php associative array as object of TYPE where TYPE will become node name and then if you return multiple elements: you encode them as soap array holding objects of specific TYPE. Not encoded array (because it can contain multiple different types) is converted into a map with multiple item nodes, each with key node and a value node.
Change this:
$items[] = new SoapVar($itemParams[0], SOAP_ENC_OBJECT, null, null, 'TranItems');
to that:
$items[] = new SoapVar($itemParams[0], SOAP_ENC_OBJECT);
And you will get BOGUS back.