RecursiveIteratorIterator
returns extra result if rewind()
is not called before while
loop
Example
$array = array("A","B","C");
$iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($array));
//$iterator->rewind() ; this would fix it
while ( $iterator->valid() ) {
print($iterator->current()) ;
$iterator->next();
}
Output
AABC <--- Instead of ABC
A
not C
?$iterator->rewind()
required for while loopforeach
works perfectly without having to call rewind
whats the differences between foreach
and while
when working with iterators Code In action
I'm going to answer the questions in reverse order:
foreach
works perfectly without having to callrewind
whats the differences between thoseforeach
andwhile
when working with iterators
foreach
internally does a call to rewind()
, that's why you don't have to do it yourself. This is always done, so even if you have already used an iterator, the foreach
loop will start over from the beginning. (You can avoid this by wrapping it in a NoRewindIterator
).
The Array has never been initiated or called why is $iterator->rewind() required for while loop
The SPL iterators are designed to be used with foreach
and to avoid duplicate method calls in this case. If the RecursiveIteratorIterator
would call the RecursiveArrayIterator::rewind()
method on construction, then it would be called again when the foreach
loop starts. That's why the call isn't done.
Why an extra A not C ?
To figure this out it is nice to see which methods of the RecursiveArrayIterator
actually get called:
<?php
class DebugRAI extends RecursiveArrayIterator {
public function rewind() { echo __METHOD__, "\n"; return parent::rewind(); }
public function current() { echo __METHOD__, "\n"; return parent::current(); }
public function key() { echo __METHOD__, "\n"; return parent::key(); }
public function valid() { echo __METHOD__, "\n"; return parent::valid(); }
public function next() { echo __METHOD__, "\n"; return parent::next(); }
}
$array = array("A", "B", "C");
$iterator = new RecursiveIteratorIterator(new DebugRAI($array));
while ($iterator->valid()) {
echo $iterator->current(), "\n";
$iterator->next();
}
This produces the following output:
DebugRAI::valid
DebugRAI::current
A
DebugRAI::valid
DebugRAI::valid
A
DebugRAI::next
DebugRAI::valid
DebugRAI::valid
DebugRAI::current
B
DebugRAI::next
DebugRAI::valid
DebugRAI::valid
DebugRAI::current
C
DebugRAI::next
DebugRAI::valid
DebugRAI::valid
The output looks a bit odd, in particular the second iteration misses the next()
call, so it just stays at the same element.
The reason for this is a peculiarity in the RecursiveIteratorIterator
implementation: Iterators start off in the RS_START
state and the first next
call in this state only checks hasChildren()
, but does not actually call the underlying iterator's next()
method. After this was done it switches into the RS_NEXT
mode in which the next()
call happens properly. That's why the forward move is delayed by one step.
In my eyes this is a bug, but https://bugs.php.net/bug.php?id=44063 claims otherwise.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With