To give you an overview of what I'm trying to accomplish here, I am running a WordPress site with a custom theme and utilizing the Gravity Forms plugin to create all forms on the site.
We are integrating all (2) forms on the site with an external lead management service. Of course their documentation on how to interact with their webservice is far from well documented.
The provided PDF states:
It is assumed that the third-party vendor is familiar with creating XML files, understands XSD documents in order to create well-formed XML documents, and has the appropriate tools necessary to submit and receive XML documents via a webservice.
Webservice URL:
https://interface.webservices.popcard.ltsolutions.com/service.asmx
Webservice Method
InsertTraffic
InsertTraffic is a method a third-party vendor will use to insert a single piece of traffic into Yardi PopCard’ PopCard application. This single piece of traffic could represent a prospect filling out a contact form on an ILS website or a phone call received from a prospect which was answered by a call center. - Method description from PDF
I have been in contact with the company and know that the XML that I'm trying to push to their webservice is correct, but the leads are not getting into the system.
<?php
add_action("gform_after_submission", "submit_contact_lead", 10, 2);
function submit_contact_lead($entry, $form){
$fname = $entry['1.3'];
$lname = $entry['1.6'];
$userEmail = $entry['2'];
if ($entry['3']) {
$comments = $entry['3'];
} else {
$comments = '';
}
$date = date('Y-m-d\TH:i');
$baseURL = 'http://interface.webservices.popcard.ltsolutions.com/service.asmx/InsertTraffic';
$xmlRequest = '<?xml version="1.0" encoding="utf-8"?>
<traffic
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
contactdatetime="'.$date.'"
transactiondatetime="'.$date.'">
<trafficsource>
<vendorid>551d12d8a1de</vendorid>
<emailaddress></emailaddress>
<sourcename></sourcename>
<propertyname>CityView</propertyname>
</trafficsource>
<prospect>
<firstname>'.$fname.'</firstname>
<middlename></middlename>
<lastname>'.$lname.'</lastname>
<streetaddress1></streetaddress1>
<streetaddress2></streetaddress2>
<city></city>
<state></state>
<zipcode></zipcode>
<daytimephone></daytimephone>
<eveningphone></eveningphone>
<cellphone></cellphone>
<otherphone></otherphone>
<emailaddress>'.$userEmail.'</emailaddress>
<comments>'.$comments.'</comments>
</prospect>
<prospectpreferences>
<pricerangemin></pricerangemin>
<pricerangemax></pricerangemax>
<numberofoccupants></numberofoccupants>
<pets></pets>
<dateneeded></dateneeded>
<appointmentdate></appointmentdate>
<appointmenttime></appointmenttime>
<numberofbedsdesired></numberofbedsdesired>
<numberofbathsdesired></numberofbathsdesired>
</prospectpreferences>
</traffic>';
$xmlRequest = preg_replace( "/\r|\n/", "", $xmlRequest );
// Set up cURL request directly in this funtion
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://interface.webservices.popcard.ltsolutions.com/service.asmx');
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/xml'));
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, "InsertTraffic=" . $xmlRequest);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 0); // set to 300 after testing purposes
curl_setopt($ch, CURLOPT_TIMEOUT, 0); // set to 300 after testing purposes
$result = curl_exec($ch);
curl_close($ch);
// $array_data = json_decode(json_encode(simplexml_load_string($data)), true);
error_log('PopCards Submission | Contact/Reserve Submission for '.$lname.', '. $fname.'.');
error_log($xmlRequest);
error_log($result);
}
As you can see at the end of the code I'm logging the lead and the result. The following is what I'm getting in my error_log file.
[10-Apr-2015 15:08:34 UTC] PopCards Submission | Contact/Reserve Submission for John, Doe.
[10-Apr-2015 15:08:34 UTC]
<?xml version="1.0" encoding="utf-8"?>
<traffic
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
contactdatetime="2015-04-10T15:08"
transactiondatetime="2015-04-10T15:08">
<trafficsource>
<vendorid>551d12d8a1de</vendorid>
<emailaddress></emailaddress>
<sourcename></sourcename>
<propertyname>CityView</propertyname>
</trafficsource>
<prospect>
<firstname>John</firstname>
<middlename></middlename>
<lastname>Doe</lastname>
<streetaddress1></streetaddress1>
<streetaddress2></streetaddress2>
<city></city>
<state></state>
<zipcode></zipcode>
<daytimephone></daytimephone>
<eveningphone></eveningphone>
<cellphone></cellphone>
<otherphone></otherphone>
<emailaddress>johndoe#gmail.com</emailaddress>
<comments></comments>
</prospect>
<prospectpreferences>
<pricerangemin></pricerangemin>
<pricerangemax></pricerangemax>
<numberofoccupants></numberofoccupants>
<pets></pets>
<dateneeded></dateneeded>
<appointmentdate></appointmentdate>
<appointmenttime></appointmenttime>
<numberofbedsdesired></numberofbedsdesired>
<numberofbathsdesired></numberofbathsdesired>
</prospectpreferences>
</traffic>
[10-Apr-2015 15:08:34 UTC]
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"/>
<title>500 - Internal server error.</title>
<style type="text/css">
<!--
body{margin:0;font-size:.7em;font-family:Verdana, Arial, Helvetica, sans-serif;background:#EEEEEE;}
fieldset{padding:0 15px 10px 15px;}
h1{font-size:2.4em;margin:0;color:#FFF;}
h2{font-size:1.7em;margin:0;color:#CC0000;}
h3{font-size:1.2em;margin:10px 0 0 0;color:#000000;}
#header{width:96%;margin:0 0 0 0;padding:6px 2% 6px 2%;font-family:"trebuchet MS", Verdana, sans-serif;color:#FFF;
background-color:#555555;}
#content{margin:0 0 0 2%;position:relative;}
.content-container{background:#FFF;width:96%;margin-top:8px;padding:10px;position:relative;}
-->
</style>
</head>
<body>
<div id="header"><h1>Server Error</h1></div>
<div id="content">
<div class="content-container"><fieldset>
<h2>500 - Internal server error.</h2>
<h3>There is a problem with the resource you are looking for, and it cannot be displayed.</h3>
</fieldset></div>
</div>
</body>
</html>
I need some assistance/guidance on what I'm doing wrong here.
Ok, we will take this one step at a time.
Your request is being rejected by the Serve the service is running on.
We need to get your request header from curl.
Leave the timeouts at 300, zero = never time out an you get no response and it hangs until (if) PHP times out.
None of these option I want yu to add will affect you Request, they are only for test and debug.
Change:
curl_setopt($ch, CURLOPT_HEADER, true);
Add these:
curl_setopt($ch, CURLINFO_HEADER_OUT, true);
curl_setopt($ch, CURLOPT_VERBOSE, true);
curl_setopt($ch, CURLOPT_FAILONERROR,true);
Add this code:
$result = curl_exec($ch);
if (curl_errno($ch)){
$data .= 'Retreive Base Page Error: ' . curl_error($ch);
}
else {
$skip = intval(curl_getinfo($ch, CURLINFO_HEADER_SIZE));
$responseHeader = substr($result ,0,$skip);
$result = substr($result ,$skip);
$info = var_export(curl_getinfo($ch),true);
}
$fp = fopen('xml.log','w');
fwrite($fp,"$responseHeader\n$info\n$result ");
fclose($fp);
Post this xml.log rather than your log. I think this will include what you have and more. Important stuff more.
Update
Change:
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/xml'));
To:
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type:application/xml'),'Content-Length: ' . strlen($xmlRequest ));
Why is this there? Maybe.
contactdatetime="$date" transactiondatetime="$date"
Heredoc format is better than concatenation "' . $date . '". too easy to miss a double quote or some other issue.
Put the namespace (xmlns) all on one line, don't need editor EOL issues.
$xmlRequest = <<<EOX
<?xml version="1.0" encoding="utf-8"?>
<traffic xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" contactdatetime="$date" transactiondatetime="$date">
<trafficsource>
<vendorid>551d12d8a1de</vendorid>
<emailaddress></emailaddress>
<sourcename></sourcename>
<propertyname>CityView</propertyname>
</trafficsource>
<prospect>
<firstname>$fname</firstname>
<middlename></middlename>
<lastname>$lname</lastname>
<streetaddress1></streetaddress1>
<streetaddress2></streetaddress2>
<city></city>
<state></state>
<zipcode></zipcode>
<daytimephone></daytimephone>
<eveningphone></eveningphone>
<cellphone></cellphone>
<otherphone></otherphone>
<emailaddress>$userEmail</emailaddress>
<comments>$comments</comments>
</prospect>
<prospectpreferences>
<pricerangemin></pricerangemin>
<pricerangemax></pricerangemax>
<numberofoccupants></numberofoccupants>
<pets></pets>
<dateneeded></dateneeded>
<appointmentdate></appointmentdate>
<appointmenttime></appointmenttime>
<numberofbedsdesired></numberofbedsdesired>
<numberofbathsdesired></numberofbathsdesired>
</prospectpreferences>
</traffic>
EOX;
end update 1
Update 2
This is what I think your XML should look like:
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<traffic xmlns="http://tempuri.org/PopCardInterfaceWebservice/Service1">
<trafficsource>
<vendorid>551d12d8a1de</vendorid>
<emailaddress></emailaddress>
<sourcename></sourcename>
<propertyname>CityView</propertyname>
</trafficsource>
<prospect>
<firstname>John</firstname>
<middlename></middlename>
<lastname>Doe</lastname>
<streetaddress1></streetaddress1>
<streetaddress2></streetaddress2>
<city></city>
<state></state>
<zipcode></zipcode>
<daytimephone></daytimephone>
<eveningphone></eveningphone>
<cellphone></cellphone>
<otherphone></otherphone>
<emailaddress>johndoe#gmail.com</emailaddress>
<comments></comments>
</prospect>
<prospectpreferences>
<pricerangemin></pricerangemin>
<pricerangemax></pricerangemax>
<numberofoccupants></numberofoccupants>
<pets></pets>
<dateneeded></dateneeded>
<appointmentdate></appointmentdate>
<appointmenttime></appointmenttime>
<numberofbedsdesired></numberofbedsdesired>
<numberofbathsdesired></numberofbathsdesired>
</prospectpreferences>
</traffic>
</soap:Body>
</soap:Envelope>
Related
I'm using Curl to execute a soap request.
Now it looks like there is a mistake returned in the headers that prevents me from turning the returned string into a simplexml object with the function simplexml_load_string. Below you can find the part of the response that fails in the simplexml function:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SOAP-ENV:Header><SOAP-SEC:Signature xmlns:SOAP-SEC="http://schemas.xmlsoap.org/soap/security/2000-12"><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:SignedInfo><ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#dsa-sha1"/><ds:Reference URI="#Body"><ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><ds:DigestValue>HV+/cOkUjNCdH5xuiLlGSHVgkUo=</ds:DigestValue></ds:Reference><ds:SignatureValue>MCwCFHXmoMrDUOScwMQ5g76OfxouICjBAhQtGKAorJLUQ0bA0UaKIe1gtmQPgA==</ds:SignatureValue></ds:SignedInfo></ds:Signature></SOAP-SEC:Signature></SOAP-ENV:Header><SOAP-ENV:Body xmlns:SOAP-SEC="http://schemas.xmlsoap.org/soap/security/2000-12" SOAP-SEC:id="Body">
Is there a way to isolate the soap body content and parsing only that part with the simplexml_load_string?
Below the curl request:
$headers = array(
"Content-type: text/xml;charset=\"utf-8\"",
"Accept: text/xml",
"Cache-Control: no-cache",
"Pragma: no-cache",
"Content-length: ".strlen($xml_post_string),
);
$url = $soapUrl;
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml_post_string);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
$response = curl_exec($ch);
curl_close($ch);
$xml = simplexml_load_string(html_entity_decode($response), 'SimpleXMLElement', LIBXML_NOCDATA);
echo $xml->asXML();
if ($xml === false) {
echo "Failed to load XML: ";
foreach(libxml_get_errors() as $error) {
echo "<br>", $error->message;
}
} else {
var_dump($xml);
}
I don't have an answer for you right now, but you first need to separate curl from XML processing. You should start with logging your result from curl and making sure it is sane and what you expect. If it is, then move on to parsing it. curl should never break/change your data in any way, but the request itself (headers, etc.) might change the server's response.
Since I can't validate your server, I'm just going to go off of what you've provided. I've closed the <SOAP-ENV:Body> tag and converted the XML to readable, but otherwise it is untouched. This code parses the XML without a problem and then emits it exactly as expected.
$response = <<<'TAG'
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<SOAP-ENV:Header>
<SOAP-SEC:Signature xmlns:SOAP-SEC="http://schemas.xmlsoap.org/soap/security/2000-12">
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:SignedInfo>
<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#dsa-sha1" />
<ds:Reference URI="#Body">
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
<ds:DigestValue>HV+/cOkUjNCdH5xuiLlGSHVgkUo=</ds:DigestValue>
</ds:Reference>
<ds:SignatureValue>MCwCFHXmoMrDUOScwMQ5g76OfxouICjBAhQtGKAorJLUQ0bA0UaKIe1gtmQPgA==</ds:SignatureValue>
</ds:SignedInfo>
</ds:Signature>
</SOAP-SEC:Signature>
</SOAP-ENV:Header>
<SOAP-ENV:Body xmlns:SOAP-SEC="http://schemas.xmlsoap.org/soap/security/2000-12" SOAP-SEC:id="Body"></SOAP-ENV:Body>
</SOAP-ENV:Envelope>
TAG;
$xml = simplexml_load_string(html_entity_decode($response), 'SimpleXMLElement', LIBXML_NOCDATA);
echo '<pre>';
print_r(htmlspecialchars($xml->asXML()));
echo '</pre>';
The output is exactly the same as the input except it includes the XML directive and converts the body tag to self-closing:
<?xml version="1.0"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<SOAP-ENV:Header>
<SOAP-SEC:Signature xmlns:SOAP-SEC="http://schemas.xmlsoap.org/soap/security/2000-12">
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:SignedInfo>
<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#dsa-sha1"/>
<ds:Reference URI="#Body">
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<ds:DigestValue>HV+/cOkUjNCdH5xuiLlGSHVgkUo=</ds:DigestValue>
</ds:Reference>
<ds:SignatureValue>MCwCFHXmoMrDUOScwMQ5g76OfxouICjBAhQtGKAorJLUQ0bA0UaKIe1gtmQPgA==</ds:SignatureValue>
</ds:SignedInfo>
</ds:Signature>
</SOAP-SEC:Signature>
</SOAP-ENV:Header>
<SOAP-ENV:Body xmlns:SOAP-SEC="http://schemas.xmlsoap.org/soap/security/2000-12" SOAP-SEC:id="Body"/>
</SOAP-ENV:Envelope>
So use this as a baseline. Write your curl response to a text file before doing anything else, and then read that text file back in and perform logic. Any transformation you apply to the string XML should also be logged and compared to make sure it is doing what you expected. On production you'd skip that but this just helps during the debugging.
Also, I'm not really sure what the point of html_entity_decode is in this. If you are receiving XML (as your request mime type specifies) then it shouldn't have any escape sequences applied to it, but maybe you have an exceptional case, too.
Just to give some example XML content, this will vary for any file but just shows how you can access the data...
<SOAP-ENV:Body
xmlns:SOAP-SEC="http://schemas.xmlsoap.org/soap/security/2000-12"
SOAP-SEC:id="Body">
<BodyContent>SomeData</BodyContent>
<OtherContent>2</OtherContent>
</SOAP-ENV:Body>
Then it would be a case of using XPath to find the <SOAP-ENV:Body> tag
$xml->registerXPathNamespace("SOAP-ENV", "http://schemas.xmlsoap.org/soap/envelope/");
$bodyBlock = $xml->xpath("//SOAP-ENV:Body")[0];
(note that as xpath() returns a list of matches, using [0] just uses the first one).
This next part depends on the message being processed, but as the example I gave has child elements with no namespace prefix, then you can extract these using ->children() and this eases access to the contents. The main part is that at this point the $bodyBlock contains this...
<SOAP-ENV:Body xmlns:SOAP-SEC="http://schemas.xmlsoap.org/soap/security/2000-12" SOAP-SEC:id="Body">
<BodyContent>SomeData</BodyContent>
<OtherContent>2</OtherContent>
</SOAP-ENV:Body>
So to put that together in your original code...
$xml = simplexml_load_string($response, 'SimpleXMLElement', LIBXML_NOCDATA);
if ($xml === false) {
echo "Failed to load XML: ";
foreach(libxml_get_errors() as $error) {
echo "<br>", $error->message;
}
} else {
// Search for the Body element (this is in the SOAP-ENV namespace)
$xml->registerXPathNamespace("SOAP-ENV", "http://schemas.xmlsoap.org/soap/envelope/");
$bodyBlock = $xml->xpath("//SOAP-ENV:Body")[0];
// If the content does not have a namespace, extract the children from the default namespace
$body = $bodyBlock->children();
// You can now access the content.
echo $body->BodyContent.PHP_EOL;
echo $body->OtherContent;
}
which outputs the two values in the body....
SomeData
2
I do a Soap request but I obtain an string imposible to convert to XML, what is the problem?
This is what I do:
$url = "https://test.com/services";
$XML ='<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<Consult Localitation xmlns="Services/">
<XMLin>
<ConsultXMLin Language="1" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Consult>
<Code>XXXXX0700005020128012D</Code>
</Consult>
</ConsultXMLin></XMLin>
</Consult Localitation></soap:Body>
</soap:Envelope>';
$ch = curl_init();
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_HEADER, FALSE);
curl_setopt($ch, CURLOPT_FORBID_REUSE, TRUE);
curl_setopt($ch, CURLOPT_FRESH_CONNECT, TRUE);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HTTPHEADER, Array( 'Content-Type: text/xml; charset=utf-8','Content-Length: '.strlen($XML),'SOAPAction: Services'));
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $XML);
$postResult = curl_exec($ch);
$test= simplexml_load_string($postResult);
print_r($test); // I obtain nothing.
I obtain this string from the curl response:
string(1128) "<?xml version="1.0" encoding="Windows-1252"?><ConsultaXMLout xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><Respuestas><DatosIdiomas><DatosEnvios><Datos Idioma="1" Codigo="XXXXX0700005020128012D" Evento="1" web_id="Sin web_id"><Estado>Información sobre su envío no disponible. Compruebe si es correcto.</Estado><Descripcion>La información sobre su envío todavía no está disponible. Por favor, realice su consulta transcurridos unos días.</Descripcion><Fecha /></Datos></DatosEnvios></DatosIdiomas></Respuestas></ConsultaXMLout>"
Thank you in advance!
You're doing a somewhat common (but easy to prevent) mistake in the code: The XML is created "by hand" as writing a string. Even thought this is possible, this is also very error prone.
The XML you provide in your question is with many errors and neither well-formed nor valid. If you want to learn more about these two terms, please see Is there a difference between 'valid xml' and 'well formed xml'? (Sep 2008).
This shows the errors your string produces when loaded into a DOMDocument or a SimpleXMLElement:
#001 <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
#002 xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
#003 <soap:Body>
#004 <Consult Localitation xmlns="Services/">
[FATAL] ^- (41) Specification mandate value for attribute Localitation (4:41)
[FATAL] ^- (65) attributes construct error (4:41)
[FATAL] ^- (73) Couldn't find end of Start Tag Consult line 4 (4:41)
#005 <XMLin>
#006 <ConsultXMLin Language="1" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
#007 <Consult>
#008 <Code>XXXXX0700005020128012D</Code>
#009 </Consult>
#010 </ConsultXMLin></XMLin>
#011 </Consult Localitation></soap:Body>
[FATAL] ^ ^- (76) Opening and ending tag mismatch: Envelope line 1 and Body (11:55)
[FATAL] ^- (73) expected '>' (11:30)
[FATAL] ^- (76) Opening and ending tag mismatch: Body line 3 and Consult (11:30)
#012 </soap:Envelope>
[FATAL] ^- (5) Extra content at the end of the document (12:21)
Instead of creating the SOAP XML via a string, you can make use of existing libraries like SimpleXML or - as this is Soap related - SoapClient.
The problem is that print_r doesn't do well at displaying the output of simplexml parsing. Full credit to #Josh Davis and #hakre, who discuss this at this answer. Try
print_r($test->xpath("//Estado"))
and you should get the contents of the <Estado> tag. See examples from the PHP manual for more ways to retrieve the content.
Finally I found the solution.
// String to extract string from.
string(1128) "<?xml version="1.0" encoding="Windows-1252"?><ConsultaXMLout xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><Respuestas><DatosIdiomas><DatosEnvios><Datos Idioma="1" Codigo="XXXXX0700005020128012D" Evento="1" web_id="Sin web_id"><Estado>Información sobre su envío no disponible. Compruebe si es correcto.</Estado><Descripcion>La información sobre su envío todavía no está disponible. Por favor, realice su consulta transcurridos unos días.</Descripcion><Fecha /></Datos></DatosEnvios></DatosIdiomas></Respuestas></ConsultaXMLout>";
I used htmlspecialchars of the response, then I obtained the full code.
// Call the function.
echo extractString($string, '<XmlIn>', '</Xmlin>');
// Here I taked all the code inside these two tags with the function extractString.
// Function that returns the string between two strings.
function extractString($string, $start, $end) {
$string = " ".$string;
$ini = strpos($string, $start);
if ($ini == 0) return "";
$ini += strlen($start);
$len = strpos($string, $end, $ini) - $ini;
return substr($string, $ini, $len);
And this is the function that gets the content between two tags.
I hope that it will be usefull ;)
I am trying to call a soap server method. Everything works fine except one thing. I get a respone from the server in XML format. So far so good. But the problem is that i need to get the values of the XML and normally i do that just with a foreach and get the values i need. But this time the name of the child i need to get data from is called: 'return'. So i can not reference to that in a foreach function.
Could someone tell me how i can reach the same result but with a different way?
My answer from the server:
<soap:envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:body>
<ns2:getauthresponse xmlns:ns2="http://dpd.com/common/service/types/LoginService/2.0" xmlns:ns3="http://dpd.com/common/service/exceptions">
<return>
<delisid>thedelisid</delisid>
<customeruid>thecustomerid</customeruid>
<authtoken>theauthenticationcode</authtoken>
<depot>thedepot</depot>
</return>
</ns2:getauthresponse>
</soap:body>
</soap:envelope>
the code i would normally use to get the result:
foreach($xml->return->authtoken as $authtoken)
{
print_r($authtoken);
}
The problem is the return sign here, php keeps seeing it as the return statement.
I also made it an array using new SimpleXMLElement.
And the error i get when i run the code is:
Invalid argument supplied for foreach()
How can i get the value of authtoken?
All the code:
$xml_getAuth = '
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns="http://dpd.com/common/service/types/LoginService/2.0">
<soapenv:Header/>
<soapenv:Body>
<ns:getAuth>
<delisId>'.$delisId.'</delisId>
<password>'.$password.'</password>
<messageLanguage>'.$messageLanguage.'</messageLanguage>
</ns:getAuth>
</soapenv:Body>
<soapenv:Envelope>
';
$headers_getAuth = array(
"POST HTTP/1.1",
"Content-type: application/soap+xml; charset=\"utf-8\"",
"SOAPAction: \"http://dpd.com/common/service/LoginService/2.0/getAuth\"",
"Content-length: ".strlen($xml_getAuth)
);
$getAuth = curl_init('https://public-ws-stage.dpd.com/services/LoginService/V2_0/');
curl_setopt($getAuth, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($getAuth, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($getAuth, CURLOPT_POST, 1);
curl_setopt($getAuth, CURLOPT_HTTPHEADER, $headers_getAuth);
curl_setopt($getAuth, CURLOPT_POSTFIELDS, "$xml_getAuth");
curl_setopt($getAuth, CURLOPT_RETURNTRANSFER, 1);
$output_getAuth = curl_exec($getAuth);
//Gebruikernsaam en wachtwoord komen niet overeen
if(strpos($output_getAuth,'LOGIN_8') !== false)
{
echo "Verkeerde gebruikernsaam of wachtwoord, neem contact op met uw systeembeheerder voor meer informatie.";
exit;
}
$xml = new SimpleXMLElement($output_getAuth);
foreach($xml->return->authtoken as $authtoken)
{
print_r($authtoken);
}
Answer from the soap call:
stdClass Object ( [return] => stdClass Object ( [delisId] => delisid [customerUid] => custid [authToken] => authtoken [depot] => depot ) )
First of all, you should probably be using SoapClient instead of SimpleXML. For example:
$c = new SoapClient('https://public-ws-stage.dpd.com/services/LoginService/V2_0/?WSDL');
$res = $c->getAuth(array(
'delisId' => 'foo',
'password' => 'bar',
'messageLanguage' => 'en-us',
));
echo $res->result->authToken;
That said, using xpath solves many problems:
<?php
$response = <<<XML
<soap:envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:body>
<ns2:getauthresponse xmlns:ns2="http://dpd.com/common/service/types/LoginService/2.0" xmlns:ns3="http://dpd.com/common/service/exceptions">
<return>
<delisid>thedelisid</delisid>
<customeruid>thecustomerid</customeruid>
<authtoken>theauthenticationcode</authtoken>
<depot>thedepot</depot>
</return>
</ns2:getauthresponse>
</soap:body>
</soap:envelope>
XML;
$xml = simplexml_load_string($response);
foreach ($xml->xpath('//return') as $tmp) {
echo "authtoken = ", $tmp->authtoken, PHP_EOL;
}
Im trying to get the shipping info of a product, this is my code (I have hidden my app-id).
$endpoint2 = "http://open.api.ebay.com/shopping";
$xmlrequest2 = "<?xml version='1.0' encoding='utf-8'?>\n";
$xmlrequest2 .= "<GetShippingCostsRequest xmlns='urn:ebay:apis:eBLBaseComponents'>\n";
// $xmlrequest2 .= "<DestinationCountryCode>GB</DestinationCountryCode>\n";
// $xmlrequest2 .= "<IncludeDetails>true</IncludeDetails>\n";
$xmlrequest2 .= "<ItemID>".$id."</ItemID>\n";
$xmlrequest2 .= "</GetShippingCostsRequest>\n";
$session2 = curl_init($endpoint2); // create a curl session
curl_setopt($session2, CURLOPT_POST, true);
var_dump($xmlrequest2);
curl_setopt($session2, CURLOPT_POSTFIELDS, $xmlrequest2); // set the body of the POST
curl_setopt($session2, CURLOPT_RETURNTRANSFER, true);
$headers2 = array(
'X-EBAY-API-APP-ID:'.getsetting(2),
'X-EBAY-API-VERSION:849',
'X-EBAY-API-SITE-ID:3',
'X-EBAY-API-CALL-NAME:GetShippingCosts',
'X-EBAY-API-REQUEST-ENCODING:XML',
);
print_r($headers2);
// create a curl session
curl_setopt($session2, CURLOPT_HTTPHEADER, $headers2); //set headers using the above array of headers
$responseXML = curl_exec($session2);
// send the request
//echo $responseXML;
curl_close($session2);
return $responseXML;
And this is the output/errors i get.
string(162) "<?xml version='1.0' encoding='utf-8'?>
<GetShippingCostsRequest xmlns='urn:ebay:apis:eBLBaseComponents'>
<ItemID>300903657321</ItemID>
</GetShippingCostsRequest>
"
Array
(
[0] => X-EBAY-API-APP-ID:1234
[1] => X-EBAY-API-VERSION:849
[2] => X-EBAY-API-SITE-ID:3
[3] => X-EBAY-API-CALL-NAME:GetShippingCosts
[4] => X-EBAY-API-REQUEST-ENCODING:XML
)
<?xml version="1.0" encoding="UTF-8"?>
<GetShippingCostsResponse xmlns="">
<ns1:Ack xmlns:ns1="urn:ebay:apis:eBLBaseComponents">Failure</ns1:Ack>
<ns2:Errors xmlns:ns2="urn:ebay:apis:eBLBaseComponents">
<ns2:ShortMessage>Input data is invalid.</ns2:ShortMessage>
<ns2:LongMessage>Input data for the given tag is invalid or missing. Please check API documentation.</ns2:LongMessage>
<ns2:ErrorCode>1.22</ns2:ErrorCode>
<ns2:SeverityCode>Error</ns2:SeverityCode>
<ns2:ErrorParameters ParamID="0">
<ns2:Value>XML document structures must start and end within the same entity.</ns2:Value>
</ns2:ErrorParameters>
<ns2:ErrorClassification>RequestError</ns2:ErrorClassification>
</ns2:Errors>
<ns3:Build xmlns:ns3="urn:ebay:apis:eBLBaseComponents">E853_CORE_APILW_16579549_R1</ns3:Build>
<ns4:Version xmlns:ns4="urn:ebay:apis:eBLBaseComponents">853</ns4:Version>
</GetShippingCostsResponse>
Any ideas? I can't work out at all what the problem is. Nothing on their documentation seems to help. If i copy and paste the XML into their API Test Tool it works no problems.
I had the same problem with every call of eBay Shopping API. You can fix it adding Content-Type: text/xml;charset=UTF-8 to your headers (this fix the problem for me).
I don't see your credentials listed anywhere. The API tool they give you is a bit deceptive in that it wraps your calls for you with the credentials. http://developer.ebay.com/DevZone/guides/ebayfeatures/Basics/Call-StandardCallData.html#StandardOutputData
<?xml version="1.0" encoding="utf-8"?>
<GeteBayOfficialTimeRequest xmlns="urn:ebay:apis:eBLBaseComponents">
<RequesterCredentials>
<eBayAuthToken> Token goes here </eBayAuthToken>
</RequesterCredentials>
<Version>383</Version>
</GeteBayOfficialTimeRequest>
I know there are any number of similar questions to this on SO, but I've tried messing around with all the solutions and haven't seemed to be able to make it work. I am trying to post xml directly to a web service and get a response back. Technically I am trying to connect to freightquote.com, the documentation for which you can find in the upper right hand corner of this page under documentation. I only mention that because I see the term SOAP a lot in their xml and it might make a difference. Anyway what I want is the ability to send xml to some url and get a response back.
So if I had the following
$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>
<GetRatingEngineQuote xmlns='http://tempuri.org/'>
<request>
<CustomerId>0</CustomerId> <!-- Identifier for customer provided by Freightquote -->
<QuoteType>B2B</QuoteType> <!-- B2B / eBay /Freightview -->
<ServiceType>LTL</ServiceType> <!-- LTL / Truckload / Groupage / Haulage / Al -->
<QuoteShipment>
<IsBlind>false</IsBlind>
<PickupDate>2010-09-13T00:00:00</PickupDate>
<SortAndSegregate>false</SortAndSegregate>
<ShipmentLocations>
<Location>
<LocationType>Origin</LocationType>
<RequiresArrivalNotification>false</RequiresArrivalNotification>
<HasDeliveryAppointment>false</HasDeliveryAppointment>
<IsLimitedAccess>false</IsLimitedAccess>
<HasLoadingDock>false</HasLoadingDock>
<IsConstructionSite>false</IsConstructionSite>
<RequiresInsideDelivery>false</RequiresInsideDelivery>
<IsTradeShow>false</IsTradeShow>
<IsResidential>false</IsResidential>
<RequiresLiftgate>false</RequiresLiftgate>
<LocationAddress>
<PostalCode>30303</PostalCode>
<CountryCode>US</CountryCode>
</LocationAddress>
<AdditionalServices />
</Location>
<Location>
<LocationType>Destination</LocationType>
<RequiresArrivalNotification>false</RequiresArrivalNotification>
<HasDeliveryAppointment>false</HasDeliveryAppointment>
<IsLimitedAccess>false</IsLimitedAccess>
<HasLoadingDock>false</HasLoadingDock>
<IsConstructionSite>false</IsConstructionSite>
<RequiresInsideDelivery>false</RequiresInsideDelivery>
<IsTradeShow>false</IsTradeShow>
<IsResidential>false</IsResidential>
<RequiresLiftgate>false</RequiresLiftgate>
<LocationAddress>
<PostalCode>60606</PostalCode>
<CountryCode>US</CountryCode>
</LocationAddress>
<AdditionalServices />
</Location>
</ShipmentLocations>
<ShipmentProducts>
<Product>
<Class>55</Class>
<Weight>1200</Weight>
<Length>0</Length>
<Width>0</Width>
<Height>0</Height>
<ProductDescription>Books</ProductDescription>
<PackageType>Pallets_48x48</PackageType>
<IsStackable>false</IsStackable>
<DeclaredValue>0</DeclaredValue>
<CommodityType>GeneralMerchandise</CommodityType>
<ContentType>NewCommercialGoods</ContentType>
<IsHazardousMaterial>false</IsHazardousMaterial>
<PieceCount>5</PieceCount>
<ItemNumber>0</ItemNumber>
</Product>
</ShipmentProducts>
<ShipmentContacts />
</QuoteShipment>
</request>
<user>
<Name>someone#something.com</Name>
<Password>password</Password>
</user>
</GetRatingEngineQuote>
</soap:Body>
</soap:Envelope>";
(I edited this to contain my actual xml since it may lend some perspective
I'd want to send it to http://www.someexample.com and get a response. Also, do I need to encode it? I've done a lot of sending xml back and forth with android, and never had to but that might be part of my problem.
My attempt to send the information currently looks like this
$xml_post_string = 'XML='.urlencode($xml->asXML());
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://b2b.Freightquote.com/WebService/QuoteService.asmx');
curl_setopt($ch, CURLOPT_POST, TRUE);
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml_post_string);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$response = curl_exec($ch);
curl_close($ch);
If you are walking around SOAP services, I strongly recommend you to learn basics once, and then use this great tool again and again. There are many features you can just use, or you will be reinventing the wheel and struggling with generating xml files, parsing xml files, faults etc. Use prepared tools and your life will be easier and your code better (less bugs).
Look at http://www.php.net/manual/en/soapclient.soapcall.php#example-5266 how to consume SOAP webservice. It is not so hard to understand.
Here is some code how you can analyze webserivce. Then map types to classes and just send and receive php objects. You can look for some tool to generate classes automatically (http://www.urdalen.no/wsdl2php/manual.php).
<?php
try
{
$client = new SoapClient('http://b2b.freightquote.com/WebService/QuoteService.asmx?WSDL');
// read function list
$funcstions = $client->__getFunctions();
var_dump($funcstions);
// read some request obejct
$response = $client->__getTypes();
var_dump($response);
}
catch (SoapFault $e)
{
// do some service level error stuff
}
catch (Exception $e)
{
// do some application level error stuff
}
If you will use wsdl2php generating tool, everything is very easy:
<?php
require_once('./QuoteService.php');
try
{
$client = new QuoteService();
// create request
$tracking = new TrackingRequest();
$tracking->BOLNumber = 67635735;
$request = new GetTrackingInformation();
$request->request = $tracking;
// send request
$response = $client->GetTrackingInformation($request);
var_dump($response);
}
catch (SoapFault $e)
{
// do some service level error stuff
echo 'Soap fault ' . $e->getMessage();
}
catch (Exception $e)
{
// do some application level error stuff
echo 'Error ' . $e->getMessage();
}
Generated php code for QuoteService.php you can see here: http://pastie.org/8165331
This is captured communication:
Request
POST /WebService/QuoteService.asmx HTTP/1.1
Host: b2b.freightquote.com
Connection: Keep-Alive
User-Agent: PHP-SOAP/5.4.17
Content-Type: text/xml; charset=utf-8
SOAPAction: "http://tempuri.org/GetTrackingInformation"
Content-Length: 324
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://tempuri.org/">
<SOAP-ENV:Body>
<ns1:GetTrackingInformation>
<ns1:request>
<ns1:BOLNumber>67635735</ns1:BOLNumber>
</ns1:request>
</ns1:GetTrackingInformation>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Response
HTTP/1.1 200 OK
Date: Mon, 22 Jul 2013 21:46:06 GMT
Server: Microsoft-IIS/6.0
X-Powered-By: ASP.NET
X-AspNet-Version: 2.0.50727
Cache-Control: private, max-age=0
Content-Type: text/xml; charset=utf-8
Content-Length: 660
Set-Cookie: BIGipServerb2b_freightquote_com=570501130.20480.0000; path=/
<?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>
<GetTrackingInformationResponse xmlns="http://tempuri.org/">
<GetTrackingInformationResult>
<BOLNumber>0</BOLNumber>
<EstimatedDelivery>0001-01-01T00:00:00</EstimatedDelivery>
<TrackingLogs />
<ValidationErrors>
<B2BError>
<ErrorType>Validation</ErrorType>
<ErrorMessage>Unable to find shipment with BOL 67635735.</ErrorMessage>
</B2BError>
</ValidationErrors>
</GetTrackingInformationResult>
</GetTrackingInformationResponse>
</soap:Body>
</soap:Envelope>
First, if your code is written like this, I doubt this works be cause of the quotes...
You should use double quote around your xml:
$my_xml = "<?xml version='1.0' standalone='yes'?>
<user>
<Name>xmltest#freightquote.com</Name>
<Password>XML</Password>
</user>";
Also, you could use poster, a firefox addon (there is probably the equivalent on chrome), to help you with your requests, especially if you use WebServices. That way, you will be able to see if the error is server-side or client-side.
This should help you debugging.
I use this command-line script to test SOAP call:
#!/usr/bin/php
<?php
//file client-test.php
$xml_data = file_get_contents('php://stdin');
$ch = curl_init('http://example.com/server/');
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: text/xml'));
curl_setopt($ch, CURLOPT_HTTPHEADER, array('SOAPAction', 'MySoapAction'));
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml_data);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$output = curl_exec($ch);
curl_close($ch);
print_r($output);
Usage like this (in command line) :
$ client-test.php < yourSoapEnveloppe.xml
In this example the yourSoapEnveloppe.xml file is the content of your $xml variable.
You can use stream_context_create and file_get_contents to send xml in post.
$xml = "<your_xml_string>";
$send_context = stream_context_create(array(
'http' => array(
'method' => 'POST',
'header' => 'Content-Type: application/xml',
'content' => $xml
)
));
print file_get_contents($url, false, $send_context);