Finder: Ricerca di file

Avete bisogno di trovare i file che corrispondono a una determinata maschera? Il Finder può aiutarvi. È uno strumento versatile e veloce per navigare nella struttura delle directory.

Installazione:

composer require nette/utils

Gli esempi presuppongono la creazione di un alias:

use Nette\Utils\Finder;

Utilizzando

Per prima cosa, vediamo come utilizzare Nette\Utils\Finder per elencare i nomi dei file con estensione .txt e .md nella directory corrente:

foreach (Finder::findFiles(['*.txt', '*.md']) as $name => $file) {
	echo $file;
}

La directory predefinita per la ricerca è quella corrente, ma è possibile cambiarla utilizzando i metodi in() o from(). La variabile $file è un'istanza della classe FileInfo con molti metodi utili. La chiave $name contiene il percorso del file come stringa.

Cosa cercare?

Oltre al metodo findFiles(), esistono anche findDirectories(), che cerca solo nelle directory, e find(), che cerca in entrambe. Questi metodi sono statici, quindi possono essere richiamati senza creare un'istanza. Il parametro mask è opzionale; se non viene specificato, viene cercato tutto.

foreach (Finder::find() as $file) {
	echo $file; // ora tutti i file e le directory sono elencati
}

Si possono usare i metodi files() e directories() per aggiungere altri elementi da ricercare. I metodi possono essere richiamati ripetutamente e si può fornire un array di maschere come parametro:

Finder::findDirectories('vendor') // tutte le directory
	->files(['*.php', '*.phpt']); // più tutti i file PHP

