Finder: iskanje datotek

Potrebujete najti datoteke, ki ustrezajo določeni maski? Finder vam bo pri tem pomagal. Je vsestransko in hitro orodje za brskanje po strukturi imenikov.

Namestitev:

composer require nette/utils

Primeri predpostavljajo ustvarjen vzdevek:

use Nette\Utils\Finder;

Uporaba

Najprej si poglejmo, kako lahko z uporabo Nette\Utils\Finder izpišete imena datotek s končnicami .txt in .md v trenutnem imeniku:

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

Privzeti imenik za iskanje je trenutni imenik, vendar ga lahko spremenite z metodama in() ali from(). Spremenljivka $file je instanca razreda FileInfo z veliko uporabnimi metodami. V ključu $name je pot do datoteke kot niz.

Kaj iskati?

Poleg metode findFiles() obstaja tudi findDirectories(), ki išče samo imenike, in find(), ki išče oboje. Te metode so statične, zato jih je mogoče klicati brez ustvarjanja instance. Parameter z masko je neobvezen, če ga ne navedete, se poišče vse.

foreach (Finder::find() as $file) {
	echo $file; // zdaj se izpišejo vse datoteke in imeniki
}

Z metodama files() in directories() lahko dodate, kaj še želite iskati. Metode je mogoče klicati večkrat in kot parameter lahko navedete tudi polje mask:

Finder::findDirectories('vendor') // vsi imeniki
	->files(['*.php', '*.phpt']); // plus vse PHP datoteke

Alternativa statičnim metodam je ustvarjanje instance z new Finder (tako ustvarjen svež objekt ne išče ničesar) in navedbo, kaj iskati, z uporabo files() in directories():

(new Finder)
	->directories()      // vsi imeniki
	->files('*.php');    // plus vse PHP datoteke

