Like by the name of the class alone (NoRewindIterator
), the manual has the following wording in specific:
NoRewindIterator - This iterator cannot be rewound.
And for the concrete method:
NoRewindIterator::rewind() - Prevents the rewind operation on the inner iterator.
This implies that the Iterator::rewind()
method is not passed through to the inner iterator. Tests also show this, here is a simple one I've been running (code of all iterators not part of PHP are in the Iterator Garden):
$iterator = new RangeIterator(1, 1);
$debug = new DebugIteratorDecorator($iterator);
$noRewind = new NoRewindIterator($debug);
echo "first foreach:\n";
foreach ($noRewind as $value) {
echo "iteration value: $value\n";
}
In this code, the debug-iterator prints (echoes) iteration information on the fly:
first foreach:
Iterating (RangeIterator): #0 valid()
Iterating (RangeIterator): #0 parent::valid() is TRUE
Iterating (RangeIterator): #0 current()
Iterating (RangeIterator): #0 parent::current() is 1
iteration value: 1
Iterating (RangeIterator): #1 next()
Iterating (RangeIterator): #1 after parent::next()
Iterating (RangeIterator): #1 valid()
Iterating (RangeIterator): #1 parent::valid() is FALSE
As this shows, $iterator->rewind()
is never called.
This also makes sense for the same reasons given in a related question: Why must I rewind IteratorIterator. The NoRewindIterator
extends from IteratorIterator
and different to it's parent class, the getInnerIterator()
method returns an Iterator
and not a Traversable
.
This change allows you to initialize the rewind when you need to:
echo "\n\$calling noRewind->getInnerIterator()->rewind():\n";
$noRewind->getInnerIterator()->rewind();
echo "\nsecond foreach:\n";
foreach ($noRewind as $value) {
echo "iteration value: $value\n";
}
Exemplary debug output again:
$calling noRewind->getInnerIterator()->rewind():
Iterating (RangeIterator): #0 rewind()
Iterating (RangeIterator): #0 after parent::rewind()
second foreach:
Iterating (RangeIterator): #0 valid()
Iterating (RangeIterator): #0 parent::valid() is TRUE
Iterating (RangeIterator): #0 current()
Iterating (RangeIterator): #0 parent::current() is 1
iteration value: 1
Iterating (RangeIterator): #1 next()
Iterating (RangeIterator): #1 after parent::next()
Iterating (RangeIterator): #1 valid()
Iterating (RangeIterator): #1 parent::valid() is FALSE
Knowing about these details then does allow to create a OneTimeRewindIterator
for example:
/**
* Class OneTimeRewindIterator
*/
class OneTimeRewindIterator extends NoRewindIterator
{
private $didRewind = FALSE;
public function rewind() {
if ($this->didRewind) return;
$this->didRewind = TRUE;
$this->getInnerIterator()->rewind();
}
}