PHP/SimpleXML - Arrays generated differently for single child and multiple children - php

I'm using SimpleXML to parse an XML feed of property listings from different realtors. The relevant section of the XML feed looks something like this:
<branch name="Trustee Realtors">
<properties>
<property>
<reference>1</reference>
<price>275000</price>
<bedrooms>3</bedrooms>
</property>
<property>
<reference>2</reference>
<price>350000</price>
<bedrooms>4</bedrooms>
</property>
<property>
<reference>3</reference>
<price>128500</price>
<bedrooms>4</bedrooms>
</property>
</properties>
</branch>
<branch name="Quick-E-Realty Inc">
<properties>
<property>
<reference>4</reference>
<price>180995</price>
<bedrooms>3</bedrooms>
</property>
</properties>
</branch>
and is then converted to an array like this:
$xml = file_get_contents($filename);
$xml = simplexml_load_string($xml);
$xml_array = json_decode(json_encode((array) $xml), 1);
$xml_array = array($xml->getName() => $xml_array);
The issue I'm having is that when the array is created the data for the single listing is in a different position in the array to the multiple listings - I'm not sure exactly how to explain this, but if I var_dump() the array for the multiple items it looks like this:
array(3) {
[0]=>
array(3) {
["reference"]=>
string(4) "0001"
["price"]=>
string(6) "275000"
["bedrooms"]=>
int(3)
}
[1]=>
array(3) {
["reference"]=>
string(4) "0002"
["price"]=>
string(6) "350000"
["bedrooms"]=>
int(4)
}
[2]=>
array(3) {
["reference"]=>
string(4) "0003"
["price"]=>
string(6) "128500"
["bedrooms"]=>
int(2)
}
}
If I var_dump() the array for the single listing it looks like this:
array(3) {
["reference"]=>
string(4) "0004"
["price"]=>
string(6) "180995"
["bedrooms"]=>
int(3)
}
But what I need it to look like is this:
array(1) {
[0]=>
array(3) {
["reference"]=>
string(4) "0004"
["price"]=>
string(6) "180995"
["bedrooms"]=>
int(3)
}
}
Each of these arrays represents the property listings from a single realtor. I'm not sure whether this is just the way that SimpleXML or the json functions work but what I need is for the same format to be used (the array containing the property listing to be the value of the [0] key).
Thanks in advance!

SimpleXML is quirky like this. I used it recently trying to make configuration files "easier" to write up and found out in the process that SimpleXML doesn't always act consistent. In this case I think you will benefit from simply detecting if a <property> is the only one in a set, and if so, then wrap it in an array by itself and then send it to your loop.
NOTE: ['root'] is there because I needed to wrap a '<root></root>' element around your XML to make my test work.
//Rebuild the properties listings
$rebuild = array();
foreach($xml_array['root']['branch'] as $key => $branch) {
$branchName = $branch['#attributes']['name'];
//Check to see if 'properties' is only one, if it
//is then wrap it in an array of its own.
if(is_array($branch['properties']['property']) && !isset($branch['properties']['property'][0])) {
//Only one propery found, wrap it in an array
$rebuild[$branchName] = array($branch['properties']['property']);
} else {
//Multiple properties found
$rebuild[$branchName] = $branch['properties']['property'];
}
}
That takes care of rebuilding your properties. It feels a little hackish. But basically you are detecting for the lack of a multi-dimensional array here:
if(is_array($branch['properties']['property']) && !isset($branch['properties']['property'][0]))
If you don't find a multi-dimensional array then you explicitly make one of the single <property>. Then to test that everything was rebuilt correctly you can use this code:
//Now do your operation...whatever it is.
foreach($rebuild as $branch => $properties) {
print("Listings for $branch:\n");
foreach($properties as $property) {
print("Reference of " . $property['reference'] . " sells at $" . $property['price'] . " for " . $property['bedrooms'] . " bedrooms.\n");
}
print("\n");
}
This produces the following output:
Listings for Trustee Realtors:
Reference of 1 sells at $275000 for 3 bedrooms.
Reference of 2 sells at $350000 for 4 bedrooms.
Reference of 3 sells at $128500 for 4 bedrooms.
Listings for Quick-E-Realty Inc:
Reference of 4 sells at $180995 for 3 bedrooms.
And a dump of the rebuild will produce:
Array
(
[Trustee Realtors] => Array
(
[0] => Array
(
[reference] => 1
[price] => 275000
[bedrooms] => 3
)
[1] => Array
(
[reference] => 2
[price] => 350000
[bedrooms] => 4
)
[2] => Array
(
[reference] => 3
[price] => 128500
[bedrooms] => 4
)
)
[Quick-E-Realty Inc] => Array
(
[0] => Array
(
[reference] => 4
[price] => 180995
[bedrooms] => 3
)
)
)
I hope that helps you out getting closer to a solution to your problem.

