Questo articolo illustra un usuale compito che si potrebbe avere sperimentato durante lo sviluppo di un’applicazione in PHP: elenchi di file e directory. Si occupa di diverse soluzioni di base e avanzate, ciascuno con i suoi pro e contro. Per primo si presenta tre approcci che utilizzano alcune funzioni di base di PHP e poi si progredisce verso quelli più robusti che fanno uso di iteratori SPL.

Questo articolo descrive i diversi modi per raggiungere lo stesso obiettivo: come recuperare e filtrare i file e le directory in un determinato percorso. Questi sono alcuni punti chiave da ricordare:

  • La funzione glob() è una soluzione one-line e permette il filtraggio, ma non è molto flessibile.
  • La soluzione usando opendir(), readdir(), e closedir() è un po’ prolisso e ha bisogno di un post-filtraggio, ma è più flessibile.
  • La funzione scandir() necessita il post-filtraggio e non ha bisogno di gestire lo handle.
  • Se si desidera utilizzare un approccio OOP, è necessario utilizzare la libreria SPL. Inoltre è possibile estendere le classi in base alle proprie esigenze.
  • Mentre il GlobIterator è in grado di fare pre-filtraggio, gli altri come RegexIterator possono fare lo stesso in modo confortevole.

Ai fini della discussione, supponiamo una struttura di directory simile a quella riportata di seguito:

\admin
\user
  |---documenti.txt
  |---dati.dat
  |---stile.css
|---articolo.txt
|---admin.dat
|---script.php
|---test.dat
|---text.txt
Le soluzioni di base
La prima serie di approcci dimostrano l’uso delle funzioni glob(), una combinazione delle funzioni opendir(), readdir() e closedir(), e la funzione scandir().
Usando glob()
La prima funzione in discussione è glob(), che ci permette di eseguire una ricerca di percorsi utilizzando i caratteri jolly, comuni nelle shell note. La funzione ha due parametri:

  • $pattern (obbligatorio): il modello di ricerca
  • $flags (opzionale): alcuni flag vengono elencati nella documentazione ufficiale

Vediamo alcuni esempi! Per eseguire una ricerca nella directory su tutti i file e directory che terminano in .txt, è necessario scrivere:

<?php

$filelist = glob("*.txt");

?>
Se si visualizza filelist $filelist, l’uscita sarà:
array (
  0 => 'articolo.txt',
  1 => 'text.txt'
)
Se si vuole un elenco di file e directory che iniziano con te, il codice da scrivere è il seguente:
<?php

$filelist = glob("te*");

?>
L’uscita sarà:
array (
  0 => 'test.dat'
  1 => 'text.txt'
)
Per ottenere un elenco di directory che contiene ad, il codice è:
<?php

$filelist = glob("*ad*", GLOB_ONLYDIR);

?>
In quest’ultimo esempio, l’uscita è:
array (
  0 => 'admin'
)
L’ultimo esempio fa uso della costante GLOB_ONLYDIR come secondo parametro opzionale. Come si può vedere, il file chiamato admin.dat è escluso per questo motivo. Anche se la funzione glob() è facile da usare, ma a volte non è così flessibile. Per esempio, non ha un flag per recuperare solo i file (e non le directory) che corrispondono a un dato modello.
Usando opendir() e readdir()
Il secondo approccio serve per leggere i file e le directory e coinvolge le funzioni opendir(), readdir(), and closedir().
opendir() apre la directory e restituisce lo handle della connessione. Una volta che lo handle è recuperato, è possibile utilizzare readdir(). Ad ogni chiamata, questa funzione darà il nome del file successivo o directory all’interno di una directory aperta. Quando tutti i nomi sono stati recuperati, la funzione restituisce il valore false. Per chiudere lo handle della connessione si utilizza closedir().
A differenza glob(), questo approccio è un po’ più complicato dal momento che non si dispone di parametri che consentono di filtrare i file e le directory restituiti. Si deve effettuare un post-filtraggio per ottenere quello che si cerca.
In parallelo con la funzione glob(), l’esempio seguente recupera un elenco di tutti i file e le directory che iniziano con “te”:
<?php

