php xpath problems - php

I'm doing a cURL POST and get the error response back, parse it into an array but having issues with xpath now.
// XML
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<errors xmlns="http://host/project">
<error code="30" description="[] is not a valid email address."/>
<error code="12" description="id[] does not exist."/>
<error code="3" description="account[] does not exist."/>
<error code="400" description="phone[] does not exist."/>
</errors>
// Function / Class
class parseXML
{
protected $xml;
public function __construct($xml) {
if(is_file($xml)) {
$this->xml = simplexml_load_file($xml);
} else {
$this->xml = simplexml_load_string($xml);
}
}
public function getErrorMessage() {
$in_arr = false;
$el = $this->xml->xpath("//#errors");
$returned_errors = count($el);
if($returned_errors > 0) {
foreach($el as $element) {
if(is_object($element) || is_array($element)) {
foreach($element as $item) {
$in_arr[] = $item;
}
}
}
} else {
return $returned_errors;
}
return $in_arr;
}
}
// Calling function
// $errorMessage is holding the XML value in an array index
// something like: $arr[3] = $xml;
$errMsg = new parseXML($arr[3]);
$errMsgArr = $errMsg->getErrorMessage();
What I would like is all the error code and description attribute values
EDIT:
OK this is print_r($this->xml,true);
SimpleXMLElement Object
(
[error] => Array
(
[0] => SimpleXMLElement Object
(
[#attributes] => Array
(
[code] => 30
[description] => [] is not a valid email address.
)
)
[1] => SimpleXMLElement Object
(
[#attributes] => Array
(
[code] => 12
[description] => Id[12345] does not exist.
)
)
[2] => SimpleXMLElement Object
(
[#attributes] => Array
(
[code] => 3
[description] => account[] does not exist.
)
)
[3] => SimpleXMLElement Object
(
[#attributes] => Array
(
[code] => 400
[description] => phone[] does not exist.
)
)
)
)
for the life of me I can't figure out why I can get the code and description, any thoughts?
EDIT #2
Okay so I guess I will break it down.
I'm using cURL to POST a request to one of our servers, I parse out the HTTP response headers and xml (if xml is returned). each line in the header/xml I explode into an array. so if there is an error I see an extra index to the array. I then do something like this.
$if_err_from_header = $http_return_response[10];
// I know that index 10 is where if any the error message in xml is (the one posted above).
after that I do this:
$errMsg = new parseXML($if_err_from_header);
$errMsgArr = $errMsg->getErrorMessage();
still I can not get the code and description from the attributes in error, what am I missing?
EDIT #3
Okay why does this work?
$in_arr = false;
// This returns all the code attributes
$el = $this->xml->xpath("//#code");
# if $el is false, nothing returned from xpath(), set to an empty array
$el = $el == false ? array() : $el;
foreach($el as $element) {
$in_arr[] = array("code" => $element["code"], "description" => $element["description"]);
}
return $in_arr;
EDIT #4:
Okay this gets that values I want but it's kinda a hack, would like to select specific elements but...
$el = $this->xml->xpath("//*");

Make sure you take the namespace into account:
$this->xml->registerXPathNamespace('n', 'http://host/project');
$el = $this->xml->xpath("/n:errors/n:error");
$returned_errors = count($el);
And example of accessing values for lower down..
foreach($el as $element) {
print "code: " . $element["code"] . "\n";
}

# in XPath is the attribute selector. You're trying to select the root element so it should be:
$el = $this->xml->xpath("/errors");
If you want to select all error elements, use
$el = $this->xml->xpath("/errors/error");
or
$el = $this->xml->xpath("//error");

Related

Getting object into array

I'm not so advanced in PHP, just basic I can understand. I was trying an API and it returns it as object, how do I convert it to array? I did searched through stackoverflow and found lots of solution, but none works for me. $array returns empty array. Here is my code:-
while (1) {
$line = '';
$w->pollMessage();
$msgs = $w->getMessages();
foreach ($msgs as $m) {
print_r($msgs);
$array = json_decode(json_encode($msgs), true);
print_r($array);
}
And here is the result I get print_r($msgs):
Array
(
[0] => ProtocolNode Object
(
[tag:ProtocolNode:private] => message
[attributeHash:ProtocolNode:private] => Array
(
[from] => amy
[type] => text
[id] => 3EB0E191F15B831D244E
[t] => 1462939886
[notify] => Nick
)
[children:ProtocolNode:private] => Array
(
[0] => ProtocolNode Object
(
[tag:ProtocolNode:private] => enc
[attributeHash:ProtocolNode:private] => Array
(
[v] => 2
[type] => msg
)
[children:ProtocolNode:private] =>
[data:ProtocolNode:private] => 3
)
[1] => ProtocolNode Object
(
[tag:ProtocolNode:private] => body
[attributeHash:ProtocolNode:private] =>
[children:ProtocolNode:private] =>
[data:ProtocolNode:private] => good
)
)
[data:ProtocolNode:private] =>
)
)
I don't see your problem. You already have an array and do not need to handle with json. If you don't like it as array, you can simply cast it to an object.
$myArray = (object) $myArray;
Requirements: Walk a multiway object tree and apply a callback to each node.
Working demonstration at eval.in
Explanation:
1) There are a lot of private properties and I am not sure 'casting to an array' will let get at them.
2) If you have the original class then you may want to usesome of the methods on it.
The approach I used is:
a) Provide a 'tree walk' that will visit all the nodes in the tree. I am not concerned about order of visiting. Just to visit then all.
b) Rather than provide specific code to process the node. I provide a 'callback' hook.
c) As I don't have the class I create a simple one that will give me easy access to the private properties.
The Tree Walk function:
function walkProtocolNodes(\ProtocolNode $currentNode, /* callable */ $processNode)
{
$processNode($currentNode);
foreach ($currentNode->children as $nextNode) {
walkProtocolNodes($nextNode, $processNode);
}
return;
}
Sample processNode function:
All it does is append the attributesHash to the output array (outputResults)...
$processNode = function (\ProtocolNode $node) use (&$outputResults) {
$outputResults[] = $node->attributeHash;
};
Run the tree walk:
walkProtocolNodes($node1, $processNode);
The 'protocolNode' class I used:
class ProtocolNode {
private $tag = 'message';
private $attributeHash = array();
private $children = array();
private $data = 'good';
public function __construct($attrs = array()) {
$this->attributeHash = $attrs;
}
public function addChild(\ProtocolNode $node) {
$this->children[] = $node;
}
public function __get($propertyName) {
return $this->$propertyName;
}
}
See the demonstration to see the output

