PHP SOAP XML attributes & namespaces with XMLWriter

If you've experienced the unending joy of attempting to make a PHP SOAP call using the SoapClass, you may have discovered XML node attributes cannot easily be created and passed. According to this comment: http://www.php.net/manual/en/soapvar.soapvar.php#101225 some people have had luck creating node attributes using the following code:

 
  $amount['_'] = 25; 
  $amount['currencyId'] = 'GBP'; 
  $encoded = new SoapVar($amount, SOAP_ENC_OBJECT); 

It has not worked on the three PHP installations I've tested it on, plus I needed to also specify namespaces. I decided I best go lower level, and looked into the SimpleXML class. Attributes are easy with SimpleXML, but attributes with namespaces aren't possible (Someone correct me if I am wrong.) So, I looked into the XMLWriter class and found exactly what I needed. The XMLWriter class gives you complete control over the XML creation, without have to write any XML yourself.

Here is the method I worked out for my use case and WSDL (code has been simplified, real thing is far more complex):

function call_soap_wsdl($method, $message, $operation, $important_id) {
  $xml = new XMLWriter();
  $xml->openMemory();
  $xml->startElementNS('objs', $method, 'ObjectNamespace');
    // Send NULL to not specify the NameSpace on every call.
    $xml->startElementNS('objs', $message, NULL);
      //Attribute on a Namespaced node!
      $xml->writeAttribute('node-attribute', $operation);
      $xml->startElementNS('objs', 'ImportantId', NULL);
        $xml->Text($important_id);
      $xml->endElement();
    $xml->endElement();
  $xml->endElement();
 
  //Convert it to a valid SoapVar
  $args = new SoapVar($xml->outputMemory(), XSD_ANYXML);
 
  $options = array(
    'trace' => TRUE,
    'soap_version' => SOAP_1_1,
  );
 
  $wsdl = 'http:// example.com/service.wsdl';
  $client = new SoapClient($wsdl, $options);  
  try {
    $result = $client->__SoapCall($method, array($args));
  }
  catch (Exception $e) {
    //Handle Exception - Use $client->__getLastRequestHeaders(), 
    //$client->__getLastRequest(), $client->__getLastResponse().
    $result = FALSE;
  }
  return $result;
}

If the above function is called using: call_soap_wsdl('Search', 'SearchObj', 'FindUser', 123);, the resulting SOAP request (printed using print $client->__getLastRequest()) is:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http ://schemas.xmlsoap.org/soap/envelope/">
  <SOAP-ENV:Body>
    <objs:Search xmlns:objs="ObjectNamespace">
      <objs:SearchObj operation="FindUser">
        <objs:ImportantId>123</objs:ImportantId>
      </objs:SearchObj>
    </objs:Search>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Comments

Helped me loads, thanks!

Add new comment