8 votes

RecursiveIteratorIterator renvoie des éléments supplémentaires

RecursiveIteratorIterator renvoie un résultat supplémentaire si rewind() n'est pas appelé avant while boucle

Exemple

$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();
}

Sortie

AABC  <--- Instead of ABC
  • Pourquoi un supplément A no C ?
  • Le réseau n'a jamais été initié ou appelé, pourquoi est-ce que $iterator->rewind() nécessaire pour la boucle while
  • foreach fonctionne parfaitement sans avoir à appeler rewind Quelles sont les différences entre foreach y while lorsque vous travaillez avec des itérateurs

Le code en action

5voto

NikiC Points 47270

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.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X