Recursive DOM Iterator

PHP programming forum. Ask questions or help people concerning PHP code. Don't understand a function? Need help implementing a class? Don't understand a class? Here is where to ask. Remember to do your homework!

Moderator: General Moderators

Post Reply
User avatar
Ambush Commander
DevNet Master
Posts: 3698
Joined: Mon Oct 25, 2004 9:29 pm
Location: New Jersey, US

Recursive DOM Iterator

Post by Ambush Commander »

I'm trying to get SPL's RecursiveIterator to play nicely with DOM, but so far I've been unsuccessful. Anyone know why?

Code: Select all

<?php

class DOMIterator implements RecursiveIterator
{
    
    protected $node;
    protected $index = 0;
    
    public function __construct(DOMNode $node) {
        $this->node = $node;
    }
    
    public function key() {
        return $this->index;
    }
    
    public function current() {var_dump($this);
        if (!$this->valid()) return false;
        return $this->node->childNodes->item($this->index);
    }
    
    public function next() {
        if ($this->valid()) {
            return $this->node->childNodes->item(++$this->index);
        } else {
            return false;
        }
    }
    
    public function rewind() {
        $this->index = 0;
        if ($this->valid()) return false;
        else return $this->current();
    }
    
    public function valid() {
        return $this->node->childNodes->length > $this->index;
    }
    
    public function hasChildren() {
        return (bool) $this->node->childNodes->length;
    }
    
    public function getChildren() {
        return new DOMIterator($this->current());
    }
    
}
User avatar
stereofrog
Forum Contributor
Posts: 386
Joined: Mon Dec 04, 2006 6:10 am

Post by stereofrog »

Your code works for me with one single change

Code: Select all

public function valid() {
		return $this->node->childNodes && 
			$this->node->childNodes->length > $this->index;
    }
Test code:

Code: Select all

$doc = new DOMDocument;
$doc->loadXML('<?xml version="1.0" encoding="iso-8859-1"?><root><a>blah</a><b>bar</b></root>');
foreach(new RecursiveIteratorIterator(new DOMIterator($doc), 1) as $n)
	var_dump($n);
User avatar
Ambush Commander
DevNet Master
Posts: 3698
Joined: Mon Oct 25, 2004 9:29 pm
Location: New Jersey, US

Post by Ambush Commander »

Ah, so there were two primary problems with my code:

- The exposed iterator is RecursiveIteratorIterator
- RecursiveIteratorIterator::SELF_FIRST must be passed, otherwise only leaves will be iterated on

New code, which is operational:

Code: Select all

<?php

class DOMIterator extends RecursiveIteratorIterator
{
    public function __construct(DOMNode $node) {
        parent::__construct(new DOMNodeIterator($node), self::SELF_FIRST);
    }
}

class DOMNodeIterator implements RecursiveIterator
{
    
    protected $node;
    protected $index = 0;
    
    public function __construct(DOMNode $node) {
        $this->node = $node;
    }
    
    public function key() {
        return $this->index;
    }
    
    public function current() {
        if (!$this->valid()) return false;
        $node = $this->node->childNodes->item($this->index);
        return $node;
    }
    
    public function next() {
        $this->index++;
        return $this->current();
    }
    
    public function rewind() {
        $this->index = 0;
        return $this->current();
    }
    
    public function valid() {
        return 
            $this->node->childNodes &&
            $this->node->childNodes->length > $this->index;
    }
    
    public function hasChildren() {
        return (bool) $this->node->childNodes->length;
    }
    
    public function getChildren() {
        return new DOMNodeIterator($this->current());
    }
    
}

$dom = new DOMDocument();
$dom->loadHTML('<html><body><b>foo</b><i>as<a>sdf</a>df</i></body></html>');

$iterator = new DOMIterator($dom);
echo '<pre>';
foreach ($iterator as $node) {
    echo str_repeat(' ', $iterator->getDepth() * 2) . $node->nodeName . "\n";
}
echo '</pre>';
Post Reply