Comment fonctionne RecursiveIteratorIterator en PHP?

88

Comment ça RecursiveIteratorIteratormarche?

Le manuel PHP n'a rien de bien documenté ou expliqué. Quelle est la différence entre IteratorIteratoret RecursiveIteratorIterator?

varuog
la source
2
il y a un exemple sur php.net/manual/en/recursiveiteratoriterator.construct.php et il y a aussi une introduction sur php.net/manual/en/class.iteratoriterator.php - pouvez-vous indiquer exactement ce que vous avez du mal à comprendre . Que doit contenir le manuel pour faciliter sa compréhension?
Gordon le
1
Si vous demandez comment RecursiveIteratorIteratorfonctionne, avez-vous déjà compris comment IteratorIteratorfonctionne? Je veux dire que c'est fondamentalement le même, seule l'interface qui est consommée par les deux est différente. Et êtes-vous plus intéressé par quelques exemples ou voulez-vous voir le diff de l'implémentation du code C sous-jacent?
hakre
@Gordon je ne savais pas comment une seule boucle foreach peut traverser tous les éléments de la structure arborescente
varuog
@hakra j'essaie maintenant d'étudier toutes les interfaces intégrées ainsi que l'interface spl et l'implémentation de l'itérateur.J'étais intéressé de savoir comment cela fonctionne en arrière-plan avec la boucle forach avec quelques exemples.
varuog
@hakre Ils sont tous les deux très différents. IteratorIteratorcartes Iteratoret IteratorAggregatedans un Iterator, où REcusiveIteratorIteratorest utilisé pour traverser récusivement aRecursiveIterator
Adam

Réponses:

249

RecursiveIteratorIteratorest une traversée d'arbre d'Iterator implémentation concrète . Il permet à un programmeur de parcourir un objet conteneur qui implémente l' interface, voir Iterator dans Wikipedia pour les principes généraux, les types, la sémantique et les modèles d'itérateurs.RecursiveIterator

À la différence de IteratorIteratorlaquelle se trouve un Iteratorparcours d'objet concret implémentant dans un ordre linéaire (et par défaut acceptant n'importe quel type de Traversabledans son constructeur), le RecursiveIteratorIteratorpermet de boucler sur tous les nœuds dans un arbre ordonné d'objets et son constructeur prend un RecursiveIterator.

En bref: RecursiveIteratorIteratorvous permet de boucler sur un arbre, IteratorIteratorvous permet de boucler sur une liste. Je montre cela avec quelques exemples de code ci-dessous bientôt.

Techniquement, cela fonctionne en brisant la linéarité en parcourant tous les enfants d'un nœud (le cas échéant). Cela est possible car, par définition, tous les enfants d'un nœud sont à nouveau un RecursiveIterator. Le niveau supérieur Iteratorempile ensuite en interne les différents RecursiveIterators par leur profondeur et garde un pointeur vers le sous-marin actif actuel Iteratorpour le parcours.

Cela permet de visiter tous les nœuds d'un arbre.

Les principes sous-jacents sont les mêmes qu'avec IteratorIterator: Une interface spécifie le type d'itération et la classe d'itérateur de base est l'implémentation de cette sémantique. Comparez avec les exemples ci-dessous, pour une boucle linéaire avec foreachvous, normalement, ne pensez pas beaucoup aux détails de l'implémentation à moins que vous n'ayez besoin d'en définir un nouveau Iterator(par exemple, lorsqu'un type concret lui-même ne l'implémente pas Traversable).

Pour une traversée récursive - à moins que vous n'utilisiez une Traversalitération de parcours récursive déjà définie - vous devez normalement instancier l' RecursiveIteratorIteratoritération existante ou même écrire une itération de parcours récursive qui est Traversablela vôtre pour avoir ce type d'itération de traversée avec foreach.

Astuce: vous n'avez probablement pas implémenté l'un ni l'autre le vôtre, donc cela pourrait valoir la peine de le faire pour votre expérience pratique des différences qu'ils ont. Vous trouvez une suggestion de bricolage à la fin de la réponse.

