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>
</code>

Comments

(Statically copied from previous site)

Martin replied on January 16, 2012 - 4:44pm

Helped me loads, thanks!

Bruno replied on May 9, 2012 - 5:38am

As you would if you would have to put an attribute in the Envelope tag? E.g:

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http ://schemas.xmlsoap.org/soap/envelope/" xmlns:tag="other namespace" xmlns:tag2="other namespace2">

lemay replied on January 14, 2013 - 7:52am

good job buddy, but, if I want to make a xml document like this:

<node xmlns:"http:example.com">
</node>

instead of <ns1:node xmlns:"http://example.com"> because the xml required in my web service can’t accept the tag </ns1 node> do you understand me ? Any help will be very upseful.. thanks a lot.

brad replied on January 24, 2013 - 5:47pm

I’d probably use SimpleXML instead then. It is simpler than XMLwriter, but doesn’t support namespaces.

nico replied on July 30, 2013 - 9:57am

Thank god for this. Please comment on the php.net manual.

Jean replied on November 7, 2013 - 7:27pm

Thank you so much! This has doubtless saved people (including me) hundreds of hours.

By the way, in order to produce something like:

<node xmlns:"http://example.com">
</node>

instead of

<ns1:node xmlns:"http://example.com"/>

Something like the following worked for me:

$xml->startElementNS(null, 'node', 'http://example.com');

Kendall Smith replied on September 4, 2014 - 7:11am

Can XMLwriter be used to create a SoapHeader with attributes?