Felipe Figueiredo
Felipe Figueiredo

Reputation: 3

Difficulties in extracting content from a SimpleXMLElement

I have this XML:

<?xml version='1.0' encoding='UTF-8'?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
  <soapenv:Body>
    <soapenv:Fault>
      <faultcode>soapenv:Server</faultcode>
      <faultstring>For input string: ""</faultstring>
      <detail />
    </soapenv:Fault>
  </soapenv:Body>
</soapenv:Envelope>

I want to get the content of Body > Fault > faultcode, faultstring

But I'm not getting the expected output with this code:

$xml = "\<?xml version='1.0' encoding='UTF-8'?\>\<soapenv:Envelope xmlns:soapenv='http://schemas.xmlsoap.org/soap/envelope/'\>\<soapenv:Body\>\<soapenv:Fault\>\<faultcode\>soapenv:Server\</faultcode\>\<faultstring\>For input string: '\</faultstring\>\<detail /\>\</soapenv:Fault\>\</soapenv:Body\>\</soapenv:Envelope\>";

$xmlObject = simplexml_load_string($xml, "SimpleXMLElement", LIBXML_NOCDATA);
$namespaces = $xmlObject->getNamespaces(true);
$body = $xmlObject->children($namespaces['soapenv'])->Body->Fault->faultcode;
dd($body);

output :

SimpleXMLElement {#1890}

Upvotes: 0

Views: 58

Answers (2)

&#193;lvaro Gonz&#225;lez
&#193;lvaro Gonz&#225;lez

Reputation: 146588

There is no <faultcode> in the soapenv namespace so you need to switch namespaces:

$xmlObject->children($namespaces['soapenv'])->Body->Fault->children()->faultcode

Full working example (with damaged XML corrected):

$xml = "<?xml version='1.0' encoding='UTF-8'?>
<soapenv:Envelope xmlns:soapenv='http://schemas.xmlsoap.org/soap/envelope/'>
    <soapenv:Body>
        <soapenv:Fault>
            <faultcode>soapenv:Server</faultcode>
            <faultstring>For input string: '</faultstring>
            <detail/>
        </soapenv:Fault>
    </soapenv:Body>
</soapenv:Envelope>";

$xmlObject = simplexml_load_string($xml, "SimpleXMLElement", LIBXML_NOCDATA);
$namespaces = $xmlObject->getNamespaces(true);
$faultcode = $xmlObject->children($namespaces['soapenv'])->Body->Fault->children()->faultcode;
var_dump((string)$faultcode);
string(14) "soapenv:Server"

Demo.

To be fully correct, though, it's important to be aware that what really defines a namespace is the URI (http://schemas.xmlsoap.org/soap/envelope/ here). The prefix (soapenv) can be changed locally, which is the whole point of having namespaces in the first place: to avoid name collisions. Having that into account, the namespace becomes hard-coded data:

$xml = "<?xml version='1.0' encoding='UTF-8'?>
<changedToIllustrateHowNamespacesWork:Envelope xmlns:changedToIllustrateHowNamespacesWork='http://schemas.xmlsoap.org/soap/envelope/'>
    <changedToIllustrateHowNamespacesWork:Body>
        <changedToIllustrateHowNamespacesWork:Fault>
            <faultcode>changedToIllustrateHowNamespacesWork:Server</faultcode>
            <faultstring>For input string: '</faultstring>
            <detail/>
        </changedToIllustrateHowNamespacesWork:Fault>
    </changedToIllustrateHowNamespacesWork:Body>
</changedToIllustrateHowNamespacesWork:Envelope>";

const SOAPENV_NAMESPACE_URI = 'http://schemas.xmlsoap.org/soap/envelope/';

$xmlObject = simplexml_load_string($xml, "SimpleXMLElement", LIBXML_NOCDATA);
$faultcode = $xmlObject->children(SOAPENV_NAMESPACE_URI)->Body->Fault->children()->faultcode;
var_dump((string)$faultcode);
string(43) "changedToIllustrateHowNamespacesWork:Server"

Demo.

On a side note, there is a dedicated SOAP extension though, admittedly, it isn't as intuitive as it could. You use that extension to do the whole request and map results to PHP classes, and that way you don't need to parse responses manually.

Upvotes: 1

Angel Cubas
Angel Cubas

Reputation: 26

I think your mistake is how you get the "faultstring" property. Remember that you are setting the "soapenv" namespace and the "faultcode" property does not have this namespace. This is the example I was trying:

<?php

$xml = <<<XML
<?xml version='1.0' encoding='UTF-8'?>
    <soapenv:Envelope xmlns:soapenv='http://schemas.xmlsoap.org/soap/envelope/'>
        <soapenv:Body>
            <soapenv:Fault>
                <faultcode>soapenv:Server</faultcode>
                <faultstring>For input string</faultstring>
                <detail />
            </soapenv:Fault>
        </soapenv:Body>
    </soapenv:Envelope>
XML;

$xmlObject = simplexml_load_string($xml);
$namespaces = $xmlObject->getNamespaces(true);
$body = $xmlObject->children($namespaces['soapenv'])->Body->Fault;

print_r($body->xpath('faultstring'));

This is the result: Screenshot of result

I also leave you an example of how I think it would be easier to obtain the value you need from the "faultstring" property:

<?php

try {
    $dom = new DOMDocument();
    $dom->loadXML($xml);
    $xpath = new DOMXPath($dom);

    // Execute XPath query to get "faultstring" property value
    $faultString = $xpath->evaluate('//soapenv:Fault/faultstring');

    var_dump($faultString->item(0)->nodeValue);
} catch (Exception $e) {
    echo "Error: " . $e->getMessage();
}

This is the result: Screenshot of result

Upvotes: 1

Related Questions