Différences techniques en bref:

  • Alors que IteratorIteratorprend n'importe quel Traversablepour un parcours linéaire, RecursiveIteratorIteratornécessite une RecursiveIteratorboucle plus spécifique sur un arbre.
  • IteratorIteratorexpose son Iteratorvia principal getInnerIerator(), RecursiveIteratorIteratorfournit le sous-système actif actuel Iteratoruniquement via cette méthode.
  • Bien que IteratorIteratorne sache absolument rien comme un parent ou des enfants, il RecursiveIteratorIteratorsait également comment obtenir et traverser des enfants.
  • IteratorIteratorn'a pas besoin d'une pile d'itérateurs, RecursiveIteratorIteratorpossède une telle pile et connaît le sous-itérateur actif.
  • IteratorIteratora son ordre en raison de la linéarité et de l'absence de choix, RecursiveIteratorIteratora le choix pour une traversée ultérieure et doit décider pour chaque nœud (décidé via le mode parRecursiveIteratorIterator ).
  • RecursiveIteratorIteratora plus de méthodes que IteratorIterator.

Pour résumer: RecursiveIteratorest un type concret d'itération (boucle sur un arbre) qui fonctionne sur ses propres itérateurs, à savoir RecursiveIterator. C'est le même principe sous-jacent que avec IteratorIerator, mais le type d'itération est différent (ordre linéaire).

Idéalement, vous pouvez également créer votre propre ensemble. La seule chose nécessaire est que votre itérateur implémente Traversablece qui est possible via Iteratorou IteratorAggregate. Ensuite, vous pouvez l'utiliser avec foreach. Par exemple, une sorte d'objet d'itération récursive de traversée d'arbre ternaire avec l'interface d'itération correspondante pour le ou les objets conteneurs.


Passons en revue avec quelques exemples réels qui ne sont pas si abstraits. Entre interfaces, itérateurs concrets, objets conteneurs et sémantique d'itération, ce n'est peut-être pas une si mauvaise idée.

Prenons une liste de répertoires comme exemple. Considérez que vous avez le fichier et l'arborescence de répertoires suivants sur le disque:

Arborescence des répertoires

Alors qu'un itérateur avec un ordre linéaire traverse simplement le dossier et les fichiers de niveau supérieur (une seule liste de répertoires), l'itérateur récursif traverse également les sous-dossiers et répertorie tous les dossiers et fichiers (une liste de répertoires avec les listes de ses sous-répertoires):

Non-Recursive        Recursive
=============        =========

   [tree]            [tree]
     dirA             dirA
     fileA             dirB
                         fileD
                        fileB
                        fileC
                       fileA

Vous pouvez facilement comparer cela avec IteratorIteratorlequel ne fait aucune récursivité pour parcourir l'arborescence de répertoires. Et le RecursiveIteratorIteratorqui peut traverser dans l'arbre comme le montre la liste récursive.

Au début, un exemple très basique avec un DirectoryIteratorqui implémente Traversablequi permet foreachd' itérer dessus:

$path = 'tree';
$dir  = new DirectoryIterator($path);

echo "[$path]\n";
foreach ($dir as $file) {
    echo " ├ $file\n";
}

L'exemple de sortie pour la structure de répertoires ci-dessus est alors:

[tree]
  .
  ..
  dirA
  fileA

Comme vous le voyez, cela n'utilise pas encore IteratorIteratorou RecursiveIteratorIterator. Au lieu de cela, il suffit d'utiliser foreachqui fonctionne sur l' Traversableinterface.

Comme foreachpar défaut ne connaît que le type d'itération nommé ordre linéaire, nous pourrions vouloir spécifier le type d'itération explicitement. À première vue, cela peut sembler trop détaillé, mais à des fins de démonstration (et pour faire la différence avec RecursiveIteratorIteratorplus de visibilité plus tard), spécifions le type d'itération linéaire en spécifiant explicitement le IteratorIteratortype d'itération pour la liste des répertoires:

$files = new IteratorIterator($dir);

echo "[$path]\n";
foreach ($files as $file) {
    echo " ├ $file\n";
}

Cet exemple est presque identique au premier, la différence est qu'il $filess'agit désormais d'un IteratorIteratortype d'itération pour Traversable $dir:

$files = new IteratorIterator($dir);

Comme d'habitude, l'acte d'itération est effectué par foreach:

foreach ($files as $file) {

La sortie est exactement la même. Alors qu'est-ce qui est différent? L'objet utilisé dans le foreach. Dans le premier exemple, c'est un DirectoryIteratordans le deuxième exemple, c'est le IteratorIterator. Cela montre la flexibilité des itérateurs: vous pouvez les remplacer les uns par les autres, le code à l'intérieur foreachcontinue de fonctionner comme prévu.

Permet de commencer à obtenir la liste complète, y compris les sous-répertoires.

Comme nous avons maintenant spécifié le type d'itération, considérons de le changer en un autre type d'itération.

Nous savons que nous devons parcourir tout l'arbre maintenant, pas seulement le premier niveau. Avoir ce travail avec un simple foreachnous avons besoin d' un autre type de iterator: RecursiveIteratorIterator. Et que l'on ne peut itérer que sur les objets conteneurs qui ont l' RecursiveIteratorinterface .

L'interface est un contrat. Toute classe l'implémentant peut être utilisée avec le RecursiveIteratorIterator. Un exemple d'une telle classe est le RecursiveDirectoryIterator, qui est quelque chose comme la variante récursive de DirectoryIterator.

Voyons un premier exemple de code avant d'écrire une autre phrase avec le mot I:

$dir  = new RecursiveDirectoryIterator($path);

echo "[$path]\n";
foreach ($dir as $file) {
    echo " ├ $file\n";
}

Ce troisième exemple est presque identique au premier, mais il crée une sortie différente:

[tree]
  tree\.
  tree\..
  tree\dirA
  tree\fileA

D'accord, pas si différent, le nom de fichier contient maintenant le chemin d'accès devant, mais le reste se ressemble également.

Comme le montre l'exemple, même l'objet annuaire imprègne déjà l' RecursiveIteratorinterface, ce n'est pas encore suffisant pour faire foreachparcourir toute l'arborescence d'annuaire. C'est là que le RecursiveIteratorIteratorentre en action. L'exemple 4 montre comment:

$files = new RecursiveIteratorIterator($dir);

echo "[$path]\n";
foreach ($files as $file) {
    echo " ├ $file\n";
}

Utiliser le à la RecursiveIteratorIteratorplace de l' $dirobjet précédent fera foreachparcourir tous les fichiers et répertoires de manière récursive. Cela répertorie ensuite tous les fichiers, car le type d'itération d'objet a été spécifié maintenant:

[tree]
  tree\.
  tree\..
  tree\dirA\.
  tree\dirA\..
  tree\dirA\dirB\.
  tree\dirA\dirB\..
  tree\dirA\dirB\fileD
  tree\dirA\fileB
  tree\dirA\fileC
  tree\fileA

Cela devrait déjà démontrer la différence entre la traversée à plat et l'arborescence. Le RecursiveIteratorIteratorest capable de parcourir n'importe quelle structure arborescente sous forme de liste d'éléments. Comme il y a plus d'informations (comme le niveau auquel l'itération a lieu actuellement), il est possible d'accéder à l'objet itérateur en l'itérant et par exemple indenter la sortie:

echo "[$path]\n";
foreach ($files as $file) {
    $indent = str_repeat('   ', $files->getDepth());
    echo $indent, " ├ $file\n";
}

Et sortie de l' exemple 5 :

[tree]
  tree\.
  tree\..
     tree\dirA\.
     tree\dirA\..
        tree\dirA\dirB\.
        tree\dirA\dirB\..
        tree\dirA\dirB\fileD
     tree\dirA\fileB
     tree\dirA\fileC
  tree\fileA

Bien sûr, cela ne gagne pas un concours de beauté, mais cela montre qu'avec l'itérateur récursif, il y a plus d'informations disponibles que juste l'ordre linéaire de la clé et de la valeur . Même foreachne peut qu'exprimer ce genre de linéarité, accéder à l'itérateur lui-même permet d'obtenir plus d'informations.

Comme pour les méta-informations, il existe également différentes manières de parcourir l'arbre et donc de classer la sortie. C'est le mode duRecursiveIteratorIterator et il peut être défini avec le constructeur.

L'exemple suivant indiquera au RecursiveDirectoryIteratorde supprimer les entrées de points ( .et ..) car nous n'en avons pas besoin. Mais aussi le mode de récursivité sera changé pour prendre l'élément parent (le sous-répertoire) en premier ( SELF_FIRST) avant les enfants (les fichiers et sous-sous-répertoires dans le sous-répertoire):

$dir  = new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS);
$files = new RecursiveIteratorIterator($dir, RecursiveIteratorIterator::SELF_FIRST);

echo "[$path]\n";
foreach ($files as $file) {
    $indent = str_repeat('   ', $files->getDepth());
    echo $indent, " ├ $file\n";
}

La sortie affiche maintenant les entrées de sous-répertoire correctement répertoriées, si vous comparez avec la sortie précédente, celles-ci n'étaient pas là:

[tree]
  tree\dirA
     tree\dirA\dirB
        tree\dirA\dirB\fileD
     tree\dirA\fileB
     tree\dirA\fileC
  tree\fileA

Le mode récursivité contrôle donc quoi et quand une brach ou une feuille dans l'arborescence est retournée, pour l'exemple de répertoire:

  • LEAVES_ONLY (par défaut): répertorie uniquement les fichiers, pas de répertoires.
  • SELF_FIRST (ci-dessus): répertorie le répertoire et les fichiers qu'il contient.
  • CHILD_FIRST (sans exemple): Listez d'abord les fichiers dans le sous-répertoire, puis le répertoire.

Sortie de l' exemple 5 avec les deux autres modes:

  LEAVES_ONLY                           CHILD_FIRST

  [tree]                                [tree]
          tree\dirA\dirB\fileD                 tree\dirA\dirB\fileD
       tree\dirA\fileB                      tree\dirA\dirB
       tree\dirA\fileC                      tree\dirA\fileB
    tree\fileA                              tree\dirA\fileC
                                         tree\dirA
                                         tree\fileA

Lorsque vous comparez cela avec le parcours standard, toutes ces choses ne sont pas disponibles. L'itération récursive est donc un peu plus complexe lorsque vous devez l'enrouler la tête, mais elle est facile à utiliser car elle se comporte comme un itérateur, vous la mettez dans un foreachet c'est fait.

Je pense que ce sont suffisamment d'exemples pour une réponse. Vous pouvez trouver le code source complet ainsi qu'un exemple pour afficher de jolis ascii-arbres dans cet article: https://gist.github.com/3599532

Faites-le vous-même: faites le RecursiveTreeIteratortravail ligne par ligne.

L'exemple 5 a démontré qu'il existe des méta-informations sur l'état de l'itérateur. Cependant, cela a été délibérément démontré dans l' foreachitération. Dans la vraie vie, cela appartient naturellement au RecursiveIterator.

Un meilleur exemple est le RecursiveTreeIterator, il s'occupe de l'indentation, du préfixe et ainsi de suite. Voir le fragment de code suivant:

$dir   = new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS);
$lines = new RecursiveTreeIterator($dir);
$unicodeTreePrefix($lines);
echo "[$path]\n", implode("\n", iterator_to_array($lines));

Le RecursiveTreeIteratorest destiné à fonctionner ligne par ligne, la sortie est assez simple avec un petit problème:

[tree]
  tree\dirA
   tree\dirA\dirB
    tree\dirA\dirB\fileD
   tree\dirA\fileB
   tree\dirA\fileC
  tree\fileA

Lorsqu'il est utilisé en combinaison avec a, RecursiveDirectoryIteratoril affiche le chemin d'accès complet et pas seulement le nom du fichier. Le reste semble bon. C'est parce que les noms de fichiers sont générés par SplFileInfo. Celles-ci devraient être affichées comme nom de base à la place. La sortie souhaitée est la suivante:

/// Solved ///

[tree]
  dirA
   dirB
    fileD
   fileB
   fileC
  fileA

Créez une classe de décorateur qui peut être utilisée avec RecursiveTreeIteratorau lieu de RecursiveDirectoryIterator. Il doit fournir le nom de base du courant SplFileInfoau lieu du chemin. Le fragment de code final pourrait alors ressembler à:

$lines = new RecursiveTreeIterator(
    new DiyRecursiveDecorator($dir)
);
$unicodeTreePrefix($lines);
echo "[$path]\n", implode("\n", iterator_to_array($lines));

Ces fragments $unicodeTreePrefixfont partie de l'essentiel de l' annexe: Faites-le vous-même: faites le RecursiveTreeIteratortravail ligne par ligne. .

