XML Soap body outside of envelope - php

I have looked at this for hours and really need another set of eyes. My envelope is closed and the body is not showing under it as a child but at a root. Here is the relevant code.
$XMLDoc = new DOMDocument('1.0', 'UTF-8');
$XMLDoc->preserveWhiteSpace = false;
$XMLDoc->formatOutput = true;
// SOAP ENVELOPE ELEMENT AND ATTRIBUTES
$soap = $XMLDoc->createElementNS('http://schemas.xmlsoap.org/soap/envelope/', 'soap12:Envelope');
$XMLDoc->appendChild($soap);
$soap->setAttributeNS('http://www.w3.org/2000/xmlns/' ,'xmlns:soap12', 'http://www.w3.org/2003/05/soap-envelope');
$soap->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance');
$soap->setAttributeNS('http://www.w3.org/2000/xmlns/' ,'xmlns:xsd', 'http://www.w3.org/2001/XMLSchema');
// SOAP BODY
$example_element = $XMLDoc->createElement('soap12:Body');
$rootNode = $soap->appendChild($example_element);
and here is the XML it is producing.
<?xml version="1.0" encoding="UTF-8"?>
<soap12:Envelope xmlns:soap12="http://www.w3.org/2003/05/soap-envelope" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"/>
<soap12:Body>

Related

soap xml response - how to get that stubborn data out [duplicate]

This question already has answers here:
Reference - How do I handle Namespaces (Tags and Attributes with a Colon in their Name) in SimpleXML?
(2 answers)
Closed 3 years ago.
I'm trying to get the contents of the faultstring element and I'm more comfortable with SimpleXML's object syntax ($xml->...->faultstring.) The DomDocument method below works, but I'd prefer to stick with SimpleXML.
PHP version 5.6.40, Libxml 2.9.1.
$xmlResponse = '<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<soap:Fault>
<faultcode>soap:Client</faultcode>
<faultstring>Server did not recognize the value of HTTP Header SOAPAction: https://gw1.domain.com:4430/.</faultstring>
<detail />
</soap:Fault>
</soap:Body>
</soap:Envelope>';
// DOES NOT WORK
$xml = simplexml_load_string($xmlResponse,NULL,NULL,"http://schemas.xmlsoap.org/soap/envelope/");
$faultstring = (string) ($xml->Body->Fault->faultstring);
var_dump($faultstring);
// WORKS
$_DomObject = new DOMDocument;
$_DomObject->loadXML($xmlResponse);
if (!$_DomObject) die("Error while parsing the document");
$s = simplexml_import_dom($_DomObject);
foreach(['faultcode','faultstring','detail'] as $tag) {
echo $tag.' => '.$_DomObject->getElementsByTagName($tag)[0]->textContent.'<br/>';
}
<?php
$xmlResponse = <<<XML
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<soap:Fault>
<faultcode>soap:Client</faultcode>
<faultstring>Server did not recognize the value of HTTP Header SOAPAction: https://gw1.domain.com:4430/.</faultstring>
<detail />
</soap:Fault>
</soap:Body>
</soap:Envelope>
XML;
XML namespaces are getting in the way; they're the soap: prefix on the element names. It's possible to register all the namespaces and get it working that way, but just using XPath is easier:
$xml = new SimpleXMLElement($xmlResponse);
$fault = $xml->xpath("//faultstring");
echo (string)$fault[0] . "\n";
And here's how it looks using namespaces. You still get the object syntax, but it becomes a lot more complicated.
$xml = new SimpleXMLElement($xmlResponse);
$fault = $xml->children("soap", true)->Body->children("soap", true)->Fault->children()->faultstring;
echo (string)$fault . "\n";
Output for both:
Server did not recognize the value of HTTP Header SOAPAction: https://gw1.domain.com:4430/.

Duplicate xml namespace declarations php DOMDocument

I use PHP DOMDocument to generate xml. Sometimes namespaces are declared only on root element, which is intended behaviour, but sometimes no.
For example:
$xml = new DOMDocument('1.0', 'utf-8');
$ns = "http://ns.com";
$otherNs = "http://otherns.com";
$docs = $xml->createElementNS($ns, "ns:Documents");
$doc = $xml->createElementNS($otherNs, "ons:Document");
$innerElement = $xml->createElementNS($otherNs, "ons:innerElement", "someValue");
$doc->appendChild($innerElement);
$docs->appendChild($doc);
$xml->appendChild($docs);
$xml->formatOutput = true;
$xml->save("dom");
I expect:
<?xml version="1.0" encoding="UTF-8"?>
<ns:Documents xmlns:ns="http://ns.com" xmlns:ons="http://otherns.com">
<ons:Document>
<ons:innerElement>someValue</ons:innerElement>
</ons:Document>
</ns:Documents>
But got:
<?xml version="1.0" encoding="UTF-8"?>
<ns:Documents xmlns:ns="http://ns.com" xmlns:ons="http://otherns.com">
<ons:Document xmlns:ons="http://otherns.com">
<ons:innerElement>someValue</ons:innerElement>
</ons:Document>
</ns:Documents>
Why declaration of xmlns:ons="http://otherns.com" appears on Document element, but not in <innerElement>? And how to prevent duplicates?
It's very easy. Just add your nodes into document tree.
Additionally you can explicitly create xmlns:XXX atrtribute in root node.
See example:
namespace test;
use DOMDocument;
$xml = new DOMDocument("1.0", "UTF-8");
$ns = "http://ns.com";
$otherNs = "http://otherns.com";
$docs = $xml->createElementNS($ns, "ns:Documents");
$xml->appendChild($docs);
$docs->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:ons', $otherNs);
$doc = $xml->createElement("ons:Document");
$docs->appendChild($doc);
$innerElement = $xml->createElement("ons:innerElement", "someValue");
$doc->appendChild($innerElement);
$xml->formatOutput = true;
echo $xml->saveXML();
Result:
<?xml version="1.0" encoding="UTF-8"?>
<ns:Documents xmlns:ns="http://ns.com" xmlns:ons="http://otherns.com">
<ons:Document>
<ons:innerElement>someValue</ons:innerElement>
</ons:Document>
</ns:Documents>