SimpleXmlElement count

I'm trying to count the amount of children in a SimpleXmlElement. I've searched on StackOverflow but I can't seem to find anything;
$xml = simplexml_load_string($xml);
foreach($xml as $key => $field) {
if (count($field) == 0){
$field[0][0] = 'test';
}
}
Some of my XmlElement are empty. Yet count gives 0 on all the elements. The only way I've found to do what I want is this:
if ($field[0][0] == '')
I've tried using $field->count() as specified on http://php.net/manual/en/simplexmlelement.count.php, but no matter what is in $field, it always returns 0.
Isn't there a better way to do this?
Here is the format of the xml through print_r:
SimpleXMLElement Object
(
[firstName] => Test
[lastName] => Test2
[middleName] => SimpleXMLElement Object
(
)
)
You can use the count() function like this:
$elem = new SimpleXMLElement($xml);
$elem->count();
http://php.net/manual/en/simplexmlelement.count.php for reference.

Can I find selected options in a form using simplexml?

I'm able to find a select's options on a website using the following code:
$dom = new DOMDocument();
$dom->loadHTMLFile('http://webseven.com.au/carl/testpage.htm');
$xml = simplexml_import_dom($dom);
//print_r($xml);
$select = $xml->xpath('//table/tr/td/select');
print_r($select);
I get (as an example)
[0] => SimpleXMLElement Object
(
[#attributes] => Array
(
[name] => product_OnWeb
[tabindex] => 4
)
[option] => Array
(
[0] => Yes
[1] => No
)
)
But I cannot find a way to find which of those is selected. Can this be done with SimpleXML or is there another method?
You need to loop through all the options (using foreach ( $node->option ... )), and check for the selected attribute (using $node['selected']):
$dom = new DOMDocument();
$dom->loadHTMLFile('http://webseven.com.au/carl/testpage.htm');
$xml = simplexml_import_dom($dom);
$selects = $xml->xpath('//table/tr/td/select');
foreach ( $selects as $select_node )
{
echo $select_node['name'] . ': ';
foreach ( $select_node->option as $option_node )
{
if ( isset($option_node['selected']) )
{
echo $option_node['value'] . ' ';
}
}
echo "\n";
}
As an aside, you are likely to be led astray if you use print_r to debug SimpleXML, as it doesn't show you the true state of the object. I've written a simplexml_dump function which might be more useful.

How to parse this XML file with PHP

I need to parse the following XML file below:
https://www.performanceexchange.com/publisher/report/api/17adeb41da1343209a32e6790ee1a286/xml/report/stats?startDate=2012-07-01&endDate=2012-08-13
$xml = simplexml_load_file( urlencode($mediatrust_url) );
Which outputs:
SimpleXMLElement Object
(
[#attributes] => Array
(
[name] => StatsReport
)
)
So it seems that it just picks up the name of the first tag.
Try this:
foreach($xml->attributes() as $k => $v)
{
if($k == "name")
{
//do something
}
}
Also, try using loading the XML string like this:
$sxml = simplexml_load_string(#file_get_contents($url));

add to associative array

I have some xml that looks like this:
<?xml version="1.0"?>
<data>
<items>
<item><timestamp>2011-07-11T09:01:42Z</timestamp><title><![CDATA[ some data here ]]></title><link>http://twitter.com/aurl</link></item>
<item><timestamp>2011-05-11T09:01:42Z</timestamp><title><![CDATA[ some data here ]]></title><link>http://twitter.com/aurlhere</link></item>
</items>
and I'm trying to loop over it and add it to an array:
foreach($xml->items->item as $e) {
$feedData['timestamp'] = $e->timestamp;
$feedData['title'] = $e->title;
$feedData['link'] = $e->link;
$feedData['type'] = $e->type;
}
print_r($feedData);
The xml is there and if I put an echo in the foreach I get back 10 responses (whcih are how many items I have) but when I print the array out all I get is one line (which is the last entry in the xml doc).
Array ( [timestamp] => SimpleXMLElement Object ( [0] => 2011-07-08T08:05:19Z ) [title] => SimpleXMLElement Object ( ) [link] => SimpleXMLElement Object ( [0] => http://twitter.com.aurl ) [type] => SimpleXMLElement Object ( ) )
I suspoect I'm overwriting the array on each loop. But why ? It should be adding to the array. Sorry my php isn't great...hence what Is suspect is a bit of a stupid question.
$i = 0;
foreach($xml->items->item as $e) {
$feedData[$i]['timestamp'] = $e->timestamp;
$feedData[$i]['title'] = $e->title;
$feedData[$i]['link'] = $e->link;
$feedData[$i]['type'] = $e->type;
$i++;
}
print_r($feedData);
Well, there is only one $feedData array and you are always setting the value for some specific keys, e.g. $feedData['timestamp']. How do you think this should add to the array? Keys are unique, the same key cannot appear twice, so yes, you are just overwriting the values.
It seems you want an array of arrays:
$feedData = array();
foreach($xml->items->item as $e) {
$feedData[] = array(
'timestamp' => $e->timestamp;
'title' => $e->title;
'link' => $e->link;
'type' => $e->type;
);
}
Have a look at the array manual [docs] for more information about arrays.

Categories