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:
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
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
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:
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:
and for “classic” non-empty:
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.
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
prices 100 150 2 300 1078 306
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
prices - 420
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:
prices 0 100 1 200 2 300
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.