phpsoapsoapfault

How can I add custom elements to the detail section of a SoapFault using PHP's SOAP library


I am building a service against Sonos' Music API (SMAPI). Sometimes I have to send back a response in the following format:

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
   <soap:Body>
      <soap:Fault>
         <faultcode>Client.NOT_LINKED_RETRY</faultcode>
         <faultstring>Link Code not found retry...</faultstring>
         <detail>
            <ExceptionInfo>NOT_LINKED_RETRY</ExceptionInfo>
            <SonosError>5</SonosError>
         </detail>
      </soap:Fault>
   </soap:Body>
</soap:Envelope>

I am building my service using the PHP SOAP library and for the above response I tried throwing a SoapFault like this:

throw new SoapFault('Client.NOT_LINKED_RETRY', 'Link Code not found retry...');

But when I try this the response that is sent back looks like this:

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
   <soap:Body>
      <soap:Fault>
         <faultcode>Client.NOT_LINKED_RETRY</faultcode>
         <faultstring>Link Code not found retry...</faultstring>
         <detail>
            <SonosError/>
         </detail>
      </soap:Fault>
   </soap:Body>
</soap:Envelope>

Notice that there is no ExceptionInfo and that SonosError is empty. Is it possible to set ExceptionInfo and SonosError using SoapFault? I tried all kinds of things, but couldn't get it working, so as a work around I am doing this now:

http_response_code(500);
header("Content-type: text/xml");

$ret = '<?xml version="1.0" encoding="UTF-8"?>'."\n";
$ret .= '<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">';
$ret .= '<SOAP-ENV:Body>';
$ret .= '<SOAP-ENV:Fault>';
$ret .= '<faultcode>Client.NOT_LINKED_RETRY</faultcode>';
$ret .= '<faultstring>Link Code not found retry...</faultstring>';
$ret .= '<detail>';
$ret .= '<ExceptionInfo>NOT_LINKED_RETRY</ExceptionInfo>';
$ret .= '<SonosError>5</SonosError>';
$ret .= '</detail>';
$ret .= '</SOAP-ENV:Fault>';
$ret .= '</SOAP-ENV:Body>';
$ret .= '</SOAP-ENV:Envelope>'."\n";

echo $ret; exit;

Not sure if it's relevant but the WSDL can be found here.

Update: when I try the suggestion below like this:

$detail = new StdClass();
$detail->SonosError = 5;
$detail->ExceptionInfo = 'NOT_LINKED_RETRY';

throw new SoapFault(
  'Client.NOT_LINKED_RETRY',
  'Link Code not found retry...',
  NULL,
  $detail
);

I get:

<detail>
  <customFault>
    <SonosError>5</SonosError>
    <ExceptionInfo>NOT_LINKED_RETRY</ExceptionInfo>
  </customFault>
</detail>

This is almost what I need, except for <customFault> tag. Is there a way to get rid of it and have SonosError and ExceptionInfo in <detail> directly?


Solution

  • The fact that you don't see the ExceptionInfo tag is because it is not defined in the wsdl. On the other hand SonosError is defined. First thing first, in order to fill the SonosError you have to pass the arguments.

    From here you can see that the constructor has more parameters

    SoapFault('code', 'string', 'actor', 'detail', 'name', 'header');
    

    In order to pass the SonosError call it like this

    $detail = new StdClass(); 
    $detail->SonosError = 5;
    throw new SoapFault('Client.NOT_LINKED_RETRY', 'Link Code not found retry...', null, $details);
    

    As for the ExceptionInfo, the wsdl must be changed. As it is now, the details tag is represented by this sections

    <wsdl:message name="customFault">
        <wsdl:part name="customFault" element="tns:SonosError"/>
    </wsdl:message> 
    
    <xs:element name="SonosError" type="xs:int"/>
    

    If you change the above sections with these, you will have what you need.

    <wsdl:message name="customFault">
       <wsdl:part name="customFault" type="tns:customFaultType" />
    </wsdl:message>
    
    <xs:complexType name="customFaultType">
       <xs:sequence>
          <xs:element name="SonosError" type="xs:int"/>
          <xs:element name="ExceptionInfo" type="xs:string"/>
       </xs:sequence>
    </xs:complexType>
    

    And of course you add the parameter and the array becomes like this

    $detail = new StdClass(); 
    $detail->SonosError = 5;
    $detail->ExceptionInfo = 'NOT_LINKED_RETRY';