tags:

views:

104

answers:

5

Is there any way to loop through an xml loaded in simpleXML in reverse? say if i have the following xml

$string ="<items>
    <item>1</item>
    <item>2</item>
    <item>3</item> 
</items>";

$xml = simplexml_load_string($string);

is there any way to reverse $xml so that i get item 3 when i do a foreach

A: 

$xml->item is an array. Use array sorting functions.

rsort($xml->item);
print_r($xml->item);

EDIT: Ok, there is no way to do this. You have to make an array first.

$myArray = array_reverse((array)$xml->item, true);
print_r($myArray);

(no testing again)

Anpher
Won't work because `$xml->item` is not an array. It's an iterator (`SimpleXMLIterator` to be exact). And you can't sort iterators...
ircmaxell
Nope. It's an object. That's why you'll get a Warning: rsort() expects parameter 1 to be array
Gordon
`rsort` does not reverse the array, it sorts the array in reverse alphabetical order. `array_reverse` could be used instead.
Sjoerd
@Sjoerd no. same warning.
Gordon
Than make an array and sort or reverse it.
Anpher
@Anpher nope. wont work either. The array will contain only 1 element, not all three.
Gordon
A: 

Since Iterators in PHP have no previous style method, the short answer is no. You could read them into an array and then reverse the array, but that wouldn't be directly iterating backwards.

One option would be to create a ReverseIterator, where you wrap a regular iterator and then traverse it backwards. But that might get problematic since you don't know if a iterator has an end (You can see some examples that don't in the SPL).

So no, you can't. Your only option would be to do something like this:

$nodes = array();
foreach ($xml AS $node) {
    array_unshift($nodes, $node);
}
ircmaxell
I am not sure `SimpleXmlElement` uses the `SimpleXmlIterator` by default when using it with `foreach`. The default class with `simplexml_load_file` is `SimpleXmlElement` and that is a `Class [ <internal:SimpleXML> <iterateable> class SimpleXMLElement implements Traversable ]`.
Gordon
It does. `$xml->item` is itself an instance of `SimpleXmlIterator` (at least on my 5.3.3 install)...
ircmaxell
On my 5.3.3 install `var_dump( $xml->item );` returns `SimpleXmlElement`. [I'm notoriously bad at reading C code](http://svn.php.net/viewvc/php/php-src/trunk/ext/simplexml/), but I'd say a SimpleXmlElement is basically a HashTable on Steroids and the Iterator is baked into it.
Gordon
I just checked again, and now it's reporting `SimleXMLElement` as well. I'm not sure why I was getting an iterator, but something tells me there's some dark voodoo going on under the hood...
ircmaxell
+1  A: 
$items = $xml->item;
for ($i = count($items) - 1; $i >= 0; $i--)
{
    echo (string) $items[$i].", ";
}
Sjoerd
Yeah, [`SimpleXMLIterator`](http://us3.php.net/manual/en/class.simplexmliterator.php) does implement `Countable` so the `count` part should work. However neither it nor any of its inherited classes/interfaces inherit `ArrayAccess`, so the `$items[$i]` should not work. But I tried it and it does. Gotta love incorrect implementations (Since the only way it should work is if `$it instanceof ArrayAccess` returns true, but it doesn't (I've checked))... Chalk one up for undocumented functionality...
ircmaxell
@ircmaxell the above works fine.
Gordon
I'm not saying the above code doesn't work. I'm just saying it's using undocumented (and improperly implemented if you want to get down to it) functionality. This is a PHP issue, not an issue with the provided code...
ircmaxell
@ircmaxell it's not undocumented. SimpleXmlElement has a count method and being able to access SimpleXmlElement through array notation is shown in the examples. It's just that SimpleXmlElement does not implements the interfaces you would expect.
Gordon
A: 

With simplexml:

<?php
$string ="<items>
    <item>1</item>
    <item>2</item>
    <item>3</item>
</items>";

$xml = simplexml_load_string($string);

$length = count($xml->item);
for($i = $length; $i; --$i) {
    echo $xml->item[$i-1];
}

prints:

321

Another expample:

It's a little verbose and it uses the more powerful DomDocument and not SimpleXml if you really want to ;)

<?php
$string ="<items>
    <item>1</item>
    <item>2</item>
    <item>3</item>
</items>";

$oXml = DomDocument::loadXml($string);

$oItems = $oXml->firstChild->childNodes;
$lastItemIndex = $oItems->length;

$oItem = $oItems->item($lastItemIndex-1);
do {
    echo $oItem->nodeValue;
} while($oItem = $oItem->previousSibling);

prints:

3
2
1
edorian
+1  A: 

Assuming the OP is a beginner only interested in a solution I'd recommend using xpath() to get the elements and array_reverse() to reverse their order:

$items = array_reverse($xml->xpath('item'));

Note: the XPath "item" grabs all <item/> elements that are the direct children of current element.

Josh Davis