📜 ⬆️ ⬇️

SoapServer in PHP. Let array be always Map

While working on the server side of one iphone application, a curious feature of Zend_Soap_Server surfaced. It led to spontaneously (at first glance) errors that occur when returning php arrays. It took us a few man-hours to detect and debug, and perhaps this article will allow someone to save the same few hours.

It should be noted that Zend_Soap_Server is a simple wrapper over the built-in SoapServer , and the effect described below is observed not only when using ZF, but also when working with SoapServer directly.

The method in which a mysterious error occurred is concerned with finding prices for hotels according to the parameters set by the client. They are returned along with some additional information and the final issuance structure looks like this:
<?php $result = array( 'info' => array( 'key_1' => 'value_1', ..., 'key_n' => 'value_n' ), 'prices' => array( id_1 => price_1, ... id_m => price_m ) ); return $result; ?> 

Of the important features - the prices on the server are ordered in ascending order; id_k are nonnegative integer numbers (hotel identifiers); if according to the specified criteria it is impossible to find any actual price, an error message is returned (another structure).

All the problems, as it turned out from the analysis of request logs, were related to the prices array. In the overwhelming majority of cases, it was not an array in the “classical” sense. That is, his keys were not consecutive integers starting with 0. SoapServer considers (fair) the Map type and converts (if you get into the raw xml answer) to the form
 <item> <key xsi:type="xsd:string">prices</key> <value xsi:type="ns2:Map"> <item> <key xsi:type="xsd:int">100</key> <value xsi:type="xsd:int">150</value> </item> <item> <key xsi:type="xsd:int">2</key> <value xsi:type="xsd:int">300</value> </item> <item> <key xsi:type="xsd:int">1078</key> <value xsi:type="xsd:int">306</value> </item> </value> </item> 

')
And only occasionally prices turned out to be a truly “classic” array. For example, such a situation arose when the only available option was a hotel with id = 0. Such data is considered by SoapSever (again, rightly) the Array type and results in
 <item> <key xsi:type="xsd:string">prices</key> <value enc:itemType="xsd:int" enc:arraySize="1" xsi:type="enc:Array"> <item xsi:type="xsd:int">420</item> </value> </item> 


Perhaps the error would have manifested itself earlier if the method returned a similar structure for “empty” results. This situation is more common, it stands out as a separate case when testing, and SoapServer also considers an empty array as an Array type:
 <item> <key xsi:type="xsd:string">prices</key> <value enc:itemType="xsd:anyType" enc:arraySize="0" xsi:type="enc:Array"/> </item> 


Thus, depending on the data, our server returned different types to the client - basically Map, but sometimes Array. And the client always expected to see the Map and fell when receiving an Array. The problem has been identified, but not yet resolved.

As possible solutions were proposed.
1) the change on the client side - so that he could deal with both cases;
2) add a fake string key to the prices array and skip it on the client during parsing;
3) to ensure that the server always produces the Map type in a less “crooked” way than paragraph 2.

The third option was considered preferable and after high-quality and not too short googling the solution was found on stackoverflow .
The prices array needs to be wrapped in SoapVar with an APACHE_MAP type
 <?php $result = ...; $result['prices'] = new SoapVar($result['prices'], APACHE_MAP); return $result; ?> 

and only then return to the customer. With such a wrapper, SoapServer no longer looks at the actual data to determine the type, but always returns a Map for empty arrays:
 <item> <key xsi:type="xsd:string">prices</key> <value xsi:type="ns2:Map"/> </item> 

and for the “classic” non-empty:
 <item> <key xsi:type="xsd:string">prices</key> <value xsi:type="ns2:Map"> <item> <key xsi:type="xsd:int">0</key> <value xsi:type="xsd:int">100</value> </item> <item> <key xsi:type="xsd:int">1</key> <value xsi:type="xsd:int">200</value> </item> <item> <key xsi:type="xsd:int">2</key> <value xsi:type="xsd:int">300</value> </item> </value> </item> 


Despite the final simplicity of the solution and the fact that it was found by Google, the situation itself seemed interesting to me, and the error was quite typical and worthy of description, which led to the writing of this article.

For completeness of information: software running on the server: PHP 5.3, Zend Framework 1.11, on other versions I did not check, although I assume that everything should be similar.

Source: https://habr.com/ru/post/165707/


All Articles