Je vais répondre aux questions dans l'ordre inverse :
foreach
fonctionne parfaitement sans avoir à appeler rewind
Quelles sont les différences entre les deux ? foreach
y while
lorsque vous travaillez avec des itérateurs
foreach
en interne, un appel à rewind()
c'est pourquoi vous n'avez pas à le faire vous-même. C'est toujours fait, donc même si vous avez déjà utilisé un itérateur, la fonction foreach
la boucle recommencera depuis le début. (Vous pouvez éviter cela en l'enveloppant dans une balise NoRewindIterator
).
Le tableau n'a jamais été lancé ou appelé, pourquoi $iterator->rewind() est-il nécessaire pour la boucle while ?
Les itérateurs de SPL sont conçus pour être utilisés avec foreach
et pour éviter les appels de méthode en double dans ce cas. Si le RecursiveIteratorIterator
appellerait le RecursiveArrayIterator::rewind()
lors de la construction, puis elle sera appelée à nouveau lorsque l'élément foreach
La boucle commence. C'est pourquoi l'appel n'est pas terminé.
Pourquoi un A supplémentaire et non un C ?
Pour s'en rendre compte, il est intéressant de voir quelles méthodes de l'élément RecursiveArrayIterator
sont effectivement appelés :
<?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();
}
Cela produit le résultat suivant :
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
La sortie semble un peu étrange, en particulier la deuxième itération manque le next()
pour qu'il reste au même élément.
La raison en est une particularité de l'utilisation de l'eau. RecursiveIteratorIterator
mise en œuvre : Les itérateurs commencent dans le RS_START
et le premier next
l'appel dans cet état vérifie uniquement hasChildren()
mais n'appelle pas réellement la fonction de l'itérateur sous-jacent next()
méthode. Une fois cette opération effectuée, il passe à la méthode RS_NEXT
mode dans lequel le next()
l'appel se déroule correctement. C'est pourquoi l'avance est retardée d'un pas.
À mes yeux, il s'agit d'un bogue, mais https://bugs.php.net/bug.php?id=44063 prétend le contraire.