SoapServer in PHP. Let array always be Map

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

It is worth noting that Zend_Soap_Server is a simple wrapper on 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 the error occurred mystically is engaged in finding hotel prices according to the parameters set by the client. They come back along with some additional information and the final structure of the output looks something like this:
 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 - prices on the server are ordered in ascending order; id_k are non-negative integers, (hotel identifiers); if according to the given criteria it is impossible to find any actual prices, then an error message is returned (another structure).

All problems, as it turned out from the analysis of query logs, were associated with the prices array. In the vast majority of cases, it was not an array in the “classical” sense. That is, its keys were not consecutive integers starting with 0. Similar data SoapServer considers (fairly) the type of Map and converts (if you get into the raw xml of the answer) to the form
prices10015023001078306


And only occasionally prices turned out to be a truly “classic” array. For example, such a situation arose when the hotel with id = 0 was the only available option. SoapSever considers (again, fairly) the Array type of such data and leads to
prices420


Perhaps an error would have occurred earlier if the method returned a similar structure for “empty” results. This situation is more common, it stands out as a separate case during testing, and SoapServer also considers an empty array to be an Array type:
prices


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

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

The third option was considered preferable and after a high-quality and not too short google solution was found on stackoverflow .
The prices array needs to be wrapped in SoapVar with type APACHE_MAP

and only then return to the customer. With such a wrapper, SoapServer no longer looks at the actual data for determining the type, but always returns Map - for empty arrays:
prices

and for “classic” non-empty:
prices010012002300


Despite the final simplicity of the solution and the fact that it was found by Google’s method, 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, I did not check on other versions, although I assume that everything should be the same.

Also popular now: