Parsing SOAP response PHP - php

I have a SOAP response that I am trying to parse, however I seem to be getting an error.
SOAP response:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<ValidateNewUserResponse xmlns="urn:websitea.com/v2"><ValidateNewUserResult xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><Message i:nil="true"/>
<Status>true</Status>
<FailureReason>None</FailureReason>
<IdentityValidationOutcome>0</IdentityValidationOutcome>
<ValidationIdentifier>112244</ValidationIdentifier>
</ValidateNewUserResult></ValidateNewUserResponse>
</s:Body>
</s:Envelope>
I have tried the following code:
$doc = new DOMDocument();
$doc->loadXML($strXml);
echo $doc->getElementsByTagName('Status')->item(0)->nodeValue;
This produces the following error:
Trying to get property of non-object
I have also tried the following code:
$get_xml = str_ireplace(['S-ENV:', 'S:'], '', $strResponse);
$xml = simplexml_load_string($get_xml);
$status=((string)$xml->Body->ValidateNewUserResponse->ValidateNewUserResult->Status);echo "<br />";
Which produces the following error:
simplexml_load_string(): namespace error : Namespace prefix i for nil on Message is not defined

Try the following, works fine for me.
$doc = new DOMDocument();
$doc->loadXML($strResponse);
$result = $doc->getElementsByTagName('Status')->item(0)->nodeValue;
echo $result;

You will get the error if the XML string is corrupted or not properly escaped and you used double quotes. Testing your code, the following worked for me (using single quotes):
$strXml = '<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<ValidateNewUserResponse xmlns="urn:websitea.com/v2"><ValidateNewUserResult xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><Message i:nil="true"/>
<Status>true</Status>
<FailureReason>None</FailureReason>
<IdentityValidationOutcome>0</IdentityValidationOutcome>
<ValidationIdentifier>112244</ValidationIdentifier>
</ValidateNewUserResult></ValidateNewUserResponse>
</s:Body>
</s:Envelope>';
$doc = new DOMDocument();
$doc->loadXML($strXml);
echo $doc->getElementsByTagName('Status')->item(0)->nodeValue;

Or you can use a WSDL to php generator so you don't have to parse the XML response nor generate the XML request as you'll always handle PHP objects. This is the case using the basic native PHP SoapClient class. You can also use a WSDL to php generator such as PackageGenerator because it's the best way to consume SOAP in my point of view

Related

Parse XML with name namespaces PHP

I'm trying to parse XML to get "text" message:
<?xml version="1.0" encoding="UTF-8"?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Body>
<S:Fault xmlns:ns4="http://www.w3.org/2003/05/soap-envelope">
<faultcode>S:Server</faultcode>
<faultstring>Error saving JobsReport</faultstring>
<detail>
<ns2:ErrorMsg xmlns:ns2="http://www.testX.com.pl/wsdl/sdis-emm">
<Error code="20" text="More than one row with the given identifier was found: 389, for class: ekt.bean.sdis.RepPerson" />
</ns2:ErrorMsg>
</detail>
</S:Fault>
</S:Body>
</S:Envelope>
but SimpleXMLElement() and simplexml_load_file() returns only empty object. When I remove "S:" is better, but not ok.
registerXPathNamespace doesn't help.
Help, thx.
Looks like a Soap response. PHP 's own SoapClient object deals automatically with this XML and delivers objects as responses as described in the functions and types definitions of the webservice. Here 's a small illustrative example:
try {
$client = new SoapClient($wsdl, $options);
$response = $client->functionName($params);
var_dump($response);
// response would be an object with members as described by the webservice
} catch (SoapFault $e) {
// prints error message
echo $e->getMessage();
}
So for more comfort you should definitely take a look on SoapClient in the PHP manual. SoapClient handles all the XML stuff automatically so you will not get in touch with it.
If you still want to handle the XML requests and responses on yourself, you can do it with PHP 's own DomDocument object. Here 's a small illustrative example:
$dom = new DomDocument();
$dom->loadXml($xmlResponse);
foreach ($dom->getElementsByTagNameNS('http://www.testX.com.pl/wsdl/sdis-emm', 'Error') as $element) {
echo $element->getAttribute('text');
}
This is the more complicated way, because SoapClient throws a SoapFault, if an error occurs, which you can easiely catch and do whatever you want with it.

PHP SoapServer addSoapHeader ignored with SoapFault

I have written a PHP SOAP Service that accepts Basic Authentication credentials as outlined at http://www.whitemesa.com/soapauth.html. I did this by defining a method BasicAuth inside the handler class of the SOAPServer instance. This all works fine.
However, when authentication fails for some reason (incorrect username, no BasicAuth header in the request) I'd like to include a BasicChallenge header in my response, like this:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header>
<h:BasicChallenge xmlns:h="http://soap-authentication.org/basic/2001/10/"
SOAP-ENV:mustUnderstand="1">
<Realm>Realm</Realm>
</h:BasicChallenge>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<SOAP-ENV:Fault>
<faultcode>SOAP-ENV:Client</faultcode>
<faultstring>Authentication failed</faultstring>
</SOAP-ENV:Fault>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
The following code does not work (the header is not added to the response).
$soapServer->addSoapHeader(new SoapHeader("http://soap-authentication.org/basic/2001/10/", "BasicChallenge", array("Realm" => "Realm"), true));
throw new SoapFault("Client", "Authentication Failed");
Calling $soapServer->fault() instead of throw new SoapFault does not make a difference.
I've tried constructing the Fault object myself, and returning that as a regular response, but I was unable to get PHP to send a well-formed response.
Thanks in advance.
This was 2013 and I actually have exactly the same problem. Well, it 's 2019 and PHP 's current version is 7.3 and 7.4 is coming up to us with big steps. Unfortunately the SoapServer class ignores soap headers completely in a SoapFault case.
I 've written a small workaround to manipulate the XML response of the soap server. For everone who 's having the same issue, here 's a small example, how to solve it.
1. Initialise your Soap Server
$server = new SoapServer($wsdl, $options);
ob_start();
$server->setObject($service);
$server->handle();
$response = ob_get_contents();
ob_end_clean();
Actually we 're doing a simple initialization. Instead of simple returning the content we intercept the xml response with the output buffering functions. The result of the is a xml string in $response.
2. Find out if the reponse is a fault
Actually the SoapServer class is adding soap headers, if it was a valid response. We have to find out, if the response is a fault.
$doc = new DOMDocument();
$doc->loadXML($response);
$xpath = new DOMXPath($doc);
$isFault = $xpath->query('//*[local-name()="Fault"]')->length;
Just load the response xml string into the DOMDocument class. From now on we are able to access xml elements with DOM functions. For better handling I 'm using the DOMXPath class. Of course it is also possible to determine the XML nodes with the DOMDocument class. The $isFault variable is useful for the next step.
3. In fault case set a soap header
Unfortunately there is no fancy addSoapHeader function for simply setting a soap header. We have to do it manually in this case.
if ($isFault) {
$header = $doc->createElementNS('http://schemas.xmlsoap.org/soap/envelope/', 'Header');
$body = $doc->getElementsByTagNameNS('http://schemas.xmlsoap.org/soap/envelope/', 'Body')->item(0);
$realm = $doc->createElementNS('http://soap-authentication.org/basic/2001/10/', 'h:Realm');
$basicChallenge = $doc->createElementNS('http://soap-authentication.org/basic/2001/10/', 'h:BasicChallenge');
$basicChallenge->appendChild($realm);
$header->appendChild($basicChallenge);
$doc->documentElement->insertBefore($header, $body);
}
$xmlResponse = $doc->saveXML($doc->documentElement);
header('Content-Length: ' . strlen($xmlResponse));
echo $xmlResponse;
exit();
In a fault case we create a header element and add all the child nodes we need. If averything was appended insert the header before the body, save the new xml structure in a string and echo it.
Hope this helps a bit.

PHP cURL XML Response with single XML Tag

