web-dev-qa-db-fra.com

Comment fonctionne RecursiveIteratorIterator en PHP?

Comment fonctionne RecursiveIteratorIterator

Le manuel PHP n'a rien de très documenté ou expliqué. Quelle est la différence entre IteratorIterator et RecursiveIteratorIterator?

78
varuog

Quelle est la différence entre IteratorIterator et RecursiveIteratorIterator?

Pour comprendre la différence entre ces deux itérateurs, il faut d’abord comprendre un peu les conventions de dénomination utilisées et ce que nous entendons par itérateurs "récursifs".

Itérateurs récursifs et non récursifs

PHP a des itérateurs non "récursifs", tels que ArrayIterator et FilesystemIterator. Il existe également des itérateurs "récursifs" tels que RecursiveArrayIterator et RecursiveDirectoryIterator. Ces derniers ont des méthodes permettant de les percer, mais pas les premiers.

Lorsque des instances de ces itérateurs sont bouclées par elles-mêmes, même les plus récursives, les valeurs ne proviennent que du niveau "supérieur", même si vous parcourez un tableau ou un répertoire imbriqué avec des sous-répertoires.

Les itérateurs récursifs implémentent un comportement récursif (via hasChildren(), getChildren()) mais ne le font pas exploiter il.

Il serait peut-être préférable de considérer les itérateurs récursifs comme des itérateurs "récursifs": ils ont la capacité d'être itérés récursivement mais simplement itérés sur une instance de l'un de ces classes ne fera pas cela. Pour exploiter le comportement récursif, continuez à lire.

RecursiveIteratorIterator

C'est ici que la RecursiveIteratorIterator entre en jeu. Il sait comment appeler les itérateurs "récursifs" de manière à creuser la structure dans une boucle normale et plate. Il met le comportement récursif en action. Il s'agit essentiellement de passer au-dessus de chacune des valeurs de l'itérateur, de rechercher des "enfants" dans lesquels rentrer ou non, et d'entrer et de sortir de ces collections d'enfants. Vous collez une instance de RecursiveIteratorIterator dans un foreach et elle plonge dans la structure pour ne pas être obligé de le faire.

Si la variable RecursiveIteratorIterator n'est pas utilisée, vous devrez écrire vos propres boucles récursives pour exploiter le comportement récursif, en vérifiant par rapport à la fonction hasChildren() de l'itérateur "récursif" et en utilisant getChildren().

C'est donc un bref aperçu de RecursiveIteratorIterator, en quoi est-il différent de IteratorIterator? Eh bien, vous posez fondamentalement le même type de question que Quelle est la différence entre un chaton et un arbre? Tout simplement parce que les deux apparaissent dans la même encyclopédie (ou manuel, pour les itérateurs) ne signifie pas que vous devriez confondre les deux.

IteratorIterator

Le travail de la variable IteratorIterator consiste à prendre n'importe quel objet Traversable et à l'envelopper de manière à satisfaire l'interface Iterator. Pour cela, il est utile d’appliquer un comportement spécifique à un itérateur sur l’objet non-itérateur.

Pour donner un exemple pratique, la classe DatePeriod est Traversable mais pas une Iterator. En tant que tel, nous pouvons parcourir ses valeurs avec foreach() mais nous ne pouvons pas faire d'autres choses que nous ferions normalement avec un itérateur, comme le filtrage.

TASK: boucle les lundis, mercredis et vendredis des quatre prochaines semaines.

Oui, ceci est trivial en foreach- sur le DatePeriod et en utilisant un if() dans la boucle; mais ce n'est pas le but de cet exemple!

$period = new DatePeriod(new DateTime, new DateInterval('P1D'), 28);
$dates  = new CallbackFilterIterator($period, function ($date) {
    return in_array($date->format('l'), array('Monday', 'Wednesday', 'Friday'));
});
foreach ($dates as $date) { … }

L'extrait ci-dessus ne fonctionnera pas, car CallbackFilterIterator attend une instance d'une classe qui implémente l'interface Iterator, contrairement à DatePeriod. Cependant, comme il s'agit de Traversable, nous pouvons facilement satisfaire à cette exigence en utilisant IteratorIterator.

$period = new IteratorIterator(new DatePeriod(…));

Comme vous pouvez le constater, cela n'a rien ni aucune itération relative aux classes d'itérateurs ni à la récursivité, et c'est là que réside la différence entre IteratorIterator et RecursiveIteratorIterator.

Résumé

RecursiveIteraratorIterator est utilisé pour effectuer une itération sur un RecursiveIterator (itérateur "récursif"), en exploitant le comportement récursif disponible.

IteratorIterator sert à appliquer le comportement Iterator à des objets non itérateurs, Traversable.

30
salathe

RecursiveDirectoryIterator affiche le chemin complet et pas seulement le nom du fichier. Le reste a l'air bien. En effet, les noms de fichiers sont générés par SplFileInfo. Ceux-ci devraient être affichés comme nom de base à la place. La sortie souhaitée est la suivante:

$path =__DIR__;
$dir = new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS);
$files = new RecursiveIteratorIterator($dir,RecursiveIteratorIterator::SELF_FIRST);
while ($files->valid()) {
    $file = $files->current();
    $filename = $file->getFilename();
    $deep = $files->getDepth();
    $indent = str_repeat('│ ', $deep);
    $files->next();
    $valid = $files->valid();
    if ($valid and ($files->getDepth() - 1 == $deep or $files->getDepth() == $deep)) {
        echo $indent, "├ $filename\n";
    } else {
        echo $indent, "└ $filename\n";
    }
}

sortie:

tree
 ├ dirA
 │ ├ dirB
 │ │ └ fileD
 │ ├ fileB
 │ └ fileC
 └ fileA
0
javad shariaty