$filelist = array();
if ($handle = opendir(".")) {
    while ($entry = readdir($handle)) {
        if (strpos($entry, "te") === 0) {
            $filelist[] = $entry;
        }
    }
    closedir($handle);
}

?>
L’uscita è la stessa dell’esempio precedente.
Ma se si esegue il codice sopra riportato, nell’uscita del valore di $entry vedrete che a volte contiene alcune strane entrate come: “..” e “.“. Si tratta di due directory virtuali che troverete in ogni directory del file system. Rappresentano la directory corrente e la directory principale, rispettivamente.
Il secondo esempio mostra come recuperare solo i file contenuti in un determinato percorso.
<?php

$filelist = array();
if ($handle = opendir(".")) {
    while ($entry = readdir($handle)) {
        if (is_file($entry)) {
            $filelist[] = $entry;
        }
    }
    closedir($handle);
}

?>
Come si può immaginare, il codice di sopra produce la seguente uscita:
array (
  0 => 'articolo.txt',
  1 => 'admin.dat',
  2 => 'script.php',
  3 => 'test.dat',
  4 => 'text.txt'
)
Usando scandir()
E infine, la funzione scandir(). Ha un solo parametro obbligatorio: il percorso di lettura. Il valore restituito è un array di file e di directory contenuti nel percorso. Proprio come l’ultima soluzione, per recuperare un sottoinsieme di file e directory, dovete fare un post-filtraggio. D’altra parte, come si può vedere, questa soluzione è più concisa e non c’è bisogno di gestire lo handle della connessione.
Questo esempio mostra come recuperare i file e le directory che iniziano con la stringa di “te“:
<?php

$entries = scandir(".");
$filelist = array();
foreach($entries as $entry) {
    if (strpos($entry, "te") === 0) {
        $filelist[] = $entry;
    }
}

?>
Con gli iteratori SPL
Ora parliamo di alcuni iteratori SPL. Ma prima di andare in profondità sul loro uso, s’indica rapidamente cos’è la biblioteca SPL. L’SPL fornisce una serie di classi per le strutture dati object-oriented, iteratori, gestori di file, e altre funzionalità.
Uno dei vantaggi è che iteratori sono classi e quindi è possibile estenderle in base alle proprie esigenze. Un altro vantaggio è che hanno metodi nativi che sono veramente utili per realizzare molti compiti in un solo ambito. Prendete come esempio l’uso di FilesystemIterator e readdir(), entrambi saranno utilizzati in un ciclo, ma durante l’utilizzo readdir() la sua entrata non sarà altro che una stringa, utilizzando invece il FilesystemIterator si dispone di un oggetto che può fornire un sacco di informazioni su questo file o directory (dimensione, il proprietario, i permessi e così via).
Naturalmente, PHP è in grado di fornire le stesse informazioni utilizzando funzioni come filesize() e fileowner(), ma PHP5 ha trasformato il suo approccio alla programmazione orientata agli oggetti. Quindi, in conclusione, il consiglio che danno è quello di seguire le nuove pratiche del linguaggio. Nel caso abbiate bisogno di ulteriori informazioni di carattere generale su iteratori SPL, date un’occhiata all’SPL Iterators Class Tree.
Come scritto nell’introduzione, verrà illustrato l’uso di FilesystemIterator, RecursiveDirectoryIterator e GlobIterator. Il primo di essi eredita da DirectoryIterator mentre gli altri ereditano da FilesystemIterator. Tutti hanno lo stesso costruttore, che ha solo due parametri:

  • $path (obbligatorio): Il percorso del file system da iterare
  • $flags (opzionale): Uno o più flag elencati nella documentazione ufficiale.