I am using SimpleXML to load a response from an API call. However, if the server returns an error, it only returns a single XML tag with the error message:
<err>ERROR MESSAGE HERE</err>
I'm currently using this code to parse the API response:
$parsedresponse = simplexml_load_string($response);
The $parsedresponse variable contains only the error message. However, I need a way to check if the <err> tag is present so I know if there was an error. I can't seem to figure out how to do this...
Thank you!
If err tag is the root tag use the following condition to trace the error.
if ($parsedresponse->getName()=='err'){
// got it
}
If its the first child use
if (isset($parsedresponse->err)){
// got it
}
negate the condition as necessary.
<?php
$xml = <<<EOF
<?xml version="1.0" encoding="UTF-8"?>
<err>ERROR MESSAGE HERE</err>
EOF;
$sxml = simplexml_load_string($xml);
if ($sxml->getName() != "err") print('not set');
else print('set');
?>

How to parse SOAP response in PHP

I have cracked my head the last few days trying to parse a soap response (via curl command-line), but I can not get it to work. I just want to get the object value of ResourceIdentifier which is rs-1304500829200-200. I am using PHP 5.3.x
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<ns2:deliverMDRecordsResponse xmlns:ns2="http://mdstore.data.dnetlib.eu/" xmlns:ns3="http://www.w3.org/2005/08/addressing">
<return>
<ns3:Address>http://129.70.12.20:8282/dnet-mdstore/services/MDStoreResultSet</ns3:Address>
<ns3:ReferenceParameters>
<ResourceIdentifier:ResourceIdentifier xmlns:ResourceIdentifier="http://www.driver.org" xmlns:wsa="http://www.w3.org/2005/08/addressing">rs-1304500829200-200</ResourceIdentifier:ResourceIdentifier>
</ns3:ReferenceParameters>
</return>
</ns2:deliverMDRecordsResponse>
</soap:Body>
</soap:Envelope>
<?
$response=<<<END ....soap response here... END;
$xml = simplexml_load_string($source);
$xml->registerXPathNamespace('t', 'http://www.driver.org');
foreach ($xml->xpath('//t:ResourceIdentifier') as $item)
{ // print_r($item);
echo $item->asXML();
}
?>
You should really be using the built in SoapClient class if you can, or if you can't, use the PEAR SOAP libraries. As you're running PHP 5.3, SoapClient ought to be available.
On the issue of your use of Xpath, you're querying against the wrong namespace and element. The namespace of the element you're querying is "http://www.driver.org", thus this should work, though keep in mind that I haven't actually ran it, though it should be correct:
<?php
$xml = simplexml_load_string($response);
$xml->registerXPathNamespace('rid', 'http://www.driver.org');
foreach ($xml->xpath('//rid:ResourceIdentifier') as $item) {
echo (string) $item;
}
?>
But please don't do that, use one of the two SOAP clients I mentioned. I've no idea where you got {http://apilistener.envoyservices.com}payment from as it's not mentioned in the response.

simplexml help how do I parse this?

I haven't done any xml projects, so I'm not quite sure what to do with this data...
I'm using curl to make a request to salesforce, and they give me back a response that I need to parse. I want to use simplexml. Here's part of the response:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns="urn:partner.soap.sforce.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<loginResponse>
<result>
<metadataServerUrl>
https://na6-api.salesforce.com/services/Soap/m/18.0/
</metadataServerUrl>
<passwordExpired>
false
</passwordExpired>
<sandbox>
false
</sandbox>
<serverUrl>
https://na6-api.salesforce.com/services/Soap/u/18.0/
</serverUrl>
<sessionId>
!AQ4AQLtDIqY.
</sessionId>
<userId>
</userId>
<userInfo>
<accessibilityMode>
false
</accessibilityMode>
<currencySymbol>
$
</currencySymbol>
<orgDefaultCurrencyIsoCode>
USD
</orgDefaultCurrencyIsoCode>
<orgDisallowHtmlAttachments>
false
</orgDisallowHtmlAttachments>
<orgHasPersonAccounts>
false
</orgHasPersonAccounts>
<organizationId>
</organizationId>
<organizationMultiCurrency>
false
</organizationMultiCurrency>
<organizationName>
Ox
</organizationName>
<profileId>
sdfgsdfg
</profileId>
<roleId>
sdfgsdfg
</roleId>
<userDefaultCurrencyIsoCode xsi:nil="true"/>
<userEmail>
####gmail.com
</userEmail>
<userFullName>
### ###
</userFullName>
<userId>
asdfasdf
</userId>
<userLanguage>
en_US
</userLanguage>
<userLocale>
en_US
</userLocale>
<userName>
asdfasdf#gmail.com
</userName>
<userTimeZone>
America/Chicago
</userTimeZone>
<userType>
Standard
</userType>
<userUiSkin>
Theme3
</userUiSkin>
</userInfo>
</result>
</loginResponse>
</soapenv:Body>
</soapenv:Envelope>
Anyway, I expected to feed that stuff (we'll call it data) into
$results = simplexml_load_string($data);
var_dump($results);
And that would give me all the data back... and then to access specific parts, it would be $results->body->loginResponse->blah->blah...
But It's not giving me that, it's not really giving me anything back, just an empty simple xml object...
So one website made me think I might need an XSLT to read this correctly.
Or something else made me think it's because I don't have at the top.
Help!
You can use SimpleXML but it's not quite as simple as you hope due to the use of namespaces (e.g. soapenv). Look into using SimpleXMLElement::children like:
$sxe = new SimpleXMLElement($data);
$login_response = $sxe->children('soapenv', TRUE)->Body->children('', TRUE)->loginResponse->result;
// Now that we have the <loginResponse> lets take a look at an item within it
echo $login_response->userInfo->userEmail;
Finally, and importantly, have you had a look at salesforce's tools & examples?
SimpleXML needs a special treatment for namespaced XML (ref.)
Mate,
Name spaces usually require you to make a call using children to return the namespaced elements. I would recommend using a soap client like php soapclient, but since I've never used it before there is one other possible option.
$results = simplexml_load_string($data);
$xml = $results->children('http://schemas.xmlsoap.org/soap/envelope/');
var_dump($xml);
I believe that's how it works.
For what it's worth, you may find you have an easier time using a PHP SoapClient for this task. O'Reilly has a good tutorial on PHP SOAP.
Also checkout the PHP Toolkit for making SOAP calls to Salesforce.com
I try to follow the syntax by salathe. But children('soapenv', TRUE) doens't work for me, Jason's children('http://schemas.xmlsoap.org/soap/envelope/') work.
Therefore, to read the field value CreatedDate in Salesforce Outbound Message, I need following code:
$rcXML->children('http://schemas.xmlsoap.org/soap/envelope/')->Body->children('http://soap.sforce.com/2005/09/outbound')->notifications->Notification->sObject->children('urn:sobject.enterprise.soap.sforce.com')->CreatedDate
To help you understand how it work, I write a post with sames code and xml which shall be easier to understand.
http://amigotechnotes.wordpress.com/2013/11/16/parse-xml-with-namespace-by-simplexml-in-php/
Parsing soap responses with SimpleXML has a brilliant and concise example of multi-namespace XML parsing.
For anyone wanting to get at the RateResponse from the UPS Rating API, here's how :
// $client is your SoapClient object
$dom = new DOMDocument;
$dom->loadXML($client->__getLastResponse());
$xml = simplexml_load_string($dom->saveXML(), NULL, NULL, 'http://schemas.xmlsoap.org/soap/envelope/');
$RateResponse = $xml->xpath('/soapenv:Envelope/soapenv:Body')[0]->children('rate', true)->RateResponse;
foreach($RateResponse->RatedShipment as $RatedShipment) {
print_r((array)$RatedShipment);
}
For SAOP request, we can parse easily with a short code by replacing the SOAP-ENV: tag with blank
$response = '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns="urn:partner.soap.sforce.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>';
$response = html_entity_decode($response);
$response = str_replace(['soapenv:', 'ns1:', ':ns1', 'SOAP-ENV:'], ['', '', '', ''], $response);
$objXmlData = simplexml_load_string($response);

Categories