foreach loop not working for simplexmlelement in php - php

I m accessing an api and reading the content with Simplexml element.But when i m using foreach loop for accessing the values for nodes.It gives me value for only first loop.not for others....
below is my code
<?php
include('connection.php');
header("Content-Type: text/xml;");
function httpGet($url)
{
$ch = curl_init();
curl_setopt($ch,CURLOPT_URL,$url);
curl_setopt($ch,CURLOPT_RETURNTRANSFER,true);
// curl_setopt($ch,CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'header1:value1',
'header2:value2'
));
$output=curl_exec($ch);
curl_close($ch);
return $output;
}
$data= httpGet("http://apiurl");
$xml = new SimpleXMLElement($data);
foreach ($xml->node1->node2->node3 as $info) {
echo $info->name, ' played by ', $info->actor, PHP_EOL;
}
?>
xml format which i m reading
<?xml version='1.0' standalone='yes'?>
<main>
<node1>
<title>PHP: Behind the Parser</title>
<node2>
<node3>
<name>Ms. Coder</name>
<actor>Onlivia Actora</actor>
</node3>
</node2>
<node2>
<node3>
<name>Mr. Coder</name>
<actor>El ActÓr</actor>
</node3>
</node2>
</node1>
</main>
When i m running above php code for reading api content which is having xml format as shown above.
I want to read name and actor node using foreach loop.but i m not able to access only first name and actor node under node3,not the second one....
Please correct the foreach loop or any flaw in code..Please highlight and help me in correcting that....

maybe set it like this
foreach ($xml->node1->node2 as $info) {
echo $info->node3->name, ' played by ', $info->node3->actor, PHP_EOL;
}

Related

remove soap headers from response before turning into simplexml object

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

php process xml into array

I'm tring to process xml (website) into php array.
I have tried the following code which works for everyting in results but i need to get the totalpage which i'm not able to see how I can do this.
function get_content($url)
/// basically opens the page and stores it as a variable. Buggered if I know how it works!
{
$ch = curl_init();
curl_setopt ($ch, CURLOPT_URL, $url);
curl_setopt ($ch, CURLOPT_HEADER, 0);
ob_start();
curl_exec ($ch);
curl_close ($ch);
$string = ob_get_contents();
ob_end_clean();
return $string;
$string = NULL;
$ch = NULL;
$url = NULL;
}
$url = "url";
$content = get_content($url);
$content_x = explode("<result>", $content);
foreach ($content_x as $item)
{
$p1 = strpos($item, '<title>');
$p2 = strpos($item, '</title>');
$l1 = $p2 - $p1;
echo '<br>'.$title = substr($item, $p1, $l1);
}
xml site feed
<?xml version="1.0" encoding="UTF-8"?>
<response version="2">
<totalpage>1005</totalpage>
<results>
<result>
<title>test</title>
<title2>test2</title2>
<title3>test3</title3>
<result>
<result>
<title>test</title>
<title2>test2</title2>
<title3>test3</title3>
<result>
<result>
<title>test</title>
<title2>test2</title2>
<title3>test3</title3>
<result>
........so on
<results>
</response>
I need to get totalpage and everyting in results
How do get totalpage and is they better way to process the results
You absolutely should not be using string manipulation to try to parse XML. There are any number of PHP libraries that can do this. I might recommend SimpleXML.
Usage would be:
$xml = simplexml_load_string($content);
$totalpage = $xml->response->totalpage;

get Contents of the child tag using simeplxml_load_string in php

I have this XML
<parent>
<sms>
<response>
<message>message</message>
<reponse>text<response>
</response>
</sms>
<sms>
<response>
<message>message</message>
<reponse>text2<response>
</response>
</sms>
</parent>
I want to get the whole contents of response tag i.e <reponse1>text<response1> , <reponse2>text<response2> .which i will strore in an array. what i tried id
function xmlSplitUpResponseXML($xmlvalue)
{
$returnSplit = array();
$r = 0;
if(simplexml_load_string($xmlvalue))
{
$xml = simplexml_load_string($xmlvalue);
foreach($xml->children() as $child)
{
if(strtolower($child->getName())=='sms')
{
foreach ($child as $fields):
if(strtolower($fields->getName())=='response')
{
echo $fields;
}
}
} }
}
But the text is not echoed.
how can i do this.
Your XML isn't completly correct, but when fixed - try this:
echo $fields->message."<br>";
echo $fields->response."<hr>";
Works on your given example, with this fixed xml:
$string = "<parent><sms><response><message>message</message><response>text</response></response></sms><sms><response><message>message</message><response>text2</response></response></sms></parent>";
Hmm, seems like you need some XPath to the rescue...
Given the following fixed-up XML (note nested response nodes)
<parent>
<sms>
<response>
<message>message</message>
<response>text</response>
</response>
</sms>
<sms>
<response>
<message>message</message>
<response>text2</response>
</response>
</sms>
</parent>
The following PHP will do it for you.
<?php
$xml = simplexml_load_file( "sms.xml" );
// Find the sms response (note responses are nested in a parent response for some reason
$result = $xml->xpath('/parent/sms/response/response');
while(list( , $node) = each($result)) {
// to just output the value of the node
// echo $node;
// to wrap value in tags *bad idea*
echo $node->asXML();
}
?>
Not sure why you'd want to return an XML string, seems like a bad idea to me. What are you
doing with the output? Probably better to construct an XML document properly, just guessing...

