Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why must I rewind IteratorIterator

Tags:

iterator

php

spl

$arrayIter = new ArrayIterator( array(1, 2) );
$iterIter = new IteratorIterator($arrayIter);

var_dump($iterIter->valid()); //false
var_dump($arrayIter->valid()); //true

If I first call $iterIter->rewind(), then $iterIter->valid() is true. I'm curious why it requires that rewind() be called. I imagine there's good reason for it, but I would have expected it to simply start iteration at whatever state it's inner iterator is in, and leave it as an option to rewind before beginning iteration.

calling next() also seems to put it in a "valid" state(although it advances to the next position, suggesting it was previously at the first position).

$arrayIter = new ArrayIterator(array(1,2));
$iterIter = new IteratorIterator($arrayIter);

$iterIter->next();
var_dump($iterIter->valid()); 

Again, I'm curious why I need to call rewind(), despite the inner iterator being in a valid state.

like image 755
goat Avatar asked Mar 07 '26 23:03

goat


2 Answers

With a fresh iterator the position isn't initialized, simply for performance reasons. You can stack iterators on top of other iterators. If all of them would rewind during construction there would be some performance impact. Additionally, some iterators might change their first value after the constructor was executed, which is unknown to iterators further out.

Iterators are usually executed by foreach() which does a rewind() first.

like image 149
johannes Avatar answered Mar 09 '26 13:03

johannes


While extending the IteratorIterator class to spare implementing the whole iterator interface and/or to create a decorator of an iterator I've been running into this as well.

That decorator is already the solution to the problem, it only needs to implement the missing functionality to remove the inconsistency. No need for an auto-rewind:

class IteratorDecorator extends IteratorIterator
{
    public function valid()
    {
        return $this->getInnerIterator()->valid();
    }
}

Example: If you have an Iterator object that is valid by default, e.g. ArrayIterator:

$it = new ArrayIterator(array(1));
var_dump($it->valid());             # bool(true)

$itit = new IteratorIterator($it);
var_dump($itit->valid());           # bool(false)

This shows the inconsistency of the IteratorIterator implementation well, the IteratorIterator object does not properly reflect the inner ArrayIterator's state. Using the IteratorDecorator can heal this:

$decor = new IteratorDecorator($it);
var_dump($decor->valid());          # bool(true)

And if you have followed up until here, here is another special case you might want to consider: If you don't need to have rewind with the inner iterator, you can just use the NoRewindIterator which returns the validity correct as well:

$noretit = new NoRewindIterator($it);
var_dump($noretit->valid());        # bool(true)

Taken Johannes "no auto-rewind" arguments into account, this makes sense, as the NoRewindIterator expects that the iterator should not be rewinded and shows the inner iterator's validity correctly.

But as the IteratorDecorator shows, I don't do any kind of auto-rewind as well to remove the inconsistency.

like image 43
hakre Avatar answered Mar 09 '26 11:03

hakre



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!