I’m developing a new piece of functionality for a client so that he can sync up his accounts software with his site (EE powered, with a cartthrob shopping basket)
His software (Sage) grabs an XML feed from a URL and then once it’s got it, it returns an XML confirmation of the orders it’s picked up.
It does this all from one URL, so I’m writing a script to check if there is any xml to parse, otherwise output an xml report.
I’m trying to test this script by posting some xml to my EE template via CURL. I’ve got full the address of the template correct, but for some reason it’s returning a 400 error:
Bad Request Your browser sent a request that this server could not understand.
Invalid URI in request POST HTTP/1.0
Resource id #49
My test script looks like this (the XML formatting is ok, I’ve validated it)
<?php
$post_string = '<?xml version="1.0" encoding="UTF-8"?>
<Company>
<Customers>
<Customer>
<Id>1</Id>
<AccountReference>ABC</AccountReference>
</Customer>
<Customer>
<Id>2</Id>
<AccountReference>DEF</AccountReference>
</Customer>
</Customers>
<SalesOrders>
<SalesOrder>
<Id>3</Id>
<SalesOrderNumber>17</SalesOrderNumber>
</SalesOrder>
<SalesOrder>
<Id>17</Id>
<SalesOrderNumber>57</SalesOrderNumber>
</SalesOrder>
</SalesOrders>
<Invoices>
<Invoice>
<Id>1</Id>
<InvoiceNumber>56</InvoiceNumber>
</Invoice>
<Invoice>
<Id>2</Id>
<InvoiceNumber>57</InvoiceNumber>
</Invoice>
</Invoices>
<Transactions>
<Transaction>
<Id>1</Id>
<TransactionNumber>56</TransactionNumber>
</Transaction>
<Transaction>
<Id>2</Id>
<TransactionNumber>57</TransactionNumber>
</Transaction>
</Transactions>
</Company>';
$header = "POST HTTP/1.0 \r\n";
$header .= "Content-type: text/xml \r\n";
$header .= "Content-length: ".strlen($post_string)." \r\n";
$header .= "Content-transfer-encoding: text \r\n";
$header .= "Connection: close \r\n\r\n";
$header .= $post_string;
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,"http://www.mydomain.com/reports/download/");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_TIMEOUT, 4);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $header);
$data = curl_exec($ch);
echo $data . "\r\n";
if(curl_errno($ch)) {
print curl_error($ch);
}
else {
print($ch);
curl_close($ch);
}
?>This is posting to this template:
<?php
$xmlInput = file_get_contents("php://input");
// Get rid of attributes in <Company> tag as xml2array doesn't like them for some reason?
$xmlString = preg_replace('/<Company.*?>/', '<Company>', $xmlInput, 1);
// Check to see if xml has been posted to this script (Notification)
if (!empty($xmlString))
{
// Convert xml string to array
$xmlData = xml2array($xmlString);
if (!$xmlData)
{
die("Couldn't read XML data");
}
// Extract SalesOrder Nodes
$salesOrderNodes =& $xmlData['Company'][0]['SalesOrders'][0]['SalesOrder'];
// Loop through sales order nodes
foreach($salesOrderNodes as $node)
{
$orderID = $node['Id'];
$results = $DB->query("SELECT title FROM exp_titles WHERE entry_id = ".$orderID." AND status NOT IN ('Closed','Failed','Processing')");
$data = array('status' => 'Sent to accounts');
if ($results->num_rows > 0)
{
$sql = $this->EE->db->update_string('exp_titles', $data, 'entry_id = $orderID');
$this->EE->db->query($sql);
}
}
}
else
{
echo "{embed=reports/latest}";
}
?>I realise this is a very specific issue - I’m really wondering if it’s the EE URI that’s causing the problem. I can put the address into my browser fine, but when I use it in CURL it returns the error. I haven’t tried doing this before, so I might be doing something foolish 😊
[Mod Edit: Moved to the Development & Programming forum]
Hi Greg
I’ve tried not posting the headers and only posting the XML. I still get a 400 error, but the message is now
“The request line contained invalid characters following the protocol string.”
If I only declare the POST header and the xml, the error returns to the invalid URI message.
However, if I remove all of the actual postdata and allow the curl to execute without it, it returns the download template, so the URI must be fine?
Any ideas very welcome, I’m a bit stumped! 😊
Hi
Thanks for that .htaccess update - unfortunately it hasn’t made the problem go away :(
I’ve made some changes to my scripts in a bid to get it working. The test_script that posts the xml to the handler URI now looks like this:
<?php
$post_string = <<<XML
<Company>
<Customers>
<Customer>
<Id>1</Id>
<AccountReference>ABC</AccountReference>
</Customer>
<Customer>
<Id>2</Id>
<AccountReference>DEF</AccountReference>
</Customer>
</Customers>
<SalesOrders>
<SalesOrder>
<Id>3</Id>
<SalesOrderNumber>17</SalesOrderNumber>
</SalesOrder>
<SalesOrder>
<Id>17</Id>
<SalesOrderNumber>57</SalesOrderNumber>
</SalesOrder>
</SalesOrders>
<Invoices>
<Invoice>
<Id>1</Id>
<InvoiceNumber>56</InvoiceNumber>
</Invoice>
<Invoice>
<Id>2</Id>
<InvoiceNumber>57</InvoiceNumber>
</Invoice>
</Invoices>
<Transactions>
<Transaction>
<Id>1</Id>
<TransactionNumber>56</TransactionNumber>
</Transaction>
<Transaction>
<Id>2</Id>
<TransactionNumber>57</TransactionNumber>
</Transaction>
</Transactions>
</Company>
XML;
$xml = simplexml_load_string($post_string);
libxml_use_internal_errors(true);
$sxe = simplexml_load_string($post_string);
if (!$sxe) {
echo "Failed loading XML<br>";
foreach(libxml_get_errors() as $error) {
echo "<br>", $error->message;
}
libxml_clear_errors();
}
$headers = array(
"POST HTTP/1.0",
"Content-type: text/xml",
"Content-length: ".strlen($post_string),
"Content-transfer-encoding: text",
);
$uploadURL = "http://www.mydomain.com/reports/download";
$ch = curl_init(); /* Create a CURL handle. */
/* Set cURL options. */
curl_setopt($ch, CURLOPT_URL, $uploadURL);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FAILONERROR, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers );
curl_setopt($ch, CURLOPT_POSTFIELDS,$post_string);
$result = curl_exec($ch); /* Execute the HTTP request. */
$info = curl_getinfo($ch);
/*output anything that might be useful*/
print_r($result);
print_r($info);
if(curl_error($ch)) {
print curl_error($ch);
} else {
print($ch);
curl_close($ch);
}
?>This outputs the following (presumably from the print_r($info) line:
Array ( [url] => http://www.mydomain.com/reports/download [content_type] => text/html [http_code] => 200 [header_size] => 423 [request_size] => 972 [filetime] => -1 [ssl_verify_result] => 0 [redirect_count] => 0 [total_time] => 0.186161 [namelookup_time] => 0.049204 [connect_time] => 0.049373 [pretransfer_time] => 0.049453 [size_upload] => 0 [size_download] => 0 [speed_download] => 0 [speed_upload] => 0 [download_content_length] => 0 [upload_content_length] => 0 [starttransfer_time] => 0.186114 [redirect_time] => 0 ) Resource id #49I’m not sure what the Resource id #49 thing is, googling it doesn’t reveal very much. It doesn’t generate a 400 error any more, which is something.
Running out of characters in this post - continued in next post!
Second part of post!
The handler script (called ‘download’) now looks like this (new xml2array function)
<?php
function xml2array ($xml_data)
{
// parse the XML datastring
$xml_parser = xml_parser_create ();
xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, 0);
xml_parser_set_option($xml_parser, XML_OPTION_SKIP_WHITE, 1);
xml_parse_into_struct ($xml_parser, $xml_data, $vals, $index);
xml_parser_free ($xml_parser);
// convert the parsed data into a PHP datatype
$params = array();
$ptrs[0] = & $params;
foreach ($vals as $xml_elem) {
$level = $xml_elem['level'] - 1;
switch ($xml_elem['type']) {
case 'open':
$tag_or_id = (array_key_exists ('attributes', $xml_elem)) ? $xml_elem['attributes']['ID'] : $xml_elem['tag'];
$ptrs[$level][$tag_or_id][] = array ();
$ptrs[$level+1] = & $ptrs[$level][$tag_or_id][count($ptrs[$level][$tag_or_id])-1];
break;
case 'complete':
$ptrs[$level][$xml_elem['tag']] = (isset ($xml_elem['value'])) ? $xml_elem['value'] : '';
break;
}
}
return ($params);
}
$xmlInput = file_get_contents("php://input");
// Get rid of attributes in <Company> tag as xml2array doesn't like them for some reason?
$xmlString = preg_replace('/<Company.*?>/', '<Company>', $xmlInput, 1);
// Check to see if xml has been posted to this script (Notification)
if (!empty($xmlString))
{
// Convert xml string to array
$xmlData = xml2array($xmlString);
print_r($xmlData);
if (!$xmlData)
{
die("Couldn't read XML data");
}
// Extract SalesOrder Nodes
$salesOrderNodes =& $xmlData['Company'][0]['SalesOrders'][0]['SalesOrder'];
// Loop through sales order nodes
foreach($salesOrderNodes as $node)
{
$orderID = $node['Id'];
$results = $DB->query("SELECT title FROM exp_titles WHERE entry_id = ".$orderID." AND status NOT IN ('Closed','Failed','Processing')");
$data = array('status' => 'Sent to accounts');
if ($results->num_rows > 0)
{
echo "Updating record ".$orderID."<br>";
$sql = $this->EE->db->update_string('exp_titles', $data, 'entry_id = $orderID');
$this->EE->db->query($sql);
}
else {
echo "No records to update";
}
}
}
else
{
echo "{embed=reports/latest}";
}
?>Because the xml stuff happens via cURL I can’t see if the what the download/update script is doing (or not doing). I can see that no db updates are happening, but for all I know it’s falling over on the first line. I’ve tried POSTing an xml file to this URI instead of doing it via cURL but that seemed to open up a whole new raft of problems.
If anyone out there has managed to wade through all this and can offer me any ideas as to how to get this working I’ll be extremely gratefully, and might be able to save the what’s left of the hair that I haven’t torn out yet today…
Packet Tide owns and develops ExpressionEngine. © Packet Tide, All Rights Reserved.