Come funziona RecursiveIteratorIterator in PHP?


88

Come RecursiveIteratorIteratorfunziona?

Il manuale PHP non ha molto di documentato o spiegato. Qual'è la differenza tra IteratorIteratore RecursiveIteratorIterator?


2
c'è un esempio su php.net/manual/en/recursiveiteratoriterator.construct.php e c'è anche un'introduzione su php.net/manual/en/class.iteratoriterator.php - puoi per favore indicare cosa esattamente hai difficoltà a capire . Cosa dovrebbe contenere il Manuale per facilitarne la comprensione?
Gordon

1
Se chiedi come RecursiveIteratorIteratorfunziona, hai già capito come IteratorIteratorfunziona? Voglio dire che è fondamentalmente lo stesso, solo l'interfaccia che viene consumata dai due è diversa. E sei più interessato ad alcuni esempi o vuoi vedere il diff dell'implementazione del codice C sottostante?
hakre

@Gordon non ero sicuro di come il singolo ciclo foreach possa attraversare tutti gli elementi nella struttura ad albero
varuog

@hakra sto ora cercando di studiare tutte le interfacce incorporate così come l'interfaccia spl e l'implementazione dell'iteratore. Ero interessato a sapere come funziona in background con il ciclo forach con alcuni esempi.
varuog

@hakre Sono entrambi molto diversi. IteratorIteratormappe Iteratore IteratorAggregatein un Iterator, dove REcusiveIteratorIteratorviene utilizzato per attraversare in modo ricusivo aRecursiveIterator
Adam

Risposte:


249

RecursiveIteratorIteratorè un attraversamento dell'albero diIterator implementazione concreta . Consente a un programmatore di attraversare un oggetto contenitore che implementa l' interfaccia, vedere Iterator in Wikipedia per i principi generali, i tipi, la semantica e i modelli degli iteratori.RecursiveIterator

A differenza di IteratorIteratorquale sia un Iteratoroggetto di implementazione concreto che attraversa in ordine lineare (e per impostazione predefinita accetta qualsiasi tipo di Traversablenel suo costruttore), il RecursiveIteratorIteratorconsente il ciclo su tutti i nodi in un albero ordinato di oggetti e il suo costruttore prende un file RecursiveIterator.

In breve: RecursiveIteratorIteratorti permette di scorrere un albero, IteratorIteratorti permette di scorrere un elenco. Lo mostro presto con alcuni esempi di codice qui sotto.

Tecnicamente questo funziona rompendo la linearità attraversando tutti i figli di un nodo (se presenti). Questo è possibile perché per definizione tutti i figli di un nodo sono di nuovo a RecursiveIterator. Il livello superiore Iteratorquindi impila internamente i diversi RecursiveIterators in base alla loro profondità e mantiene un puntatore al sub attivo corrente Iteratorper l'attraversamento.

Ciò consente di visitare tutti i nodi di un albero.

I principi di base sono gli stessi di IteratorIterator: Un'interfaccia specifica il tipo di iterazione e la classe iteratore di base è l'implementazione di queste semantiche. Confronta con gli esempi seguenti, per i cicli lineari con foreachte normalmente non pensi molto ai dettagli di implementazione a meno che tu non abbia bisogno di definirne uno nuovo Iterator(ad esempio quando un certo tipo concreto stesso non implementa Traversable).

Per l'attraversamento ricorsivo, a meno che non si utilizzi un predefinito Traversalche ha già un'iterazione trasversale ricorsiva, normalmente è necessario istanziare l' RecursiveIteratorIteratoriterazione esistente o anche scrivere un'iterazione trasversale ricorsiva che è Traversabletua per avere questo tipo di iterazione trasversale foreach.

Suggerimento: probabilmente non hai implementato né l'uno né l'altro, quindi potrebbe essere qualcosa che vale la pena fare per la tua esperienza pratica delle differenze che hanno. Alla fine della risposta trovi un suggerimento fai da te.