Un'alternativa ai metodi statici è creare un'istanza con new Finder (l'oggetto fresco creato in questo modo non cerca nulla) e specificare cosa cercare con files() e directories():

(new Finder)
	->directories() // tutte le directory
	->files('*.php'); // più tutti i file PHP

Si possono usare i caratteri jolly *, **, ? and [...] nella maschera. Si possono anche specificare le directory, ad esempio src/*.php cercherà tutti i file PHP nella directory src.

La directory di ricerca predefinita è la directory corrente. È possibile modificarla utilizzando i metodi in() e from(). Come si può vedere dai nomi dei metodi, in() cerca solo nella directory corrente, mentre from() cerca anche nelle sue sottodirectory (in modo ricorsivo). Se si desidera effettuare una ricerca ricorsiva nella directory corrente, si può usare from('.').

Questi metodi possono essere richiamati più volte o si possono passare percorsi multipli come array; i file verranno cercati in tutte le directory. Se una delle directory non esiste, viene lanciato Nette\UnexpectedValueException.

Finder::findFiles('*.php')
	->in(['src', 'tests']) // cerca direttamente in src/ e tests/
	->from('vendor'); // cerca anche nelle sottodirectory di vendor/

I percorsi relativi sono relativi alla directory corrente. Naturalmente, possono essere specificati anche percorsi assoluti:

Finder::findFiles('*.php')
	->in('/var/www/html');

Caratteri jolly *, **, ? can be used in the path. For example, you can use the path src/*/*.php per cercare tutti i file PHP nelle directory di secondo livello della directory src. Il carattere **, chiamato globstar, è un potente asso nella manica perché permette di cercare anche nelle sottodirectory: usare src/**/tests/*.php per cercare tutti i file PHP nella directory tests che si trovano in src o in una qualsiasi delle sue sottodirectory.

D'altra parte, i caratteri jolly [...] non sono supportati nel percorso, cioè non hanno un significato speciale per evitare comportamenti indesiderati nel caso in cui si cerchi, ad esempio, in(__DIR__) e per caso appaia il carattere [] nel percorso.

Quando si effettua una ricerca approfondita di file e directory, viene restituita prima la directory madre e poi i file in essa contenuti, cosa che può essere invertita con childFirst().

I caratteri jolly

È possibile utilizzare diversi caratteri speciali nella maschera:

  • * – replaces any number of arbitrary characters (except /)
  • ** – sostituisce un numero qualsiasi di caratteri arbitrari, incluso / (cioè può essere ricercato a più livelli)
  • ? – replaces one arbitrary character (except /)
  • [a-z] – sostituisce un carattere dall'elenco di caratteri tra parentesi quadre
  • [!a-z] – sostituisce un carattere al di fuori dell'elenco di caratteri tra parentesi quadre

Esempi di utilizzo:

  • img/?.png – file con il nome di una sola lettera 0.png, 1.png, x.png, ecc.
  • logs/[0-9][0-9][0-9][0-9]-[01][0-9]-[0-3][0-9].log – file di log nel formato YYYY-MM-DD
  • src/**/tests/* – file nella directory src/tests, src/foo/tests, src/foo/bar/tests e così via.
  • docs/**.md – tutti i file con l'estensione .md in tutte le sottodirectory della directory docs

Escluso

Utilizzate il metodo exclude() per escludere file e directory dalle ricerche. Si specifica una maschera a cui il file non deve corrispondere. Esempio di ricerca dei file *.txt tranne quelli contenenti la lettera X nel nome:

Finder::findFiles('*.txt')
	->exclude('*X*');

Utilizzare exclude() per saltare le sottodirectory sfogliate:

Finder::findFiles('*.php')
	->from($dir)
	->exclude('temp', '.git')

Filtraggio

Il Finder offre diversi metodi per filtrare i risultati (cioè per ridurli). È possibile combinarli e richiamarli ripetutamente.

Utilizzate size() per filtrare in base alla dimensione del file. In questo modo si trovano i file con dimensioni comprese tra 100 e 200 byte:

Finder::findFiles('*.php')
	->size('>=', 100)
	->size('<=', 200);

Il metodo date() filtra in base alla data dell'ultima modifica del file. I valori possono essere assoluti o relativi alla data e all'ora corrente, ad esempio per trovare i file modificati nelle ultime due settimane:

Finder::findFiles('*.php')
	->date('>', '-2 weeks')
	->from($dir)

Entrambe le funzioni comprendono gli operatori >, >=, <, <=, =, !=, <>.

Il Finder consente anche di filtrare i risultati utilizzando funzioni personalizzate. La funzione riceve un oggetto Nette\Utils\FileInfo come parametro e deve restituire true per includere il file nei risultati.

Esempio: ricerca dei file PHP che contengono la stringa Nette (senza distinzione tra maiuscole e minuscole):

Finder::findFiles('*.php')
	->filter(fn($file) => strcasecmp($file->read(), 'Nette') === 0);

Filtro di profondità

Quando si effettua una ricerca ricorsiva, è possibile impostare la profondità massima di crawling utilizzando il metodo limitDepth(). Se si imposta limitDepth(1), vengono strisciate solo le prime sottodirectory, limitDepth(0) disabilita il crawling di profondità e un valore di –1 annulla il limite.

Il Finder consente di utilizzare le proprie funzioni per decidere quale directory inserire durante la navigazione. La funzione riceve un oggetto Nette\Utils\FileInfo come parametro e deve restituire true per entrare nella directory:

Finder::findFiles('*.php')
	->descentFilter($file->getBasename() !== 'temp');

Ordinamento

Il Finder offre anche diverse funzioni per ordinare i risultati.

Il metodo sortByName() ordina i risultati in base al nome del file. L'ordinamento è naturale, cioè gestisce correttamente i numeri nei nomi e restituisce, ad esempio, foo1.txt prima di foo10.txt.

Il Finder consente anche di ordinare utilizzando una funzione personalizzata. Essa prende come parametri due oggetti Nette\Utils\FileInfo e deve restituire il risultato del confronto con l'operatore <=>cioè -1, 0 nebo 1. Ad esempio, ecco come ordinare i file in base alle dimensioni:

$finder->sortBy(fn($a, $b) => $a->getSize() <=> $b->getSize());

Ricerche multiple diverse

Se è necessario trovare più file diversi in posizioni diverse o che soddisfano criteri diversi, utilizzare il metodo append(). Esso restituisce un nuovo oggetto Finder, in modo da poter concatenare le chiamate ai metodi:

($finder = new Finder) // memorizza il primo Finder nella variabile $finder!
	->files('*.php') // cerca i file *.php in src/
	->from('src')
	->append()
	->files('*.md') // in docs/ cerca i file *.md
	->from('docs')
	->append()
	->files('*.json'); // nella cartella corrente cerca i file *.json

In alternativa, si può usare il metodo append() per aggiungere un file specifico (o un array di file). In tal caso, viene restituito lo stesso oggetto Finder:

$finder = Finder::findFiles('*.txt')
	->append(__FILE__);

FileInfo

Nette\Utils\FileInfo è una classe che rappresenta un file o una directory nei risultati della ricerca. È un'estensione della classe SplFileInfo che fornisce informazioni quali la dimensione del file, la data dell'ultima modifica, il nome, il percorso, ecc.

Inoltre, fornisce metodi per la restituzione di percorsi relativi, utili quando si naviga in profondità:

foreach (Finder::findFiles('*.jpg')->from('.') as $file) {
	$absoluteFilePath = $file->getRealPath();
	$relativeFilePath = $file->getRelativePathname();
}

Sono disponibili anche metodi per leggere e scrivere il contenuto di un file:

foreach ($finder as $file) {
    $contents = $file->read();
    // ...
    $file->write($contents);
}

Restituzione dei risultati come array

Come si è visto negli esempi, il Finder implementa l'interfaccia IteratorAggregate, quindi si può usare foreach per sfogliare i risultati. È programmato in modo che i risultati vengano caricati solo durante la navigazione, quindi se si ha un gran numero di file, non si aspetta che vengano letti tutti.

I risultati possono anche essere restituiti come un array di oggetti Nette\Utils\FileInfo, usando il metodo collect(). L'array non è associativo, ma numerico.

$array = $finder->findFiles('*.php')->collect();
versione: 4.0