xml parsing problems php

I have a problem.
I have 2 php scripts, one which sends xml data using curl and one which is supposed to read the posted data.
The problem is the reciever script is not getting any of the elements in the xml.
Any help would be appriciated.
SENDER SCRIPT:
<?php
$xml = '
<SMS xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://api.xxxxxxxxxxxxxxx.co.uk/send/xxxxxxxxxxxxxxx.xsd">
<auth>
<user>yourusername</user>
<pass>yourpassword</pass>
</auth>
<originator>your_sender_name</originator>
<messages>
<msg id="1" gsm="440000000000">
<text>Please come for you appointment tomorrow morning at 12:45</text>
</msg>
<msg id="1" gsm="440000000000">
<text>Please come for you appointment tomorrow morning at 14:00</text>
</msg>
</messages>
</SMS>';
function sendMessages($xml) {
$curl = curl_init();
//$url = "https://sapi.xxxxxxxxxxxxxxx.co.uk/send/xml.php";
$url = "http://api.xxxxxxxxxxxxxxx.co.uk/send/xml.php";
$options = array(CURLOPT_URL => $url, CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_POSTFIELDS => $xml);
curl_setopt_array($curl, $options);
$response = curl_exec($curl);
curl_close($curl);
return $response;
}
echo sendMessages($xml);
//echo $xml;
?>
RECIEVER SCRIPT:
<?php
function logResult() {
$postdata = file_get_contents("php://input");
$dom = new DOMDocument();
$dom->loadXML($postdata);
$xp = new domxpath($dom);
$messages = $xp->query("//SMS/auth");
//var_dump($messages);
foreach ($messages as $node) {
//var_dump($node);
return $node->getAttribute('user');
//$node->getAttribute('sentdate');
//$node->getAttribute('donedate');
}
}
echo logResult();
?>
I was unable to get DOMDocument to even load this XML string, with or without the namespace. I have no idea why.
I'd suggest using SimpleXMLElement. I use it a lot, and it works great. I was also able to get it to parse your XML string.

PHP and XML parsing

I'm struggling to parse an XML file in PHP:
Here's the XML
<rss xmlns:ac="http://palm.com/app.catalog.rss.extensions" version="2.0">
<channel>
<title>Device App Updates for US</title>
<link>http://www.palm.com</link>
<description>Updates</description>
<language>en-US</language>
<pubDate>Mon, 04 Jan 2010 18:23:51 -0800</pubDate>
<lastBuildDate>Mon, 04 Jan 2010 18:23:51 -0800</lastBuildDate>
<ac:distributionChannel>Device</ac:distributionChannel>
<ac:countryCode>US</ac:countryCode>
<item>
<title><![CDATA[My App]]></title>
<link>http://developer.palm.com/appredirect/?packageid=com.palm.myapp</link>
<description><![CDATA[My fun app.]]></description>
<pubDate>2009-12-21 21:00:58</pubDate>
<guid>334.232</guid>
<ac:total_downloads>1234</ac:total_downloads>
<ac:total_comments>12</ac:total_comments>
<ac:country>US</ac:country>
</item>
</channel>
</rss>
My Problem is; when I use:
$strURL = "http://developer.palm.com/rss/D/appcatalog.update.rss.xml";
$ch = curl_init($strURL);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, 0);
$data = curl_exec($ch);
curl_close($ch);
$doc = new SimpleXmlElement($data, LIBXML_NOCDATA);
print_r($doc);
I'm not able to display the values? The one I'm most interested in is <ac:total_downloads>1000</ac> but I don't seem to be able to parse it.
What am I doing wrong?
Many thanks
You don't need to use curl to retrieve that file, SimpleXML can fetch external resources.
Use children() to access namespaced nodes. Here's how to do it:
$rss = simplexml_load_file($strURL);
$ns = 'http://palm.com/app.catalog.rss.extensions';
foreach ($rss->channel->item as $item)
{
echo 'Title: ', $item->title, "\n";
echo 'Downloads: ', $item->children($ns)->total_downloads, "\n\n";
}

Categories