Markus
Markus

Reputation: 185

PHP: How to preserve structure of SOAP response?

I have an issue concerning SOAP calls in PHP. There is a webservice named "DoBulkOperation", that takes a list of whatever, does something, and returns a list with information about what it had done. The important thing here is, that each list element of the request has its counterpart in the response at the same index.

The response I get is something like that:

<Body>
  <DoBulkOperationResponse>
    <items>
      <OperationResponse>
        <result>17</result>
      </OperationResponse>
      <Error>Some error occured</Error>
      <OperationResponse>
        <result>18</result>
      </OperationResponse>
      <OperationResponse>
        <result>19</result>
      </OperationResponse>
      <OperationResponse>
        <result>20</result>
      </OperationResponse>
    </items>
  </DoBulkOperationResponse>
</Body>

That the correct response to my request. Now, I can say, that there is something wrong with the second list element in the request.

What I get from the PHP SoapClient is not, what I expected:

object(stdClass)#49 (1) {
  ["items"]=>
  object(stdClass)#51 (2) {
    ["OperationResponse"]=>
    array(4) {
      [0]=>
      object(stdClass)#53 (1) {
        ["customerID"]=>
        int(17)
      }
      [1]=>
      object(stdClass)#52 (1) {
        ["customerID"]=>
        int(18)
      }
      [2]=>
      object(stdClass)#50 (1) {
        ["customerID"]=>
        int(19)
      }
      [3]=>
      object(stdClass)#54 (1) {
        ["customerID"]=>
        int(20)
      }
    }
    ["Error"]=>
    string(17) "Some error occured"
  }
}

The order of the elements is completely broken. Now, it's impossible to decide, which request element leads to which response element.

In WSDL, "items" is defined as sequence.

Is there a way, to force the SoapClient instance to preserve the structure of the response?

Best regards,

Markus

Upvotes: 2

Views: 2082

Answers (1)

Sven
Sven

Reputation: 70893

You have a couple of options here, but for a definitive answer you should present the WSDL for this call.

Force the PHP SoapClient to always return arrays where defined.

Set or add array('features' => SOAP_SINGLE_ELEMENT_ARRAYS) as the option to the SoapClient. It will prevent the result from having arrays only when more than one element is encountered. From what I believe PHP does internalls: On the first element encounter, it is just created, and on the next encounter, the first element is moved into an array and the second then added.

This might already explain the structure you are seeing, and might be able to fix it.

Use a classmap with objects that predefine the expected structure.

You have to find out the names of the ComplexType structures in the WSDL and can then create PHP classes that are used for these instead of stdClass. This is very useful for everything, because your IDE can provide you with autocompletion, and you can actually see with which element you are dealing by using get_class() on it. On the other hand, you cannot create a PHP object that properly acts as the items structure in your XML, so this might just be cosmetic, but it is necessary for...

Use magic __set() in your custom class to interfere with creating the result.

You might be able to create a custom class that is used as the items which gets set all the properties. Inside the magic function you can try and push the data around. At least you'd be able to debug it with some output.

Trace the request and access the XML result directly.

Set the option trace => true in the options array, and use SoapClient::__getLastResponse.

Be prepared for failure.

PHP might just be unable to transform the XML structure you get into a proper result. I'd blame the Soap API you are offered. One would usually offer to attach some kind of ID to the request and reuse this ID on the result to enable clients to identify their stuff. At least the structure returned "in the order of the request" should always return the same item and carry the result somewhere internal, like:

<OperationResponse>
    <result>17</result>
</OperationResponse>
<OperationResponse>
    <Error>Some error occured</Error>
</OperationResponse>

Upvotes: 5

Related Questions