Question

I've been messing with RecursiveArrayIterators for handling nested objects as trees. The following code is bothering me as the result is returning only some of the values I'm expecting. Mainly, the root node seems to never be iterated over. I have a feeling I've just been staring at it a bit too long but wanted to make sure I was on the right track with this.

class Container extends \RecursiveArrayIterator
{
    protected $_alias;

    public function __construct( $alias = null )
    {
        if( is_null( $alias ) )
        {
            $alias = uniqid( 'block_' );
        }

        $this->_alias = $alias;
    }

    public function getAlias()
    {
        return $this->_alias;
    }
}

try
{
    $root = new Container( 'root_level' );
    $block = new Container( 'first_level' );
    $child = new Container( 'second_level' );
    $child_of_child_a = new Container( 'third_level_a' );
    $child_of_child_b = new Container( 'third_level_b' );

    $child->append( $child_of_child_a );
    $child->append( $child_of_child_b );
    $child_of_child_a->append( new Container );
    $child_of_child_a->append( new Container );
    $block->append( $child );
    $root->append( $block );

    $storage = new \RecursiveIteratorIterator( $root, RecursiveIteratorIterator::SELF_FIRST );

    foreach( $storage as $key => $value )
    {
        print_r( str_repeat( ' ', $storage->getDepth() * 4 ) . $value->getAlias() . PHP_EOL );
    }
}
catch( \Exception $e )
{
    var_dump( $e->getMessage() );
}

With the result being...

first_level
second_level
    third_level_a
        block_51f98b779c107
        block_51f98b779c124
    third_level_b

Where's the root node?

ANSWER

Sven's answer jarred my over-worked brain into processing this correctly. This was my final bit of successful code in case someone else is attempting something similar.

class OuterContainer extends \ArrayIterator
{

}

class Container extends \ArrayIterator
{
    protected $_alias;

    public function __construct( $alias = null )
    {
        if( is_null( $alias ) )
        {
            $alias = uniqid( 'container_' );
        }

        $this->_alias = $alias;
    }

    public function getAlias()
    {
        return $this->_alias;
    }
}

try
{
    $c1 = new Container( 'Base' );

    $c1_c1 = new Container( 'Base > 1st Child' );

    $c1_c2 = new Container( 'Base > 2nd Child' );

    $c1_c1_c1 = new Container( 'Base > 1st Child > 1st Child' );

    $c1_c1->append( $c1_c1_c1 );

    $c1->append( $c1_c1 );

    $c1->append( $c1_c2 );

    $outer_container = new OuterContainer;

    $outer_container->append( $c1 );

    $storage = new \RecursiveIteratorIterator( new \RecursiveArrayIterator( $outer_container ), RecursiveIteratorIterator::SELF_FIRST );

    foreach( $storage as $key => $value )
    {
        print_r( $value->getAlias() . PHP_EOL );
    }
}
catch( \Exception $e )
{
    var_dump( $e->getMessage() );
}
Was it helpful?

Solution

The root node is not there because you are not using the RecursiveArrayIterator correctly.

It's intended use is to have an array with multiple sub-arrays in any level and structure, and give this array into ONE instance of the RecursiveArrayIterator, and to iterate over everything, put this into a RecursiveIteratorIterator.

That way, the entire array, including it's top level, will be iterated.

Because you abused the RecursiveArrayIterator to be the carrier of information, the top level object is not iterated.

Suggested easy example that has some nastyness, but demonstrates the principle:

$root = array( 'root_level' );
$block = array( 'first_level' );
$child = array( 'second_level' );
$child_of_child_a = array( 'third_level_a' );
$child_of_child_b = array( 'third_level_b' );

$child[] = $child_of_child_a;
$child[] = $child_of_child_b ;
$child_of_child_a[] = array('');
$child_of_child_a[] = array('');
$block[] = $child;
$root[] = $block ;

$storage = new \RecursiveIteratorIterator( new \RecursiveArrayIterator($root), RecursiveIteratorIterator::SELF_FIRST );

var_dump($root);
foreach ($storage as $key=>$value) {
    echo $key.": ".$value."\n";
}

Result output:

array(2) {
    [0] =>
  string(10) "root_level"
  [1] =>
  array(2) {
        [0] =>
    string(11) "first_level"
    [1] =>
    array(3) {
            [0] =>
      string(12) "second_level"
      [1] =>
      array(1) {
                ...
            }
      [2] =>
      array(1) {
                ...
            }
    }
  }
}
0: root_level
1: Array
    0: first_level
1: Array
    0: second_level
1: Array
    0: third_level_a
2: Array
    0: third_level_b

It's output is not quite the same, but my point is that you can iterate over the structure without having every single node to be an instance of the RecursiveArrayIterator. You can add anything that is either an array, or an object acting as an array, or an object that can usefully be iterated.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top