Finder: căutarea fișierelor

Aveți nevoie să găsiți fișiere care corespund unei anumite măști? Finder vă va ajuta. Este un instrument versatil și rapid pentru parcurgerea structurii de directoare.

Instalare:

composer require nette/utils

Exemplele presupun că a fost creat un alias:

use Nette\Utils\Finder;

Utilizare

Mai întâi vom arăta cum puteți folosi Nette\Utils\Finder pentru a afișa numele fișierelor cu extensiile .txt și .md în directorul curent:

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

Directorul implicit pentru căutare este directorul curent, dar îl puteți schimba folosind metodele in() sau from(). Variabila $file este o instanță a clasei FileInfo cu o mulțime de metode utile. Cheia $name conține calea către fișier sub formă de șir.

Ce se caută?

Pe lângă metoda findFiles(), există și findDirectories(), care caută doar directoare, și find(), care le caută pe ambele. Aceste metode sunt statice, deci pot fi apelate fără a crea o instanță. Parametrul cu masca este opțional; dacă nu îl specificați, se caută totul.

foreach (Finder::find() as $file) {
	echo $file; // acum se vor afișa toate fișierele și directoarele
}

Cu ajutorul metodelor files() și directories() puteți specifica ce altceva să se caute. Metodele pot fi apelate repetat, iar ca parametru se poate specifica și un array de măști:

Finder::findDirectories('vendor') // toate directoarele
	->files(['*.php', '*.phpt']); // plus toate fișierele PHP

O alternativă la metodele statice este crearea unei instanțe folosind new Finder (un obiect nou creat astfel nu caută nimic) și specificarea a ceea ce se caută folosind files() și directories():

(new Finder)
	->directories()      // toate directoarele
	->files('*.php');    // plus toate fișierele PHP