hakre
la source
Il ne répond pas aux questions posées, contient des erreurs factuelles et manque des points essentiels lorsque vous passez à la personnalisation de l'itération. Dans l'ensemble, cela ressemble à une mauvaise tentative d'obtenir la prime sur un sujet dont vous ne savez pas grand-chose ou, si vous le savez, ne peut pas se transformer en une réponse aux questions posées.
salathe le
2
Eh bien, ce qui ne répond pas à ma question "pourquoi", il suffit de faire plus de mots sans en dire beaucoup. Peut-être que vous commencez avec une erreur qui compte? Montrez-le, ne le gardez pas secret.
hakre
La toute première phrase est incorrecte: "RecursiveIteratorIterator est un IteratorIterator qui prend en charge…", ce n'est pas vrai.
salathe le
1
@salathe: Merci de vos commentaires. J'ai édité la réponse pour y répondre. La première phrase était en effet erronée et incomplète. J'ai encore omis les détails d'implémentation concrets de RecursiveIteratorIteratorparce que c'est en commun avec d'autres types, mais j'ai donné quelques informations techniques sur la façon dont cela fonctionne réellement. Je pense que les exemples montrent bien les différences: le type d'itération est la principale différence entre les deux. Aucune idée si vous achetez le type d'itération que vous créez un peu différemment, mais à mon humble avis, pas facile avec les types d'itération sémantiques.
hakre le
1
La première partie est quelque peu améliorée, mais une fois que vous commencez à passer aux exemples, elle vire toujours à des inexactitudes factuelles. Si vous coupiez la réponse à la règle horizontale, ce serait beaucoup mieux.
salathe le
31

Quelle est la différence entre IteratorIteratoret 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 l'on entend par itérateurs «récursifs».

Itérateurs récursifs et non récursifs

PHP a des itérateurs non "récursifs", tels que ArrayIteratoret FilesystemIterator. Il existe également des itérateurs "récursifs" tels que RecursiveArrayIteratoret RecursiveDirectoryIterator. Ces derniers ont des méthodes permettant de les approfondir, les premiers non.

Lorsque les instances de ces itérateurs sont bouclées d'elles-mêmes, même récursives, les valeurs ne proviennent que du niveau "supérieur" même si elles bouclent sur 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 l' exploitent pas.

Il vaudrait peut-être mieux considérer les itérateurs récursifs comme des itérateurs «récursifs», ils ont la capacité d'être itérés de manière récursive mais simplement itérer sur une instance de l'une de ces classes ne le fera pas. Pour exploiter le comportement récursif, continuez à lire.

RecursiveIteratorIterator

C'est là que le jeu RecursiveIteratorIteratorentre en jeu. Il sait comment appeler les itérateurs "récursibles" de manière à explorer la structure en une boucle normale et plate. Il met en action le comportement récursif. Il fait essentiellement le travail de franchir chacune des valeurs de l'itérateur, de chercher à voir s'il y a des «enfants» dans lesquels rentrer ou non, et d'entrer et de sortir de ces collections d'enfants. Vous collez une instance de RecursiveIteratorIteratordans un foreach, et elle plonge dans la structure pour que vous n'ayez pas à le faire.

Si le RecursiveIteratorIteratorn'était pas utilisé, vous auriez à écrire vos propres boucles récursives pour exploiter le comportement récursif, en comparant les itérateurs "récursibles" hasChildren()et en utilisant getChildren().

Voilà donc un bref aperçu de RecursiveIteratorIterator, en quoi est-ce différent IteratorIterator? Eh bien, vous posez fondamentalement le même genre de question que Quelle est la différence entre un chaton et un arbre? Ce n'est pas parce que les deux apparaissent dans la même encyclopédie (ou manuel, pour les itérateurs) que vous devriez être confus entre les deux.

ItérateurIterator

Le travail du IteratorIteratorest de prendre n'importe quel Traversableobjet et de l'envelopper de telle sorte qu'il satisfasse l' Iteratorinterface. Une utilisation pour cela est de pouvoir ensuite appliquer un comportement spécifique à l'itérateur sur l'objet non-itérateur.

Pour donner un exemple pratique, la DatePeriodclasse est Traversablemais pas un Iterator. En tant que tel, nous pouvons boucler ses valeurs avec foreach()mais ne pouvons pas faire d'autres choses que nous ferions habituellement avec un itérateur, comme le filtrage.