Differenze tecniche in breve:

  • Sebbene ne IteratorIteratorprenda qualsiasi Traversableper l'attraversamento lineare, RecursiveIteratorIteratornecessita di un più specifico RecursiveIteratorper eseguire il ciclo su un albero.
  • Dove IteratorIteratorespone la sua Iteratorvia principale getInnerIerator(), RecursiveIteratorIteratorfornisce l'attuale sub-attivo Iteratorsolo tramite quel metodo.
  • Sebbene non IteratorIteratorsia totalmente a conoscenza di nulla come genitore o figli, RecursiveIteratorIteratorsa anche come ottenere e attraversare i bambini.
  • IteratorIteratornon ha bisogno di uno stack di iteratori, RecursiveIteratorIteratorha un tale stack e conosce il subiteratore attivo.
  • Where IteratorIteratorha il suo ordine dovuto alla linearità e nessuna scelta, RecursiveIteratorIteratorha una scelta per un ulteriore attraversamento e deve decidere per ogni nodo (deciso tramite modalità perRecursiveIteratorIterator ).
  • RecursiveIteratorIteratorha più metodi di IteratorIterator.

Per riassumere: RecursiveIteratorè un tipo concreto di iterazione (loop su un albero) che funziona sui propri iteratori, vale a dire RecursiveIterator. Questo è lo stesso principio di base di IteratorIerator, ma il tipo di iterazione è diverso (ordine lineare).

Idealmente puoi anche creare il tuo set. L'unica cosa necessaria è che il tuo iteratore implementa il Traversableche è possibile tramite Iteratoro IteratorAggregate. Quindi puoi usarlo con foreach. Ad esempio una sorta di oggetto di iterazione ricorsiva di attraversamento dell'albero ternario insieme all'interfaccia di iterazione corrispondente per gli oggetti contenitore.


Rivediamo con alcuni esempi di vita reale che non sono così astratti. Tra interfacce, iteratori concreti, oggetti contenitore e semantica di iterazione questa forse non è una cattiva idea.

Prendi un elenco di directory come esempio. Considera di avere il seguente albero di file e directory su disco:

Albero delle directory

Mentre un iteratore con ordine lineare attraversa semplicemente la cartella ei file di primo livello (un singolo elenco di directory), l'iteratore ricorsivo attraversa anche le sottocartelle ed elenca tutte le cartelle ei file (un elenco di directory con elenchi delle sue sottodirectory):

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

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

Puoi facilmente confrontare questo con il IteratorIteratorquale non fa ricorsione per attraversare l'albero delle directory. E il RecursiveIteratorIteratorquale può attraversare l'albero come mostra l'elenco ricorsivo.

All'inizio un esempio molto semplice con un DirectoryIteratorthat implements Traversableche permette foreachdi iterare su di esso:

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

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

L'output esemplare per la struttura della directory sopra è quindi:

[tree]
  .
  ..
  dirA
  fileA

Come vedi questo non sta ancora usando IteratorIteratoro RecursiveIteratorIterator. Invece è solo usando foreachche opera Traversablesull'interfaccia.

Poiché foreachper impostazione predefinita conosce solo il tipo di iterazione denominato ordine lineare, potremmo voler specificare esplicitamente il tipo di iterazione. A prima vista potrebbe sembrare troppo prolisso, ma a scopo dimostrativo (e per fare la differenza RecursiveIteratorIteratorpiù visibile in seguito), specifichiamo il tipo lineare di iterazione specificando esplicitamente il IteratorIteratortipo di iterazione per l'elenco delle directory:

$files = new IteratorIterator($dir);

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

Questo esempio è quasi identico al primo, la differenza è che $filesora è un IteratorIteratortipo di iterazione per Traversable $dir:

$files = new IteratorIterator($dir);

Come al solito, l'atto di iterazione viene eseguito da foreach:

foreach ($files as $file) {

L'output è esattamente lo stesso. Allora cosa c'è di diverso? Diverso è l'oggetto utilizzato all'interno di foreach. Nel primo esempio è un DirectoryIteratornel secondo esempio è il IteratorIterator. Questo mostra la flessibilità che hanno gli iteratori: puoi sostituirli tra loro, il codice all'interno foreachcontinua a funzionare come previsto.

Iniziamo per ottenere l'intero elenco, comprese le sottodirectory.

Poiché ora abbiamo specificato il tipo di iterazione, consideriamo di cambiarlo in un altro tipo di iterazione.

Sappiamo che ora dobbiamo attraversare l'intero albero, non solo il primo livello. Per avere che il lavoro con un semplice foreachabbiamo bisogno di un diverso tipo di iteratore: RecursiveIteratorIterator. E quello può solo iterare su oggetti contenitore che hanno l' RecursiveIteratorinterfaccia .

L'interfaccia è un contratto. Qualsiasi classe che lo implementa può essere utilizzata insieme a RecursiveIteratorIterator. Un esempio di una tale classe è il RecursiveDirectoryIterator, che è qualcosa come la variante ricorsiva di DirectoryIterator.

Vediamo un primo esempio di codice prima di scrivere qualsiasi altra frase con la parola I:

$dir  = new RecursiveDirectoryIterator($path);

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

Questo terzo esempio è quasi identico al primo, tuttavia crea un output diverso:

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

Ok, non è così diverso, il nome del file ora contiene il percorso davanti, ma anche il resto sembra simile.

Come mostra l'esempio, anche l'oggetto directory implementa già l' RecursiveIteratorinterfaccia, questo non è ancora sufficiente per far foreachattraversare l'intero albero delle directory. È qui che RecursiveIteratorIteratorentra in azione. L'esempio 4 mostra come:

$files = new RecursiveIteratorIterator($dir);

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

Usare il RecursiveIteratorIteratorinvece del solo $diroggetto precedente farà foreachattraversare tutti i file e le directory in modo ricorsivo. Questo quindi elenca tutti i file, poiché il tipo di iterazione dell'oggetto è stato specificato ora:

[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

Questo dovrebbe già dimostrare la differenza tra attraversamento piatto e albero. È in RecursiveIteratorIteratorgrado di attraversare qualsiasi struttura ad albero come un elenco di elementi. Poiché ci sono più informazioni (come il livello in cui avviene attualmente l'iterazione), è possibile accedere all'oggetto iteratore mentre si itera su di esso e ad esempio indentare l'output:

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

E output dell'Esempio 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

Sicuramente questo non vince un concorso di bellezza, ma mostra che con l'iteratore ricorsivo sono disponibili più informazioni oltre all'ordine lineare di chiave e valore . Anche foreachsolo può esprimere questo tipo di linearità, l'accesso all'iteratore stesso permette di ottenere maggiori informazioni.

Simile alle meta-informazioni ci sono anche diversi modi possibili per attraversare l'albero e quindi ordinare l'output. Questa è la modalità diRecursiveIteratorIterator e può essere impostata con il costruttore.

Il prossimo esempio dirà RecursiveDirectoryIteratora di rimuovere le voci di punto ( .e ..) poiché non ne abbiamo bisogno. Ma anche la modalità di ricorsione verrà modificata per prendere l'elemento genitore (la sottodirectory) prima ( SELF_FIRST) prima dei figli (i file e le sottodirectory nella sottodirectory):

$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";
}

L'output ora mostra le voci della sottodirectory correttamente elencate, se si confronta con l'output precedente quelle non c'erano:

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

La modalità di ricorsione controlla quindi cosa e quando viene restituito un brach o una foglia nell'albero, per l'esempio di directory:

  • LEAVES_ONLY (impostazione predefinita): elenca solo i file, nessuna directory.
  • SELF_FIRST (sopra): elenca la directory e poi i file al suo interno.
  • CHILD_FIRST (senza esempio): elenca prima i file nella sottodirectory, quindi la directory.

Risultato dell'esempio 5 con le altre due modalità:

  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

Quando lo confronti con l'attraversamento standard, tutte queste cose non sono disponibili. L'iterazione ricorsiva quindi è un po 'più complessa quando è necessario avvolgere la testa attorno ad essa, tuttavia è facile da usare perché si comporta proprio come un iteratore, lo metti in un foreache fatto.

Penso che questi siano esempi sufficienti per una risposta. Puoi trovare il codice sorgente completo e un esempio per visualizzare alberi ascii di bell'aspetto in questo riassunto: https://gist.github.com/3599532

Fai da te: crea il RecursiveTreeIteratorlavoro riga per riga.

L'esempio 5 ha dimostrato che sono disponibili meta-informazioni sullo stato dell'iteratore. Tuttavia, questo è stato volutamente dimostrata all'interno della foreachiterazione. Nella vita reale questo appartiene naturalmente al RecursiveIterator.

Un esempio migliore è il RecursiveTreeIterator, si occupa del rientro, del prefisso e così via. Vedere il seguente frammento di codice:

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

Il RecursiveTreeIteratorè destinato alla linea di lavoro per riga, l'uscita è abbastanza semplice con un piccolo problema:

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

Quando viene utilizzato in combinazione con a RecursiveDirectoryIterator, visualizza l'intero percorso e non solo il nome del file. Il resto sembra buono. Questo perché i nomi dei file vengono generati da SplFileInfo. Quelli dovrebbero essere visualizzati come nome di base invece. L'output desiderato è il seguente:

/// Solved ///

[tree]
  dirA
   dirB
    fileD
   fileB
   fileC
  fileA

Crea una classe decoratore che può essere utilizzata al RecursiveTreeIteratorposto di RecursiveDirectoryIterator. Dovrebbe fornire il nome di base della corrente SplFileInfoinvece del percorso. Il frammento di codice finale potrebbe quindi essere simile a:

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

Questi frammenti, inclusi, $unicodeTreePrefixfanno parte dell'essenza nell'Appendice: Fai da te: crea il RecursiveTreeIteratorlavoro linea per linea. .


Non risponde alle domande poste, contiene errori fattuali e manca di punti fondamentali quando si passa alla personalizzazione dell'iterazione. Tutto sommato, sembra un misero tentativo di ottenere la ricompensa su un argomento di cui non sai molto o, se lo sai, non puoi distillare in una risposta alle domande poste.
Salathe

2
Bene, il che non risponde alla mia domanda sul "perché", fai solo più parole senza dire molto. Forse inizi con effettivamente un errore che conta? Indicalo, non tenerlo segreto.
hakre

La prima frase non è corretta: "RecursiveIteratorIterator è un IteratorIterator che supporta ...", questo non è vero.
Salathe

1
@salathe: grazie per il tuo feedback. Ho modificato la risposta per affrontarla. La prima frase infatti era sbagliata e incompleta. Ho ancora tralasciato i dettagli concreti di implementazione RecursiveIteratorIteratorperché questo è in comune con altri tipi, ma ho fornito alcune informazioni tecniche su come funziona effettivamente. Gli esempi credo mostrino bene le differenze: il tipo di iterazione è la differenza principale tra i due. Non ho idea se acquisti il ​​tipo di iterazione che conio in modo leggermente diverso, ma IMHO non è facile con i tipi di iterazione semantici.
hakre

1
La prima parte è leggermente migliorata, ma una volta che inizi a passare agli esempi, vira ancora in imprecisioni fattuali. Se ritagliassi la risposta alla regola orizzontale, sarebbe molto migliorata.
Salathe

31

Qual è la differenza tra IteratorIteratore RecursiveIteratorIterator?

Per comprendere la differenza tra questi due iteratori, è innanzitutto necessario comprendere un po 'le convenzioni di denominazione utilizzate e cosa si intende per iteratori "ricorsivi".

Iteratori ricorsivi e non ricorsivi

PHP ha iteratori non "ricorsivi", come ArrayIteratore FilesystemIterator. Esistono anche iteratori "ricorsivi" come RecursiveArrayIteratore RecursiveDirectoryIterator. Questi ultimi hanno metodi che consentono loro di essere approfonditi, i primi no.

Quando le istanze di questi iteratori vengono ripetute da sole, anche quelle ricorsive, i valori provengono solo dal livello "superiore" anche se si ripetono su un array annidato o una directory con sottodirectory.

Gli iteratori ricorsivi implementano il comportamento ricorsivo (tramite hasChildren(), getChildren()) ma non lo sfruttano .

Potrebbe essere meglio pensare agli iteratori ricorsivi come iteratori "ricorsibili", hanno la capacità di essere iterati in modo ricorsivo ma semplicemente iterare su un'istanza di una di queste classi non lo farà. Per sfruttare il comportamento ricorsivo, continua a leggere.

RecursiveIteratorIterator

È qui che RecursiveIteratorIteratorentra in gioco. Ha la conoscenza di come chiamare gli iteratori "ricorsibili" in modo tale da approfondire la struttura in un ciclo normale e piatto. Mette in azione il comportamento ricorsivo. Svolge essenzialmente il lavoro di scavalcare ciascuno dei valori nell'iteratore, cercando di vedere se ci sono "figli" in cui ricorrere o meno, ed entrare e uscire da quelle raccolte di bambini. Metti un'istanza di RecursiveIteratorIteratorin un foreach e si tuffa nella struttura in modo che tu non debba farlo.

Se RecursiveIteratorIteratornon fosse stato usato, dovresti scrivere i tuoi cicli ricorsivi per sfruttare il comportamento ricorsivo, confrontandoti con l'iteratore "ricorsivo" hasChildren()e usando getChildren().

Quindi questa è una breve panoramica di RecursiveIteratorIterator, in cosa è diverso da IteratorIterator? Bene, in pratica stai facendo lo stesso tipo di domanda di Qual è la differenza tra un gattino e un albero? Solo perché entrambi appaiono nella stessa enciclopedia (o manuale, per gli iteratori) non significa che dovresti confonderti tra i due.

IteratorIterator

Il compito di IteratorIteratorè quello di prendere qualsiasi Traversableoggetto e avvolgerlo in modo tale che soddisfi l' Iteratorinterfaccia. Un utilizzo per questo è quindi essere in grado di applicare un comportamento specifico dell'iteratore sull'oggetto non iteratore.

Per fare un esempio pratico, la DatePeriodclasse è Traversablema non un file Iterator. In quanto tale, possiamo eseguire il ciclo sui suoi valori con foreach()ma non possiamo fare altre cose che normalmente faremmo con un iteratore, come il filtraggio.

COMPITO : Loop nei lunedì, mercoledì e venerdì delle prossime quattro settimane.

Sì, questo è banale foreachpassando sopra DatePeriode usando un if()all'interno del ciclo; ma non è questo il punto di questo esempio!

$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) {  }

Lo snippet di cui sopra non funzionerà perché si CallbackFilterIteratoraspetta un'istanza di una classe che implementa l' Iteratorinterfaccia, che DatePeriodnon lo fa. Tuttavia, poiché lo è Traversable, possiamo facilmente soddisfare tale requisito utilizzando IteratorIterator.

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

Come puoi vedere, questo non ha nulla a che fare con l'iterazione su classi iteratori né con la ricorsione, e qui sta la differenza tra IteratorIteratore RecursiveIteratorIterator.

Sommario

RecursiveIteraratorIteratorserve per iterare su un RecursiveIterator(iteratore "ricorsibile"), sfruttando il comportamento ricorsivo disponibile.

IteratorIteratorserve per applicare il Iteratorcomportamento a Traversableoggetti non iteratori .


Non è IteratorIteratorsolo il tipo standard di attraversamento dell'ordine lineare per gli Traversableoggetti? Quelli che potrebbero essere usati senza di esso solo foreachcom'è? E ancora di più, non è RecursiveIterator sempre un Traversablee quindi non solo IteratorIteratorma anche RecursiveIteratorIteratorsempre "per applicare Iteratorcomportamenti a oggetti non iteratori, Traversable" ? (Ora direi che foreachapplica il tipo di iterazione tramite l'oggetto iteratore su oggetti contenitore che implementano un'interfaccia di tipo iteratore, quindi questi sono oggetti-contenitore-iteratore, sempre Traversable)
hakre

Come afferma la mia risposta, IteratorIteratorè una classe che riguarda il wrapping di Traversableoggetti in un file Iterator. Niente di più . Sembra che tu stia applicando il termine in modo più generale.
Salathe

Risposta apparentemente informativa. Una domanda, RecursiveIteratorIterator non avvolgerebbe anche gli oggetti in modo che abbiano anche accesso al comportamento di Iterator? L'unica differenza tra i due sarebbe che RecursiveIteratorIterator può eseguire il drill-down, mentre IteratorIterator non può?
Mike Purcell,

@salathe, sai perché l'iteratore ricorsivo (RecursiveDirectoryIterator) non implementa il comportamento hasChildren (), getChildren ()?
anru

7
+1 per aver detto "ricorsivo". Il nome mi ha portato fuori strada per molto tempo perché Recursivein RecursiveIteratorimplica comportamento, mentre un nome più adatto sarebbe stato quello che descrive capacità, come RecursibleIterator.
capra

0

Se usato con iterator_to_array(), RecursiveIteratorIteratorpercorrerà in modo ricorsivo l'array per trovare tutti i valori. Significa che appiattirà l'array originale.

IteratorIterator manterrà la struttura gerarchica originale.

Questo esempio ti mostrerà chiaramente la differenza:

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

Questo è totalmente fuorviante. new IteratorIterator(new ArrayIterator($array))è equivalente a new ArrayIterator($array), cioè, l'esterno IteratorIteratornon fa nulla. Inoltre, l'appiattimento dell'output non ha nulla a che fare con iterator_to_array: converte semplicemente l'iteratore in un array. L'appiattimento è una proprietà del modo in cui RecursiveArrayIteratorpercorre il suo iteratore interno.
Domande Quolonel

0

RecursiveDirectoryIterator mostra l'intero percorso e non solo il nome del file. Il resto sembra buono. Questo perché i nomi dei file vengono generati da SplFileInfo. Quelli dovrebbero invece essere visualizzati come nome di base. L'output desiderato è il seguente:

$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";
    }
}

produzione:

tree
  dirA
   dirB
    fileD
   fileB
   fileC
  fileA
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.