În mască puteți folosi metacaracterele *, **, ? și [...]. Puteți chiar specifica directoare, de exemplu src/*.php va căuta toate fișierele PHP din directorul src.

Link-urile simbolice (symlinks) sunt, de asemenea, considerate directoare sau fișiere.

Unde se caută?

Directorul implicit pentru căutare este directorul curent. Îl puteți schimba folosind metodele in() și from(). După cum sugerează numele metodelor, in() caută doar în directorul specificat, în timp ce from() caută și în subdirectoarele sale (recursiv). Dacă doriți să căutați recursiv în directorul curent, puteți folosi from('.').

Aceste metode pot fi apelate de mai multe ori sau li se pot transmite mai multe căi sub formă de array; fișierele vor fi căutate apoi în toate directoarele specificate. Dacă unul dintre directoare nu există, se va arunca o excepție Nette\UnexpectedValueException.

Finder::findFiles('*.php')
	->in(['src', 'tests']) // caută direct în src/ și tests/
	->from('vendor');      // caută și în subdirectoarele vendor/

Căile relative sunt considerate relative la directorul curent. Desigur, se pot specifica și căi absolute:

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

În cale se pot folosi metacaracterele *, **, ?. De exemplu, folosind calea src/*/*.php puteți căuta toate fișierele PHP din directoarele de nivelul al doilea din directorul src. Caracterul **, numit globstar, este un atu puternic, deoarece permite căutarea și în subdirectoare: folosind src/**/tests/*.php căutați toate fișierele PHP din directorul tests aflat în src sau în oricare dintre subdirectoarele sale.

Pe de altă parte, metacaracterele [...] nu sunt suportate în cale, adică nu au o semnificație specială, pentru a evita comportamente nedorite în cazul în care căutați, de exemplu, in(__DIR__) și, întâmplător, în cale apar caracterele [].

La căutarea fișierelor și directoarelor în adâncime, se returnează mai întâi directorul părinte și abia apoi fișierele conținute în el, comportament ce poate fi inversat folosind childFirst().

Metacaractere

În mască puteți folosi mai multe caractere speciale:

  • * – înlocuiește orice număr de caractere (cu excepția /)
  • ** – înlocuiește orice număr de caractere, inclusiv / (adică se poate căuta pe mai multe niveluri)
  • ? – înlocuiește un singur caracter (cu excepția /)
  • [a-z] – înlocuiește un singur caracter din lista de caractere din parantezele drepte
  • [!a-z] – înlocuiește un singur caracter din afara listei de caractere din parantezele drepte

Exemple de utilizare:

  • img/?.png – fișiere cu nume dintr-o singură literă, cum ar fi 0.png, 1.png, x.png, etc.
  • logs/[0-9][0-9][0-9][0-9]-[01][0-9]-[0-3][0-9].log – loguri în formatul YYYY-MM-DD
  • src/**/tests/* – fișiere în directorul src/tests, src/foo/tests, src/foo/bar/tests și așa mai departe.
  • docs/**.md – toate fișierele cu extensia .md din toate subdirectoarele directorului docs

Excludere

Cu ajutorul metodei exclude(), puteți exclude fișiere și directoare din căutare. Specificați o mască căreia fișierul nu trebuie să îi corespundă. Exemplu de căutare a fișierelor *.txt, cu excepția celor care conțin litera X în nume:

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

Pentru a omite parcurgerea anumitor subdirectoare, folosiți exclude():

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

Filtrare

Finder oferă mai multe metode pentru filtrarea rezultatelor (adică reducerea lor). Le puteți combina și apela repetat.

Cu ajutorul size(), filtrăm după dimensiunea fișierului. Astfel, găsim fișiere cu dimensiunea în intervalul 100 – 200 de octeți:

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

Metoda date() filtrează după data ultimei modificări a fișierului. Valorile pot fi absolute sau relative la data și ora curentă; de exemplu, astfel găsim fișiere modificate în ultimele două săptămâni:

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

Ambele funcții înțeleg operatorii >, >=, <, <=, =, !=, <>.

Finder permite, de asemenea, filtrarea rezultatelor folosind funcții personalizate. Funcția primește ca parametru obiectul Nette\Utils\FileInfo și trebuie să returneze true pentru ca fișierul să fie inclus în rezultate.

Exemplu: căutarea fișierelor PHP care conțin șirul Nette (indiferent de majuscule/minuscule):

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

Filtrare în adâncime

La căutarea recursivă, puteți seta adâncimea maximă de parcurgere folosind metoda limitDepth(). Dacă setați limitDepth(1), se parcurg doar primele subdirectoare; limitDepth(0) dezactivează parcurgerea în adâncime, iar valoarea –1 anulează limita.

Finder permite, folosind funcții personalizate, să decideți în ce director să se intre la parcurgere. Funcția primește ca parametru obiectul Nette\Utils\FileInfo reprezentând directorul și trebuie să returneze true pentru a intra în acel director:

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

Sortare

Finder oferă, de asemenea, mai multe funcții pentru sortarea rezultatelor.

Metoda sortByName() sortează rezultatele după numele fișierelor. Sortarea este naturală, adică gestionează corect numerele din nume și returnează, de exemplu, foo1.txt înainte de foo10.txt.

Finder permite, de asemenea, sortarea folosind o funcție personalizată. Aceasta primește ca parametru două obiecte Nette\Utils\FileInfo și trebuie să returneze rezultatul comparației folosind operatorul spaceship <=> (adică -1, 0 sau 1). De exemplu, astfel sortăm fișierele după dimensiune:

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

Mai multe căutări diferite

Dacă aveți nevoie să găsiți mai multe fișiere diferite în locații diferite sau care îndeplinesc alte criterii, utilizați metoda append(). Aceasta returnează un nou obiect Finder, permițând înlănțuirea apelurilor de metode:

($finder = new Finder) // salvăm primul Finder în variabila $finder!
	->files('*.php')   // în src/ căutăm fișiere *.php
	->from('src')
	->append()
	->files('*.md')    // în docs/ căutăm fișiere *.md
	->from('docs')
	->append()
	->files('*.json'); // în directorul curent căutăm fișiere *.json

Alternativ, metoda append() poate fi folosită pentru a adăuga un fișier specific (sau un array de fișiere). În acest caz, returnează același obiect Finder:

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

FileInfo

Nette\Utils\FileInfo este o clasă care reprezintă un fișier sau un director în rezultatele căutării. Este o extensie a clasei SplFileInfo, care oferă informații precum dimensiunea fișierului, data ultimei modificări, numele, calea etc.

În plus, oferă metode pentru returnarea căii relative, ceea ce este util la parcurgerea în adâncime:

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

De asemenea, aveți la dispoziție metode pentru citirea și scrierea conținutului fișierului:

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

Returnarea rezultatelor ca array

După cum s-a văzut în exemple, Finder implementează interfața IteratorAggregate, astfel încât puteți folosi foreach pentru a parcurge rezultatele. Este implementat astfel încât rezultatele sunt încărcate doar pe parcursul iterării, deci dacă aveți un număr mare de fișiere, nu se așteaptă până când toate sunt citite.

Rezultatele pot fi, de asemenea, returnate ca un array de obiecte Nette\Utils\FileInfo, folosind metoda collect(). Array-ul nu este asociativ, ci indexat numeric.

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