Finder: ricerca di file

Hai bisogno di trovare file che corrispondano a una certa maschera? Finder ti aiuterà in questo. È uno strumento versatile e veloce per attraversare la struttura delle directory.

Installazione:

composer require nette/utils

Gli esempi presuppongono la creazione di un alias:

use Nette\Utils\Finder;

Utilizzo

Innanzitutto, vediamo come puoi usare Nette\Utils\Finder per elencare i nomi dei file con estensioni .txt e .md nella directory corrente:

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

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

Cosa cercare?

Oltre al metodo findFiles(), c'è anche findDirectories(), che cerca solo directory, e find(), che cerca entrambi. Questi metodi sono statici, quindi possono essere chiamati senza creare un'istanza. Il parametro con la maschera è opzionale; se non lo specifichi, verrà cercato tutto.

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

Usando i metodi files() e directories(), puoi specificare cos'altro cercare. I metodi possono essere chiamati ripetutamente e puoi anche specificare 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 usando new Finder (un oggetto appena creato in questo modo non cerca nulla) e specificare cosa cercare usando files() e directories():

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

Nella maschera puoi usare i caratteri jolly *, **, ? e [...]. Puoi persino specificare directory, ad esempio src/*.php cercherà tutti i file PHP nella directory src.

Anche i link simbolici sono considerati directory o file.

Dove cercare?

La directory predefinita per la ricerca è la directory corrente. Puoi cambiarla usando i metodi in() e from(). Come suggeriscono i nomi dei metodi, in() cerca solo nella directory specificata, mentre from() cerca anche nelle sue sottodirectory (ricorsivamente). Se vuoi cercare ricorsivamente nella directory corrente, puoi usare from('.').

Questi metodi possono essere chiamati più volte o puoi passare loro più percorsi come array; i file verranno quindi cercati in tutte le directory. Se una delle directory non esiste, verrà lanciata un'eccezione 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, è possibile specificare anche percorsi assoluti:

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

È possibile utilizzare i caratteri jolly caratteri jolly *, **, ? nel percorso. Ad esempio, puoi usare il percorso src/*/*.php per cercare tutti i file PHP nelle directory di secondo livello nella directory src. Il carattere **, chiamato globstar, è un potente asso nella manica perché permette di cercare anche nelle sottodirectory: usando src/**/tests/*.php cerchi tutti i file PHP nella directory tests situata in src o in una qualsiasi delle sue sottodirectory.

Al contrario, 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 casualmente nel percorso compaiano i caratteri [].

Durante la ricerca approfondita di file e directory, viene restituita prima la directory padre e solo dopo i file in essa contenuti, il che può essere invertito usando childFirst().

Caratteri jolly

Nella maschera puoi usare diversi caratteri speciali:

  • * – sostituisce un numero qualsiasi di caratteri qualsiasi (eccetto /)
  • ** – sostituisce un numero qualsiasi di caratteri qualsiasi incluso / (cioè è possibile cercare a più livelli)
  • ? – sostituisce un carattere qualsiasi (eccetto /)
  • [a-z] – sostituisce un carattere dall'elenco di caratteri tra parentesi quadre
  • [!a-z] – sostituisce un carattere non presente nell'elenco di caratteri tra parentesi quadre

Esempi di utilizzo:

  • img/?.png – file con un 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 – 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 estensione .md in tutte le sottodirectory della directory docs

Esclusione

Usando il metodo exclude(), è possibile escludere file e directory dalla ricerca. Specifichi una maschera a cui il file non deve corrispondere. Esempio di ricerca di file *.txt eccetto quelli che contengono la lettera X nel nome:

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

Per saltare le sottodirectory attraversate, usa exclude():

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

Filtraggio

Finder offre diversi metodi per filtrare i risultati (cioè ridurli). Puoi combinarli e chiamarli ripetutamente.

Usando size(), filtriamo per dimensione del file. In questo modo troviamo file con dimensioni comprese tra 100 e 200 byte:

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

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

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

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

Finder permette anche di filtrare i risultati usando funzioni personalizzate. La funzione riceve come parametro un oggetto Nette\Utils\FileInfo e deve restituire true affinché il file sia incluso nei risultati.

Esempio: ricerca di file PHP che contengono la stringa Nette (indipendentemente dalle maiuscole/minuscole):

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

Filtraggio in profondità

Durante la ricerca ricorsiva, puoi impostare la profondità massima di attraversamento usando il metodo limitDepth(). Se imposti limitDepth(1), vengono attraversate solo le prime sottodirectory, limitDepth(0) disattiva l'attraversamento in profondità e il valore –1 annulla il limite.

Finder permette di decidere in quale directory entrare durante l'attraversamento usando funzioni personalizzate. La funzione riceve come parametro un oggetto Nette\Utils\FileInfo e deve restituire true per entrare nella directory:

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

Ordinamento

Finder offre anche diverse funzioni per ordinare i risultati.

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

Finder permette anche di ordinare usando una funzione personalizzata. Questa riceve come parametri due oggetti Nette\Utils\FileInfo e deve restituire il risultato del confronto con l'operatore <=>, cioè -1, 0 o 1. Ad esempio, in questo modo ordiniamo i file per dimensione:

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

Ricerche multiple diverse

Se hai bisogno di trovare più file diversi in posizioni diverse o che soddisfano criteri diversi, usa il metodo append(). Restituisce un nuovo oggetto Finder, quindi è possibile concatenare le chiamate ai metodi:

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

In alternativa, è possibile utilizzare il metodo append() per aggiungere un file specifico (o un array di file). Quindi restituisce 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 come la dimensione del file, la data dell'ultima modifica, il nome, il percorso, ecc.

Inoltre, fornisce metodi per restituire il percorso relativo, il che è utile durante l'attraversamento in profondità:

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

Inoltre, hai a disposizione metodi per leggere e scrivere il contenuto del file:

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

Restituzione dei risultati come array

Come visto negli esempi, Finder implementa l'interfaccia IteratorAggregate, quindi puoi usare foreach per scorrere i risultati. È programmato in modo che i risultati vengano caricati solo durante l'iterazione, quindi se hai un gran numero di file, non si aspetta che vengano letti tutti.

Puoi anche farti restituire i risultati 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