PHP - Format XML Output

I'm generating some XML data in response to an HTTP POST request. I'm setting the MIME
header('Content-type: text/xml');
and everything is working well so far.
I'm generating the xml response as follows:
$response = '<?xml version="1.0" encoding="utf-8"?>';
$response .= '<error>';
$response .= '<description>'.$error.'</description>';
$response .= '</error>';
echo $response;
I would now like the format the XML so that instead of seeing this response:
<?xml version="1.0" encoding="utf-8"?><error><description>Login Error: No Username Supplied</description></error>
they see this response:
<?xml version="1.0" encoding="utf-8"?>
<error>
<description>Login Error: No Username Supplied</description>
</error>
I haven't worked with XML output with PHP before so not sure if there's a built in method for doing a "pretty print" type function on the output?
Use the DOM library and set the formatOutput property to true
$doc = new DOMDocument('1.0', 'utf-8');
$doc->formatOutput = true;
$root = $doc->createElement('error');
$doc->appendChild($root);
$desc = $doc->createElement('description', $error);
$root->appendChild($desc);
echo $doc->saveXML();
Demo ~ https://eval.in/461384

Strip SOAP Envelope for Saving Response to XML File

I have a SOAP response that I want to save to an XML file. When the response is written to the file, the SOAP envelope is written with it, making the XML file useless due to the error:
XML declaration allowed only at the start of the document in ...
In this case, XML is being declared twice:
<?xml version="1.0" encoding="ISO-8859-1"?>
<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body><ns1:NDFDgenResponse xmlns:ns1="http://graphical.weather.gov/xml/DWMLgen/wsdl/ndfdXML.wsdl">
<dwmlOut xsi:type="xsd:string">
<?xml version="1.0"?>
...
Is there a good way to strip out this SOAP envelope and just save what's between it?
Here's how I'm writing the response to the file:
$toWrite = htmlspecialchars_decode($client->__getLastResponse());
$fp = fopen('weather.xml', 'w');
fwrite($fp, $toWrite);
fclose($fp);
The problem is the htmlspecialchars_decode(). The envelope document contains other XML documents as text nodes. If you decode the entities in the XML document you will destroy it. Never use htmlspecialchars_decode() on an XML document.
Load the (envelope) XML into a DOM and read the needed value from it.
$xml = <<<'XML'
<?xml version="1.0" encoding="ISO-8859-1"?>
<SOAP-ENV:Envelope
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<ns1:NDFDgenResponse
xmlns:ns1="http://graphical.weather.gov/xml/DWMLgen/wsdl/ndfdXML.wsdl">
<dwmlOut xsi:type="xsd:string">
<?xml version="1.0"?>
<weather>XML</weather>
</dwmlOut>
</ns1:NDFDgenResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
XML;
$dom = new DOMDocument();
$dom->loadXml($xml);
$xpath = new DOMXPath($dom);
$xpath->registerNamespace('soap', 'http://schemas.xmlsoap.org/soap/envelope/');
$xpath->registerNamespace('ndfd', 'http://graphical.weather.gov/xml/DWMLgen/wsdl/ndfdXML.wsdl');
$innerXml = $xpath->evaluate(
'string(/soap:Envelope/soap:Body/ndfd:NDFDgenResponse/dwmlOut)'
);
echo $innerXml;
Output:
<?xml version="1.0"?>
<weather>XML</weather>

Append QBXML declaration in XML with DOMDocument

Trying to write clean XML for a QuickBooks integration using DOMDocument in PHP. The only thing I am stuck on is how to add the required <?qbxml version="2.0"?> after <?xml version="1.0" encoding="utf-8"?> to produce the following:
<?xml version="1.0" encoding="utf-8"?>
<?qbxml version="2.0"?>
.....
Here is what I have so far..
$dom = new DOMDocument('1.0', 'utf-8');
//Need to somehow add qbxml version here
$qbxml = $dom->appendChild($dom->createElement('QBXML'));
$qbxmlmsg = $qbxml->appendChild($dom->createElement('QBXMLMsgsRq'));
$qbxmlmsg->setAttribute('onError', 'stopOnError');
$salesReceiptAddRq = $qbxmlmsg->appendChild($dom->createElement('SalesReceiptAddRq'));
$salesReceiptAddRq->setAttribute('requestID', 1234);
$dom->formatOutput = true;
echo $dom->saveXML();
That node is called a processing instruction.
$dom = new DOMDocument('1.0', 'utf-8');
$dom->appendChild($dom->createProcessingInstruction('qbxml', 'version="2.0"'));
$qbxml = $dom->appendChild($dom->createElement('QBXML'));
// ...
echo $dom->saveXml();

Categories