The big massive "think outside the box" question to ask yourself here is: why are you converting the SimpleXML object to an array in the first place?
SimpleXML is not just a library for parsing XML and then using something else to manipulate it, it's designed for exactly the kind of thing you're about to do with that array.
In fact, this problem of sometimes having single elements and sometimes multiple is one of the big advantages it has over a plain array representation: for nodes that you know will be single, you can leave off the [0]; but for nodes you know might be multiple, you can use [0], or a foreach loop, and that will work too.
Here are some examples of why SimpleXML lives up to its name with your XML:
$sxml = simplexml_load_string($xml);
// Looping over multiple nodes with the same name
// We could also use $sxml->children() to loop regardless of name
// or even the shorthand foreach ( $sxml as $children )
foreach ( $sxml->branch as $branch ) {
// Access an attribute using array index notation
// the (string) is optional here, but good habit to avoid
// passing around SimpleXML objects by mistake
echo 'The branch name is: ' . (string)$branch['name'] . "\n";
// We know there is only one <properties> node, so we can take a shortcut:
// $branch->properties means the same as $branch->properties[0]
// We don't know if there are 1 or many <property> nodes, but it
// doesn't matter: we're asking to loop over them, so SimpleXML
// knows what we mean
foreach ( $branch->properties->property as $property ) {
echo 'The property reference is ' . (string)$property->reference . "\n";
}
}
Basically, whenever I see that ugly json_decode(json_encode( trick, I cringe a little, because 99 times out of 100 the code that follows is much uglier than just using SimpleXML.

One possibility is reading the XML with DOM+XPath. XML can not just be converted to JSON, but building a specific JSON for a specific XML is easy:
$dom = new DOMDocument();
$dom->loadXml($xml);
$xpath = new DOMXPath($dom);
$result = [];
foreach ($xpath->evaluate('//branch') as $branchNode) {
$properties = [];
foreach ($xpath->evaluate('properties/property', $branchNode) as $propertyNode) {
$properties[] = [
'reference' => $xpath->evaluate('string(reference)', $propertyNode),
'price' => (int)$xpath->evaluate('string(price)', $propertyNode),
'bedrooms' => (int)$xpath->evaluate('string(bedrooms)', $propertyNode)
];
}
$result[] = [
'name' => $xpath->evaluate('string(#name)', $branchNode),
'properties' => $properties
];
}
echo json_encode($result, JSON_PRETTY_PRINT);
Output: https://eval.in/154352
[
{
"name": "Trustee Realtors",
"properties": [
{
"reference": "1",
"price": 275000,
"bedrooms": 3
},
{
"reference": "2",
"price": 350000,
"bedrooms": 4
},
{
"reference": "3",
"price": 128500,
"bedrooms": 4
}
]
},
{
"name": "Quick-E-Realty Inc",
"properties": [
{
"reference": "4",
"price": 180995,
"bedrooms": 3
}
]
}

Use the SimpleXMLElement Class:
<?php
$xml = "<body>
<item>
<id>2</id>
</item>
</body>";
$elem = new SimpleXMLElement($xml);
if($elem->children()->count() === 1){
$id = $elem->item->addChild(0)->addChild('id',$elem->item->id);
unset($elem->item->id);
};
$array = json_decode(json_encode($elem), true);
print_r($array);
Output:
Array
(
[item] => Array
(
[0] => Array
(
[id] => 2
)
)
)

did you use this:
$xml_array['branch']['properties']['property']
as loop source? try to use this:
$xml_array['branch']['properties']
don't use ['property'] at the end of the line, don't use 3 segment just use 2 segment
<?php
$xml = file_get_contents('simple.xml');
$xml = simplexml_load_string($xml);
$xml_array = json_decode(json_encode((array) $xml), 1);
$xml_array = array($xml->getName() => $xml_array);
print_r($xml_array);
foreach($xml_array['branch']['properties'] as $a){
print_r($a);
}
?>

In order to solve this problem, you should select using xpath (as other mention), but in my opinion this is not a very familiar tool to most web-developers. I created a very small composer enabled package, which solves this problem. Credit to the symfony package CssSelector (https://symfony.com/doc/current/components/css_selector.html) which rewrites CSS selectors to xpath selectors. My package is just a thin wrapper that actually deals with what you in the most common cases will do with XML using PHP. You can find it here: https://github.com/diversen/simple-query-selector
use diversen\querySelector;
// Load simple XML document
$xml = simplexml_load_file('test2.xml');
// Get all branches as DOM elements
$elems = querySelector::getElementsAsDOM($xml, 'branch');
foreach($elems as $elem) {
// Get attribute name
echo $elem->attributes()->name . "\n";
// Get properties as array
$props = querySelector::getElementsAsAry($elem, 'property');
print_r($props); // You will get the array structure you expect
}
You could also (if you don't care about the branch name) just do:
$elems = querySelector::getElementsAsAry($xml, 'property');

Testing if the parsed XML has multiple tags, or is a single tag converted to array, instead of rebuilding the array, you could just test for the following case:
<?php
if (is_array($info[0])) {
foreach ($info as $fields) {
// Do something...
}
} else {
// Do something else...
}

Try it=)
$xml = simplexml_load_string($xml_raw, "SimpleXMLElement", LIBXML_NOCDATA);
$json = json_encode($xml);
$array = json_decode($json, TRUE);
$marray['RepairSheets']['RepairSheet'][0] = $array['RepairSheets']['RepairSheet'];
$array = (isset($array['RepairSheets']['RepairSheet'][0]) == true) ? $array : $marray;

Related

How to locate an array in a php object with no key

I have a database of zip codes from The Zip Code Database Project that came as a .csv file. I converted it to json and it has no key for each array, they look like this:
[
{
"zipcode": 35004,
"state abbreviation": "AL",
"latitude": 33.606379,
"longitude": -86.50249,
"city": "Moody",
"state": "Alabama"
}
]
I have figured out how to search all of the arrays and locate a matching zip code but I can't figure out how to make that search return data about the array that it found it in.
I can search like this:
$filename = './assets/data/zips.json';
$data = file_get_contents($filename);
$zipCodes = json_decode($data);
$inputZip = 55555;
foreach ($zipCodes as $location) {
if ($inputZip == $location->zipcode) {
echo "Success";
}
}
Can anyone tell me how I can use this search (or a better idea) to store the latitude and longitude associated with the searched zip code to two variables? I have wasted nearly my whole weekend on this so any and all help is greatly appreciated.
Also, I have no issue using the raw csv file to perform this function, I just couldn't get as far as I did with json.
Thanks everyone!
To find out the index of an array by a property of the array items, use array_column to get just the values of that column/property, then use array_search to find the index.
<?php
$inputZip = 35004;
$index = array_search($inputZip, array_column($zipCodes, 'zipcode')); // 0
print_r($zipCodes[$index]); // the entire object
https://3v4l.org/TAXQq
Based on your code:
foreach ($zipCodes as $index => $location) { // index is a key
if ($inputZip == $location->zipcode) {
echo "Index is ".$index;
break;
}
}
var_dump($zipCodes[$index]);
I should note that it seems you are doing something wrong because you don`t suppose to store your data like this and loop through array all the time.
To get the keys of an array that is filtered due to certain parameters that must be met you could use array_keys and array_filter.
For Example
$array = [1,2,1,1,1,2,2,1,2,1];
$out = array_filter($array,function($v) {
return $v == 2;
});
print_r(array_keys($out));
Output
Array
(
[0] => 1
[1] => 5
[2] => 6
[3] => 8
)
Try the above example in PHP Sandbox
To match your actual data structure.
$json = '[{"zipcode": 35004,"state abbreviation": "AL","latitude": 33.606379,"longitude": -86.50249,"city": "Moody","state": "Alabama"},{"zipcode": 35004,"state abbreviation": "AL","latitude": 33.606379,"longitude": -86.50249,"city": "Moody","state": "Alabama"},{"zipcode": 35005,"state abbreviation": "AL","latitude": 33.606579,"longitude": -86.50649,"city": "Moody","state": "Alabama"}]';
$array = json_decode($json);
$out = array_filter($array, function($v) use ($inputZip) {
return $v->zipcode == $inputZip; // for the below output $inputZip=35004
});
print_r(array_keys($out));
Output
Array
(
[0] => 0
[1] => 1
)
Try the example in PHP Sandbox

Resolve namespaces with SimpleXML regardless of structure or namespace

I got a Google Shopping feed like this (extract):
<?xml version="1.0" encoding="utf-8" ?>
<rss version="2.0" xmlns:g="http://base.google.com/ns/1.0">
...
<g:id><![CDATA[Blah]]></g:id>
<title><![CDATA[Blah]]></title>
<description><![CDATA[Blah]]></description>
<g:product_type><![CDATA[Blah]]></g:product_type>
Now, SimpleXML can read the "title" and "description" tags but it can't read the tags with "g:" prefix.
There are solutions on stackoverflow for this specific case, using the "children" function.
But I don't only want to read Google Shopping XMLs, I need it to be undependend from structure or namespace, I don't know anything about the file (I recursively loop through the nodes as an multidimensional array).
Is there a way to do it with SimpleXML? I could replace the colons, but I want to be able to store the array and reassemble the XML (in this case specifically for Google Shopping) so I do not want to lose information.
You want to use SimpleXMLElement to extract data from XML and convert it into an array.
This is generally possible but comes with some caveats. Before XML Namespaces your XML comes with CDATA. For XML to array conversion with Simplexml you need to convert CDATA to text when you load the XML string. This is done with the LIBXML_NOCDATA flag. Example:
$xml = simplexml_load_string($buffer, null, LIBXML_NOCDATA);
print_r($xml); // print_r shows how SimpleXMLElement does array conversion
This gives you the following output:
SimpleXMLElement Object
(
[#attributes] => Array
(
[version] => 2.0
)
[title] => Blah
[description] => Blah
)
As you can already see, there is no nice form to present the attributes in an array, therefore Simplexml by convention puts these into the #attributes key.
The other problem you have is to handle those multiple XML namespaces. In the previous example no specific namespace was used. That is the default namespace. When you convert a SimpleXMLElement to an array, the namespace of the SimpleXMLElement is used. As none was explicitly specified, the default namespace has been taken.
But if you specify a namespace when you create the array, that namespace is taken.
Example:
$xml = simplexml_load_string($buffer, null, LIBXML_NOCDATA, "http://base.google.com/ns/1.0");
print_r($xml);
This gives you the following output:
SimpleXMLElement Object
(
[id] => Blah
[product_type] => Blah
)
As you can see, this time the namespace that has been specified when the SimpleXMLElement was created is used in the array conversion: http://base.google.com/ns/1.0.
As you write you want to take all namespaces from the document into account, you need to obtain those first - including the default one:
$xml = simplexml_load_string($buffer, null, LIBXML_NOCDATA);
$namespaces = [null] + $xml->getDocNamespaces(true);
Then you can iterate over all namespaces and recursively merge them into the same array shown below:
$array = [];
foreach ($namespaces as $namespace) {
$xml = simplexml_load_string($buffer, null, LIBXML_NOCDATA, $namespace);
$array = array_merge_recursive($array, (array) $xml);
}
print_r($array);
This then finally should create and output the array of your choice:
Array
(
[#attributes] => Array
(
[version] => 2.0
)
[title] => Blah
[description] => Blah
[id] => Blah
[product_type] => Blah
)
As you can see, this is perfectly possible with SimpleXMLElement. However it's important you understand how SimpleXMLElement converts into an array (or serializes to JSON which does follow the same rules). To simulate the SimpleXMLElement-to-array conversion, you can make use of print_r for a quick output.
Note that not all XML constructs can be equally well converted into an array. That's not specifically a limitation of Simplexml but lies in the nature of which structures XML can represent and which structures an array can represent.
Therefore it is most often better to keep the XML inside an object like SimpleXMLElement (or DOMDocument) to access and deal with the data - and not with an array.
However it's perfectly fine to convert data into an array as long as you know what you do and you don't need to write much code to access members deeper down the tree in the structure. Otherwise SimpleXMLElement is to be favored over an array because it allows dedicated access not only to many of the XML feature but also querying like a database with the SimpleXMLElement::xpath method. You would need to write many lines of own code to access data inside the XML tree that comfortable on an array.
To get the best of both worlds, you can extend SimpleXMLElement for your specific conversion needs:
$buffer = <<<BUFFER
<?xml version="1.0" encoding="utf-8" ?>
<rss version="2.0" xmlns:g="http://base.google.com/ns/1.0">
...
<g:id><![CDATA[Blah]]></g:id>
<title><![CDATA[Blah]]></title>
<description><![CDATA[Blah]]></description>
<g:product_type><![CDATA[Blah]]></g:product_type>
</rss>
BUFFER;
$feed = new Feed($buffer, LIBXML_NOCDATA);
print_r($feed->toArray());
Which does output:
Array
(
[#attributes] => stdClass Object
(
[version] => 2.0
)
[title] => Blah
[description] => Blah
[id] => Blah
[product_type] => Blah
[#text] => ...
)
For the underlying implementation:
class Feed extends SimpleXMLElement implements JsonSerializable
{
public function jsonSerialize()
{
$array = array();
// json encode attributes if any.
if ($attributes = $this->attributes()) {
$array['#attributes'] = iterator_to_array($attributes);
}
$namespaces = [null] + $this->getDocNamespaces(true);
// json encode child elements if any. group on duplicate names as an array.
foreach ($namespaces as $namespace) {
foreach ($this->children($namespace) as $name => $element) {
if (isset($array[$name])) {
if (!is_array($array[$name])) {
$array[$name] = [$array[$name]];
}
$array[$name][] = $element;
} else {
$array[$name] = $element;
}
}
}
// json encode non-whitespace element simplexml text values.
$text = trim($this);
if (strlen($text)) {
if ($array) {
$array['#text'] = $text;
} else {
$array = $text;
}
}
// return empty elements as NULL (self-closing or empty tags)
if (!$array) {
$array = NULL;
}
return $array;
}
public function toArray() {
return (array) json_decode(json_encode($this));
}
}
Which is an adoption with namespaces of the Changing JSON Encoding Rules example given in SimpleXML and JSON Encode in PHP – Part III and End.
The answer given by hakre was well written and exactly what I was looking for, especially the Feed class he provided at the end. But it was incomplete in a couple of ways, so I modified his class to be more generically useful and wanted to share the changes:
One of the most important issues which was missed in the original is that Attributes may also have namespaces, and without taking that into account, you are quite likely to miss attributes on elements.
The other bit which is important is that when converting to an array, if you have something which may contain elements of the same name but different namespaces, there is no way to tell which namespace the element was from. (Yes, it's a really rare situation... but I ran into it with a government standard based on NIEM...) So I added a static option which will cause the namespace prefix to be added to all keys in the final array that belong to a namespace. To use it, set
Feed::$withPrefix = true; before calling toArray()
Finally, more for my own preferences, I added an option to toArray() to return the final array as associative instead of using objects.
Here's the updated class:
class Feed extends \SimpleXMLElement implements \JsonSerializable
{
public static $withPrefix = false;
public function jsonSerialize()
{
$array = array();
$attributes = array();
$namespaces = [null] + $this->getDocNamespaces(true);
// json encode child elements if any. group on duplicate names as an array.
foreach ($namespaces as $prefix => $namespace) {
foreach ($this->attributes($namespace) as $name => $attribute) {
if (static::$withPrefix && !empty($namespace)) {
$name = $prefix . ":" . $name;
}
$attributes[$name] = $attribute;
}
foreach ($this->children($namespace) as $name => $element) {
if (static::$withPrefix && !empty($namespace)) {
$name = $prefix . ":" . $name;
}
if (isset($array[$name])) {
if (!is_array($array[$name])) {
$array[$name] = [$array[$name]];
}
$array[$name][] = $element;
} else {
$array[$name] = $element;
}
}
}
if (!empty($attributes)) {
$array['#attributes'] = $attributes;
}
// json encode non-whitespace element simplexml text values.
$text = trim($this);
if (strlen($text)) {
if ($array) {
$array['#text'] = $text;
} else {
$array = $text;
}
}
// return empty elements as NULL (self-closing or empty tags)
if (!$array) {
$array = NULL;
}
return $array;
}
public function toArray($assoc=false) {
return (array) json_decode(json_encode($this), $assoc);
}
}

Parse Xml file for comparison

ok this is driving me crazy.
I have been trying to parse a xml file into a specific array or object so I can compare it to a similar file to test for differences.
However I have had no luck. I have been attempting to use SimpleXMLIterator and SimpleXMLElement to do this.
Here are some samples:
<xml>
//This is the first record of 1073
<viddb>
<movies>1074</movies>
<movie>
<title>10.5</title>
<origtitle>10.5</origtitle>
<year>2004</year>
<genre>Disaster</genre>
<release></release>
<mpaa></mpaa>
<director>John Lafia</director>
<producers>Howard Braunstein, Jeffrey Herd</producers>
<actors>Kim Delaney, Fred Ward, Ivan Sergei</actors>
<description>An earthquake reaching a 10.5 magnitude on the Richter scale, strikes the west coast of the U.S. and Canada. A large portion of land falls into the ocean, and the situation is worsened by aftershocks and tsunami.</description>
<path>E:\www\Media\Videos\Disaster\10.5.mp4</path>
<length>164</length>
<size>3648</size>
<resolution>640x272</resolution>
<framerate>29.97</framerate>
<videocodec>AVC</videocodec>
<videobitrate>2966</videobitrate>
<label>Roku Media</label>
<poster>images/10.5.jpg</poster>
</movie>
Here is the object this record produces using $iter = new SimpleXMLIterator($xml, 0, TRUE);
object(SimpleXMLIterator)#71 (1) {
["viddb"] => object(SimpleXMLIterator)#72 (2) {
["movies"] => string(4) "1074"
["movie"] => array(1074) {
[0] => object(SimpleXMLIterator)#73 (19) {
["title"] => string(4) "10.5"
["origtitle"] => string(4) "10.5"
["year"] => string(4) "2004"
["genre"] => string(8) "Disaster"
["release"] => object(SimpleXMLIterator)#1158 (0) {
}
["mpaa"] => object(SimpleXMLIterator)#1159 (0) {
}
["director"] => string(10) "John Lafia"
["producers"] => string(31) "Howard Braunstein, Jeffrey Herd"
["actors"] => string(35) "Kim Delaney, Fred Ward, Ivan Sergei"
["description"] => string(212) "An earthquake reaching a 10.5 magnitude on the Richter scale, strikes the west coast of the U.S. and Canada. A large portion of land falls into the ocean, and the situation is worsened by aftershocks and tsunami."
["path"] => string(37) "E:\www\Media\Videos\Disaster\10.5.mp4"
["length"] => string(3) "164"
["size"] => string(4) "3648"
["resolution"] => string(7) "640x272"
["framerate"] => string(5) "29.97"
["videocodec"] => string(3) "AVC"
["videobitrate"] => string(4) "2966"
["label"] => string(10) "Roku Media"
["poster"] => string(15) "images/10.5.jpg"
}
What I'm trying to produce (at the moment) is a single level associative array for each movie . All the examples I've read on and followed always produced an array of arrays, which is much more difficult to work with.
This is were i'm at :
$iter = new SimpleXMLIterator($xml, 0, TRUE);
Zend_Debug::dump($iter);
//so far xpath has not worked for me, I can't get $result to return anything
$result = $iter->xpath('/xml/viddb/movies/movie');
$movies = array();
for ($iter->rewind(); $iter->valid(); $iter->next()) {
foreach ($iter->getChildren() as $key => $value) {
//I can get each movie title to echo but when I try to put them into an
// array it only has the last record
echo $value->title . '<br />';
$movies['title'] = $value->title;
}
}
return $movies;
I feel like I'm missing something simple and obvious...as usual :)
[EDIT]
I found my error, I was tripping over the array of objects thing. I had to cast the data I wanted as a string to make it work how I wanted. Just for info here is what I came up with to put me on the track I wanted:
public function indexAction() {
$xml = APPLICATION_PATH . '/../data/Videos.xml';
$iter = new SimpleXMLElement($xml, 0, TRUE);
$result = $iter->xpath('//movie');
$movies = array();
foreach ($result as $key => $movie) {
$movies[$key + 1] = (string) $movie->title;
}
Zend_Debug::dump($movies, 'Movies');
}
XPATH is the answer you are looking for. I think the reason your XPATH isn't working is because you are looking for a movie node under the movies node when the movies node does not have any children.
Edit: Think it might be easier to just use a foreach loop instead of the iterator. I had to look up the iterator as I had never seen it before. Been using simplxml and xpath for a while too. Also, I believe you should only use SimpleXMLElement if you are planning on editing the XML as well. If you simply want to read it for comparison, best to use simplexml_load_file. You can also change your xpath to simply.
xpath('//movie');
If you just need to compare the entire file contents, read the contents of both files into a string and do a string comparison. Otherwise, you can do the same at a lower level of the document by getting the innerXML of any node.

What is the SimpleXML version of this DOMDocument XML?

I understand that SimpleXML is far more efficient than DOMDocument. Any advice on how I would reform the below into a SimpleXML version?
<?php
$doc = new DOMDocument();
$doc->load( 'feedpage.xml' );
$Main = $doc->getElementsByTagName( "varOne" );
foreach( $Main as $varOne )
{
$VarTwo = $varOne->getElementsByTagName( "VarTwo" );
$VarTwo = $VarTwo->item(0)->nodeValue;
$VarThree = $varOne->getElementsByTagName( "VarThree" );
$VarThree = $VarThree->item(0)->nodeValue;
$VarFour = $varOne->getElementsByTagName( "VarFour" );
$VarFour = $VarFour->item(0)->nodeValue;
$VarFive = $varOne->getElementsByTagName( "VarFive" );
$VarFive = $VarFive->item(0)->nodeValue;
echo "$VarTwo - $VarThree - $VarTFour - ETC\n";
echo "<img src=\"$VarFive\" />";
echo "Link";
}
?>
Start with something like this:
$doc = simplexml_load_file("feedpage.xml");
Then (since I don't know what your XML file looks like), try:
echo "<pre>".print_r($doc,true)."</pre>";
to see exactly how the resulting object is laid out. From there, you should be able to pick out the pieces you need to build what you want.
Edit:
If your output is:
SimpleXMLElement Object (
[varOne] => SimpleXMLElement Object (
[varOne] => Title
[varTwo] => A description
[VarThree] => A Link
[VarFour] => An Image
)
)
You could do this to access the properties of each one:
foreach($doc as $row) {
$title = $row->varOne;
$description = $row->varTwo;
// etc.
}
So with the foreach loop, you can go through each main item and access each item's properties.
And if you want to put code in comments, you can use the backtick (`) to surround your text, but it doesn't work well for code blocks (like the one you wanted to post). Best for variables or other short bits.
FINAL EDIT:
Let's take this example object:
SimpleXMLElement Object (
[varOne] => SimpleXMLElement Object (
[varOne] => "Title"
[varTwo] => "Description"
[varThree] => "Link"
[varFour] => "Image"
[varFive] => SimpleXMLElement Object (
[varA] => "something"
[varB] => SimpleXMLElement Object (
[varX] => "a string"
)
[varC] => "another thing"
)
)
)
Let's say the whole thing is contained in a variable $obj. Now let's say we wanted what's in varX. We'd access it like this:
echo $obj->varOne->varFive->varB->varX;
Beyond this, I don't know what else to tell you. You need to closely examine the objects you have and determine how they are structured. Extrapolate what you've learned here and apply it to your situation.

Why a loop over MySQL query change an array which has nothing to do with the query?

I have the following piece of code:
print_r($queries);
$id2query = array();
while ($res_array = mysql_fetch_array($results)) {
$id = $res_array['id'];
$query = $res_array['query'];
$id2query[$id] = $query;
}
print_r($queries);
The interesting thing is that printr_r before and after the loop return different things.
Does anybody know how it can be possible?
ADDED
$queries is an array. It shown code is a part of a function and $queries is one of the arguments of the function. Before the loop it returns:
Array ( [0] => )
and after the loop it returns:
Array ( [0] => web 2.0 )
ADDED 2
web 2.0 comes from $res_array. Here is the content of the $res_array:
Array ( [0] => 17 [id] => 17 [1] => web 2.0 [query] => web 2.0 [2]
But I do not understand how a value from $res_array migrates to $queries.
ADDED 3
I tried
print "AAAA".var_dump($queries)."BBB";
it returns AAABBB.
ADDED 4
I have managed to use var_dump in the correct way and this is what it returns before the loop:
array(1) { [0]=> &string(0) "" }
This is what I have after the loop:
array(1) { [0]=> &string(7) "web 2.0" }
But I do not understand what it means.
The var_dump below ADDED 4 shows it, the array contains a reference to a string. So it is not a copy of that string, it is something like a pointer (I know, they are not real pointers, see PHPDocs below) to the original string. So if that one gets changed, the references shows the changed value too.
I'd suggest you have a look at:
PHPDoc References
PHPDoc What references do
Example code:
$s = "lulu";
$a = array(&$s);
var_dump($a);
$s = "lala";
var_dump($a);
First var_dump will return:
array(1) {
[0]=>
&string(4) "lulu"
}
And the second:
array(1) {
[0]=>
&string(4) "lala"
}

Categories