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()
, eclosedir()
è 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 comeRegexIterator
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
glob()
, una combinazione delle funzioni opendir()
, readdir()
e closedir()
, e la funzione scandir()
.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");
?>
$filelist
, l’uscita sarà:
array (
0 => 'articolo.txt',
1 => 'text.txt'
)
<?php
$filelist = glob("te*");
?>
array (
0 => 'test.dat'
1 => 'text.txt'
)
<?php
$filelist = glob("*ad*", GLOB_ONLYDIR);
?>
array (
0 => 'admin'
)
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.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);
}
?>
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);
}
?>
array (
0 => 'articolo.txt',
1 => 'admin.dat',
2 => 'script.php',
3 => 'test.dat',
4 => 'text.txt'
)
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;
}
}
?>
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.
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();
}
}
?>
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();
}
?>
array (
0 => 'script.php',
1 => 'test.dat'
)
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();
}
?>
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();
}
?>
Ancora nessun commento