TÂCHE : Boucle les lundis, mercredis et vendredis des quatre prochaines semaines.

Oui, c'est trivial en foreachpassant par- dessus DatePeriodet 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 de code ci-dessus ne fonctionnera pas car le CallbackFilterIteratorattend une instance d'une classe qui implémente l' Iteratorinterface, ce qui DatePeriodne fonctionne pas. Cependant, comme c'est le cas, Traversablenous pouvons facilement satisfaire cette exigence en utilisant IteratorIterator.

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

Comme vous pouvez le voir, cela n'a rien à voir avec l'itération sur les classes d'itérateur ni la récursivité, et c'est là que réside la différence entre IteratorIteratoret RecursiveIteratorIterator.

Résumé

RecursiveIteraratorIteratorest pour itérer sur un RecursiveIterator(itérateur "récursible"), en exploitant le comportement récursif disponible.

IteratorIteratorest pour appliquer un Iteratorcomportement à des Traversableobjets non itérateurs .

salathe
la source
N'est-ce pas IteratorIteratorsimplement le type standard de parcours d'ordre linéaire pour les Traversableobjets? Celles qui pourraient être utilisées sans elles, foreachtelles quelles ? Et encore plus, n'est-ce pas RecursiveIterator toujours un Traversableet donc pas seulement IteratorIteratormais aussi RecursiveIteratorIteratortoujours "pour appliquer un Iteratorcomportement à des objets non itérateurs, Traversables" ? (Je dirais maintenant foreachapplique le type d'itération via l'objet iterator sur les objets conteneur qui implémentent une interface de type itérateur, donc ce sont toujours des objets iterator-container-objects Traversable)
hakre
Comme l'indique ma réponse, IteratorIteratorest une classe qui consiste à envelopper des Traversableobjets dans un fichier Iterator. Rien de plus . Vous semblez appliquer le terme plus généralement.
salathe le
Réponse apparemment informative. Une question, RecursiveIteratorIterator n'emballerait-il pas également les objets afin qu'ils aient également accès au comportement Iterator? La seule différence entre les deux serait que le RecursiveIteratorIterator peut explorer vers le bas, alors que le IteratorIterator ne le peut pas?
Mike Purcell
@salathe, savez-vous pourquoi l'itérateur récursif (RecursiveDirectoryIterator) n'implémente pas le comportement hasChildren (), getChildren ()?
anru
7
+1 pour dire "récursible". Le nom m'a induit Recursiveen erreur pendant longtemps car le in RecursiveIteratorimplique un comportement, alors qu'un nom plus approprié aurait été celui qui décrit la capacité, comme RecursibleIterator.
chèvre
0

Lorsqu'il est utilisé avec iterator_to_array(), RecursiveIteratorIteratorparcourra récursivement le tableau pour trouver toutes les valeurs. Cela signifie qu'il aplatira le tableau d'origine.

IteratorIterator conservera la structure hiérarchique d'origine.

Cet exemple vous montrera clairement la différence:

$array = array(
               'ford',
               'model' => 'F150',
               'color' => 'blue', 
               'options' => array('radio' => 'satellite')
               );

$recursiveIterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($array));
var_dump(iterator_to_array($recursiveIterator, true));

$iterator = new IteratorIterator(new ArrayIterator($array));
var_dump(iterator_to_array($iterator,true));
Tchoupi
la source
C'est totalement trompeur. new IteratorIterator(new ArrayIterator($array))équivaut à new ArrayIterator($array), c'est-à-dire que l'extérieur IteratorIteratorne fait rien. De plus, l'aplatissement de la sortie n'a rien à voir avec iterator_to_array- il convertit simplement l'itérateur en tableau. L'aplatissement est une propriété du chemin RecursiveArrayIteratorparcouru par son itérateur interne.
Questions Quolonel
0

RecursiveDirectoryIterator il affiche le chemin d'accès complet et pas seulement le nom du fichier. Le reste a l'air bien. C'est parce que les noms de fichiers sont générés par SplFileInfo. Celles-ci devraient être affichées 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";
    }
}

production:

tree
  dirA
   dirB
    fileD
   fileB
   fileC
  fileA
javad shariaty
la source