I'm having difficulty to get RecursiveFilterIterator
to visit children of objects that I don't want to accept, without also returning the unacceptable object.
I have two types of Node
objects: NodeTypeA
and NodeTypeA
, that both extend abstract type Node
:
abstract class Node implements Countable, RecursiveIterator
{
protected $children;
public function __construct( array $children = array() )
{
$this->children = $children;
}
public function count()
{
return count( $this->children );
}
public function hasChildren()
{
if( !$this->valid() )
{
return false;
}
return count( $this->current() ) > 0;
}
public function getChildren()
{
return $this->current();
}
public function rewind()
{
reset( $this->children );
}
public function key()
{
return key( $this->children );
}
public function valid()
{
return null !== $this->key();
}
public function current()
{
return current( $this->children );
}
public function next()
{
next( $this->children );
}
}
class NodeTypeA extends Node {}
class NodeTypeB extends Node {}
... for which I defined this RecursiveFilterIterator
:
class RecursiveNodeFilterIterator
extends RecursiveFilterIterator
{
public function __construct( RecursiveIterator $iterator, $kind )
{
parent::__construct( $iterator );
$this->kind = $kind;
}
public function accept()
{
$current = $this->current();
return $this->hasChildren() || $current instanceof $this->kind;
}
public function getChildren()
{
return new self( $this->getInnerIterator()->getChildren(), $this->kind );
}
}
Then, when I run this snippet:
header( 'Content-Type: text/plain; charset=utf-8' );
$nodes = new NodeTypeA( array(
new NodeTypeB( array(
new NodeTypeA( array(
new NodeTypeB( array(
new NodeTypeA(),
new NodeTypeA()
) ),
) ),
new NodeTypeA( array(
new NodeTypeB( array(
new NodeTypeB( array(
new NodeTypeA(),
new NodeTypeB()
) ),
) )
) ),
new NodeTypeB()
) ),
new NodeTypeA()
) );
$rii = new RecursiveIteratorIterator(
new RecursiveNodeFilterIterator( $nodes, 'NodeTypeA' ),
RecursiveIteratorIterator::SELF_FIRST
);
foreach( $rii as $node )
{
echo str_repeat( ' ', $rii->getDepth() ) . get_class( $node ) . PHP_EOL;
}
I was hoping to get this result:
NodeTypeA
NodeTypeA
NodeTypeA
NodeTypeA
NodeTypeA
NodeTypeA
... but got:
NodeTypeB
NodeTypeA
NodeTypeB
NodeTypeA
NodeTypeA
NodeTypeA
NodeTypeB
NodeTypeB
NodeTypeA
NodeTypeA
In other words, on iterating, it also returns NodeTypeB
objects when they have children. This makes sense, since in RecursiveNodeFilterIterator::accept()
I have defined return $this->hasChildren() || ...
, in order for the filter to visit all possible child nodes.
Is there a way to have RecursiveNodeFilterIterator
visit child nodes of NodeTypeB
without actually returning the NodeTypeB
nodes themselves?
A simple option is to wrap the RecursiveIteratorIterator
with a filter iterator that only accept
s the items that you are interested in.
(Moved from comment to answer)