Iskalnik: Iskanje datotek

Potrebujete najti datoteke, ki ustrezajo določeni maski? Iskalnik vam lahko pomaga. Je vsestransko in hitro orodje za pregledovanje strukture imenikov.

Namestitev:

composer require nette/utils

Primeri predpostavljajo, da je bil ustvarjen vzdevek:

use Nette\Utils\Finder;

Uporaba

Najprej si oglejmo, kako lahko s pomočjo Nette\Utils\Finder izpišete imena datotek s končnicama .txt in .md v trenutnem imeniku:

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

Privzet imenik za iskanje je trenutni imenik, vendar ga lahko spremenite z uporabo metod in() ali from( ). Spremenljivka $file je primerek razreda FileInfo z veliko uporabnimi metodami. Ključ $name vsebuje pot do datoteke kot niz.

Kaj iskati?

Poleg metode findFiles() obstajata še findDirectories(), ki išče samo po imenikih, in find(), ki išče po obeh. Ti metodi sta statični, zato ju lahko kličete, ne da bi ustvarili primerek. Parameter maska ni obvezen, če ga ne navedete, se poišče vse.

foreach (Finder::find() as $file) {
	echo $file; // zdaj so na seznamu vse datoteke in imeniki
}

Z metodama files() in directories() lahko dodate, kaj naj se še išče. Metodi lahko pokličete večkrat, kot parameter pa lahko navedete polje mask:

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

Alternativa statičnim metodam je, da ustvarite primerek z uporabo new Finder (tako ustvarjen svež objekt ne išče ničesar) in določite, kaj naj se išče, z uporabo files() in directories():

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

Uporabite lahko nadomestne znake *, **, ? and [...] v maski. Navedete lahko celo imenike, na primer src/*.php bo poiskal vse datoteke PHP v imeniku src.

Privzet imenik za iskanje je trenutni imenik. To lahko spremenite z uporabo metod in() in from(). Kot je razvidno iz imen metod, in() išče samo po trenutnem imeniku, medtem ko from() išče tudi po njegovih podimenikih (rekurzivno). Če želite rekurzivno iskati v trenutnem imeniku, lahko uporabite from('.').

Ti metodi lahko pokličete večkrat ali pa jima posredujete več poti kot polja, potem se bodo datoteke iskale v vseh imenikih. Če eden od imenikov ne obstaja, se vrže sporočilo 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 določite tudi absolutne poti:

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

*, **, ? can be used in the path. For example, you can use the path src/*/*.php za iskanje vseh datotek PHP v imenikih druge ravni v imeniku src. Znak **, imenovan globstar, je močan adut, saj omogoča iskanje tudi po podimenikih: uporabite src/**/tests/*.php za iskanje vseh datotek PHP v imeniku tests, ki se nahajajo v imeniku src ali katerem koli njegovem podimeniku.

Po drugi strani pa so nadomestni znaki [...] znaki v poti niso podprti, tj. nimajo posebnega pomena, da bi se izognili nezaželenemu obnašanju v primeru, da iščete na primer in(__DIR__) in se po naključju v poti pojavijo znaki [].

Pri poglobljenem iskanju datotek in imenikov se najprej vrne nadrejeni imenik, nato pa datoteke, ki jih vsebuje, kar lahko obrnete s childFirst().

Zaščitni znaki

V maski lahko uporabite več posebnih znakov:

  • * – replaces any number of arbitrary characters (except /)
  • ** – nadomesti poljubno število poljubnih znakov, vključno z / (tj. lahko se išče na več ravneh)
  • ? – replaces one arbitrary character (except /)
  • [a-z] – nadomesti en znak s seznama znakov v oglatih oklepajih
  • [!a-z] – nadomesti en znak zunaj 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 – dnevniške datoteke v obliki YYYY-MM-DD
  • src/**/tests/* – datoteke v imeniku src/tests, src/foo/tests, src/foo/bar/tests itd.
  • docs/**.md – vse datoteke s končnico .md v vseh podimenikih imenika. docs

Z izjemo

Za izključitev datotek in imenikov iz iskanja uporabite metodo exclude(). Določite masko, ki ji datoteka ne sme ustrezati. Primer iskanja datotek *.txt, razen tistih, ki v imenu vsebujejo črko X:

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

Uporabite exclude(), če želite preskočiti pregledane podimenike:

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

Filtriranje

Iskalnik ponuja več načinov za filtriranje rezultatov (tj. njihovo zmanjševanje). Te metode lahko kombinirate in jih večkrat prikličete.

Za filtriranje po velikosti datoteke uporabite size(). Na ta način poiščemo datoteke z velikostjo med 100 in 200 bajti:

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

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

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

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

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

Primer: poiščite datoteke PHP, ki vsebujejo niz Nette (brez upoštevanja velikih in malih črk):

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

Globinsko filtriranje

Pri rekurzivnem iskanju lahko največjo globino iskanja nastavite z metodo limitDepth(). Če nastavite limitDepth(1), se prečešejo samo prvi podimeniki, limitDepth(0) onemogoči globinsko pregledovanje, vrednost –1 pa omejitev prekliče.

Iskalnik omogoča uporabo lastnih funkcij za odločanje o tem, v kateri imenik naj vstopite pri brskanju. Funkcija prejme kot parameter objekt Nette\Utils\FileInfo in mora vrniti true, da vstopi v imenik:

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

Razvrščanje

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

Metoda sortByName() razvršča rezultate po imenu datoteke. Sortiranje je naravno, tj. pravilno obravnava številke v imenih in vrne npr. foo1.txt pred foo10.txt.

Iskalnik omogoča tudi razvrščanje z uporabo funkcije po meri. Ta kot parametra sprejme dva predmeta Nette\Utils\FileInfo in mora vrniti rezultat primerjave z operatorjem <=>, tj. -1, 0 nebo 1. Tako na primer razvrstimo datoteke po velikosti:

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

Več različnih iskanj

Če morate poiskati več različnih datotek na različnih lokacijah ali ki izpolnjujejo različna merila, uporabite metodo append(). Ta vrne nov objekt Finder, tako da lahko verižno kličete metode:

($finder = new Finder) // shranite prvi iskalnik v spremenljivko $finder!
	->files('*.php')   // iskanje datotek *.php v src/
	->from('src')
	->append()
	->files('*.md')    // v docs/ poiščite datoteke *.md
	->from('docs')
	->append()
	->files('*.json'); // v trenutni mapi poiščite datoteke *.json

Lahko pa uporabite tudi metodo append() za dodajanje določene datoteke (ali niza datotek). Nato vrne isti predmet Finder:

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

FileInfo

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

Poleg tega zagotavlja metode za vračanje relativnih poti, kar je uporabno pri poglobljenem iskanju:

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

Na voljo so tudi metode za branje in pisanje vsebine datoteke:

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

Vračanje rezultatov v obliki polja

Kot je razvidno iz primerov, iskalnik implementira vmesnik IteratorAggregate, zato lahko za brskanje po rezultatih uporabite foreach. Programiran je tako, da se rezultati nalagajo šele med brskanjem, tako da če imate veliko število datotek, ne čaka, da se vse preberejo.

Rezultate lahko vrnete tudi kot polje predmetov Nette\Utils\FileInfo z uporabo metode collect(). Polje ni asociativno, temveč številsko.

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