V maski lahko uporabljate nadomestne znake *, **, ? in [...]. Lahko celo določite imenike, na primer src/*.php poišče vse PHP datoteke v imeniku src.

Simbolične povezave se prav tako štejejo za imenike ali datoteke.

Kje iskati?

Privzeti imenik za iskanje je trenutni imenik. Spremenite ga z metodama in() in from(). Kot je razvidno iz imen metod, in() išče samo v danem imeniku, medtem ko from() išče tudi v njegovih podimenikih (rekurzivno). Če želite iskati rekurzivno v trenutnem imeniku, lahko uporabite from('.').

Te metode je mogoče klicati večkrat ali jim posredovati več poti kot polje, datoteke se bodo nato iskale v vseh imenikih. Če kateri od imenikov ne obstaja, se sproži izjema Nette\UnexpectedValueException.

Finder::findFiles('*.php')
	->in(['src', 'tests']) // išče neposredno v src/ in tests/
	->from('vendor');      // išče tudi v podimenikih vendor/

Relativne poti so relativne glede na trenutni imenik. Seveda lahko navedete tudi absolutne poti:

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

V poti je mogoče uporabiti nadomestne znake nadomestne znake *, **, ?. Tako lahko na primer s potjo src/*/*.php iščete vse PHP datoteke v imenikih druge ravni v imeniku src. Znak **, imenovan globstar, je močan adut, saj omogoča iskanje tudi v podimenikih: z src/**/tests/*.php iščete vse PHP datoteke v imeniku tests, ki se nahaja v src ali katerem koli njegovem podimeniku.

Nasprotno pa nadomestni znaki [...] v poti niso podprti, tj. nimajo posebnega pomena, da ne bi prišlo do nezaželenega vedenja v primeru, da bi iskali na primer in(__DIR__) in bi se v poti slučajno pojavili znaki [].

Pri iskanju datotek in imenikov v globino se najprej vrne nadrejeni imenik in šele nato datoteke, ki jih vsebuje, kar je mogoče obrniti z uporabo childFirst().

Nadomestni znaki

V maski lahko uporabljate več posebnih znakov:

  • * – nadomešča poljubno število poljubnih znakov (razen /)
  • ** – nadomešča poljubno število poljubnih znakov, vključno z / (tj. mogoče je iskati večnivojsko)
  • ? – nadomešča en poljuben znak (razen /)
  • [a-z] – nadomešča en znak s seznama znakov v oglatih oklepajih
  • [!a-z] – nadomešča en znak izven seznama znakov v oglatih oklepajih

Primeri uporabe:

  • img/?.png – datoteke z enočrkovnim imenom 0.png, 1.png, x.png, itd.
  • logs/[0-9][0-9][0-9][0-9]-[01][0-9]-[0-3][0-9].log – dnevniki v formatu YYYY-MM-DD
  • src/**/tests/* – datoteke v imenikih src/tests, src/foo/tests, src/foo/bar/tests in tako naprej.
  • docs/**.md – vse datoteke s končnico .md v vseh podimenikih imenika docs

Izključitev

Z metodo exclude() je mogoče izključiti datoteke in imenike iz iskanja. Navedete masko, ki ji datoteka ne sme ustrezati. Primer iskanja datotek *.txt, razen tistih, ki v imenu vsebujejo črko X:

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

Za izpustitev pregledanih podimenikov uporabite exclude():

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

Filtriranje

Finder ponuja več metod za filtriranje rezultatov (tj. njihovo zmanjšanje). Lahko jih kombinirate in kličete večkrat.

Z size() filtriramo glede na velikost datoteke. Tako najdemo datoteke z velikostjo v območju od 100 do 200 bajtov:

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

Metoda date() filtrira glede na datum zadnje spremembe datoteke. Vrednosti so lahko absolutne ali relativne glede na trenutni datum in čas, na primer, tako najdemo datoteke, spremenjene v zadnjih dveh tednih:

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

Obe funkciji razumeta operatorje >, >=, <, <=, =, !=, <>.

Finder omogoča tudi filtriranje rezultatov z uporabo lastnih funkcij. Funkcija prejme kot parameter objekt Nette\Utils\FileInfo in mora vrniti true, da se datoteka vključi v rezultate.

Primer: iskanje PHP datotek, ki vsebujejo niz Nette (ne glede na velikost črk):

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

Globinsko filtriranje

Pri rekurzivnem iskanju lahko nastavite največjo globino brskanja z metodo limitDepth(). Če nastavite limitDepth(1), se preiskujejo samo prvi podimeniki, limitDepth(0) izklopi globinsko brskanje in vrednost –1 prekliče omejitev.

Finder omogoča z uporabo lastnih funkcij odločanje, v kateri imenik naj se vstopi med brskanjem. Funkcija prejme kot parameter objekt Nette\Utils\FileInfo in mora vrniti true, da se vstopi v imenik:

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

Razvrščanje

Finder ponuja tudi več funkcij za razvrščanje rezultatov.

Metoda sortByName() razvršča rezultate po imenih datotek. Razvrščanje je naravno, torej pravilno obravnava števila v imenih in vrača npr. foo1.txt pred foo10.txt.

Finder omogoča tudi razvrščanje z uporabo lastne funkcije. Ta prejme kot parameter dva objekta Nette\Utils\FileInfo in mora vrniti rezultat primerjave z operatorjem <=>, torej -1, 0 ali 1. Na primer, tako razvrstimo datoteke po velikosti:

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

Več različnih iskanj

Če morate najti več različnih datotek na različnih lokacijah ali ki izpolnjujejo druga merila, uporabite metodo append(). Vrne nov objekt Finder, zato je mogoče verižiti klice metod:

($finder = new Finder) // v spremenljivko $finder shranimo prvi Finder!
	->files('*.php')   // v src/ iščemo datoteke *.php
	->from('src')
	->append()
	->files('*.md')    // v docs/ iščemo datoteke *.md
	->from('docs')
	->append()
	->files('*.json'); // v trenutni mapi iščemo datoteke *.json

Alternativno lahko uporabite metodo append() za dodajanje določene datoteke (ali polja datotek). Nato vrne isti objekt Finder:

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

FileInfo

Nette\Utils\FileInfo je razred, ki predstavlja datoteko ali imenik v rezultatih iskanja. Gre za razširitev razreda SplFileInfo, ki zagotavlja informacije, kot so velikost datoteke, datum zadnje spremembe, ime, pot itd.

Poleg tega ponuja metode za vračanje relativne poti, kar je uporabno pri globinskem brskanju:

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

Nadalje imate na voljo metode za branje in pisanje vsebine datoteke:

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

Vračanje rezultatov kot polje

Kot je bilo videti v primerih, Finder implementira vmesnik IteratorAggregate, zato lahko uporabite foreach za pregledovanje rezultatov. Programiran je tako, da se rezultati nalagajo samo med pregledovanjem, tako da če imate veliko število datotek, ni treba čakati, da se vse preberejo.

Rezultate lahko dobite tudi kot polje objektov Nette\Utils\FileInfo z metodo collect(). Polje ni asociativno, ampak numerično.

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