Di cosa si differenzia realmente in questi iteratori, è l’approccio che si usa per navigare nel percorso indicato.

Il FilesystemIterator
Utilizzare il FilesystemIterator è abbastanza semplice. Per vederlo in azione, si mostrerà due esempi. Nel primo, si fa la ricerca di tutti i file e le directory che iniziano con la stringa “te”, mentre nel secondo si utilizza un altro iteratore (il RegexIterator), per cercare tutti i file e le directory che contiene alla fine “t.dat” o “t.php”. Il RegexIterator viene utilizzato per filtrare un altro iteratore sulla base di una espressione regolare.
<?php

$iterator = new FilesystemIterator(".");
$filelist = array();
foreach($iterator as $entry) {
    if (strpos($entry->getFilename(), "te") === 0) {
        $filelist[] = $entry->getFilename();
    }
}

?>
Con il codice di sopra, il risultato è lo stesso dell’esempio precedente.
Il secondo esempio, che utilizza il RegexIterator, è il seguente:
<?php

$iterator = new FilesystemIterator(".");
$filter = new RegexIterator($iterator, '/t\.(php|dat)$/');
$filelist = array();
foreach($filter as $entry) {
    $filelist[] = $entry->getFilename();
}

?>
In questo caso l’uscita è:
array (
  0 => 'script.php',
  1 => 'test.dat'
)
Il RecursiveDirectoryIterator
Il RecursiveDirectoryIterator fornisce un’interfaccia per l’iterazione sulle directory del filesystem ricorsivamente. Grazie al suo scopo, ha alcuni metodi utili come getChildren() e hasChildren(), che restituiscono un iteratore per la voce corrente, se si tratta di una directory o se la voce corrente è una directory. Per vedere sia RecursiveDirectoryIterator e getChildren() in azione, proviamo a riscrivere l’ultimo esempio per ottenere lo stesso risultato.
<?php

$iterator = new RecursiveDirectoryIterator('.');
$filter = new RegexIterator($iterator->getChildren(), '/t\.(php|dat)$/');
$filelist = array();
foreach($filter as $entry) {
    $filelist[] = $entry->getFilename();
}
?>
Il GlobIterator
Il GlobIterator consente di scorrere il file system in modo simile alla funzione glob(). Così il primo parametro può includere caratteri jolly. Il codice seguente mostra il solito esempio con l’uso del GlobIterator.
<?php

$iterator = new GlobIterator("te*");
$filelist = array();
foreach($iterator as $entry) {
    $filelist[] = $entry->getFilename();
}

?>
Similari
Iteratori in PHP
12% Php
Iterator è uno dei più noti modelli di progettazione grazie al suo utilizzo in diversi linguaggi di programmazione come Python, Java, C++ e PHP. Fondamentalmente ciò che questo modello propone è di trasferire la responsabi…
Restrizioni per caricare un file con PHP
7% Php
Dalle foto di famiglia ai documenti aziendali, file uploads potenzia molte delle applicazioni web più importanti. [expand title=”L’oggetto $_FILES” startwrap=”” endwrap=”” excerpt=”⤽” swapexcerpt=”” expanded=”true” trigcla…
Parsing XML con SimpleXML
7% Php
Analizzare l’XML significa essenzialmente la navigazione attraverso un documento in formato XML per la restituzione dei dati pertinenti. Un numero crescente di servizi web restituiscono i dati in formato JSON, ma un gran n…
Metodi magici e costanti predefinite in PHP
6% Php
PHP fornisce un insieme di costanti predefinite e metodi magici per i nostri programmi. A differenza delle normali costanti i quali si impostano con define(), il valore delle costanti predefinite o speciali dipendono da do…
Installare Python e Django su Windows
6% Django
Quando ci riferiamo allo sviluppo web con Python, la prima cosa che viene in mente è usare un qualche framework. Il più famoso e utilizzato da tutti è il Django, ma non è l’unico. Ci sono Pylons, Grok, TurboGears e Zope: t…