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