Finder: fájlok keresése

Fájlokat kell találnia, amelyek megfelelnek egy adott maszknak? A Finder segít ebben. Ez egy sokoldalú és gyors eszköz a könyvtárstruktúra bejárására.

Telepítés:

composer require nette/utils

A példák feltételezik a következő alias létrehozását:

use Nette\Utils\Finder;

Használat

Először megmutatjuk, hogyan írhatja ki a .txt és .md kiterjesztésű fájlneveket az aktuális könyvtárban a Nette\Utils\Finder segítségével:

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

A keresés alapértelmezett könyvtára az aktuális könyvtár, de ezt megváltoztathatja az in() vagy from() metódusokkal. A $file változó a FileInfo osztály példánya, rengeteg hasznos metódussal. A $name kulcsban a fájl elérési útja található stringként.

Mit kell keresni?

A findFiles() metódus mellett létezik a findDirectories(), amely csak könyvtárakat keres, és a find(), amely mindkettőt keresi. Ezek a metódusok statikusak, így példány létrehozása nélkül hívhatók. A maszk paraméter opcionális, ha nem adja meg, mindent megkeres.

foreach (Finder::find() as $file) {
	echo $file; // most minden fájl és könyvtár kiíródik
}

A files() és directories() metódusokkal kiegészítheti, hogy mi mást kell keresni. A metódusok ismételten hívhatók, és paraméterként maszkok tömbje is megadható:

Finder::findDirectories('vendor') // minden könyvtár
	->files(['*.php', '*.phpt']); // plusz minden PHP fájl

A statikus metódusok alternatívája a példány létrehozása a new Finder segítségével (az így létrehozott friss objektum semmit sem keres), és a files() és directories() segítségével megadni, mit kell keresni:

(new Finder)
	->directories()      // minden könyvtár
	->files('*.php');    // plusz minden PHP fájl

A maszkban használhat helyettesítő karaktereket *, **, ? és [...]. Akár könyvtárakat is megadhat, például a src/*.php megkeresi az összes PHP fájlt a src könyvtárban.

A szimbolikus linkek szintén könyvtárnak vagy fájlnak minősülnek.

Hol kell keresni?

A keresés alapértelmezett könyvtára az aktuális könyvtár. Ezt az in() és from() metódusokkal változtathatja meg. Ahogy a metódusnevekből is látható, az in() csak az adott könyvtárban keres, míg a from() annak alkönyvtáraiban is keres (rekurzívan). Ha rekurzívan szeretne keresni az aktuális könyvtárban, használhatja a from('.')-ot.

Ezek a metódusok többször is hívhatók, vagy több elérési utat átadhat nekik tömbként, a fájlok ezután minden könyvtárban keresve lesznek. Ha valamelyik könyvtár nem létezik, Nette\UnexpectedValueException kivétel dobódik.

Finder::findFiles('*.php')
	->in(['src', 'tests']) // közvetlenül keres a src/ és tests/ könyvtárakban
	->from('vendor');      // a vendor/ alkönyvtáraiban is keres

A relatív elérési utak az aktuális könyvtárhoz képest relatívak. Természetesen abszolút elérési utakat is meg lehet adni:

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

Az elérési úton lehet használni helyettesítő karaktereket *, **, ?. Így például a src/*/*.php elérési út segítségével keresheti az összes PHP fájlt a src könyvtár második szintű könyvtáraiban. A ** karakter, amit globstarnak neveznek, egy erős aduász, mert lehetővé teszi az alkönyvtárakban való keresést is: a src/**/tests/*.php segítségével keresi az összes PHP fájlt a tests könyvtárban, amely a src-ben vagy annak bármely alkönyvtárában található.

Ellenben a [...] helyettesítő karakterek az elérési úton nem támogatottak, azaz nincs különleges jelentésük, hogy elkerüljük a nem kívánt viselkedést abban az esetben, ha például az in(__DIR__)-t keresi, és véletlenül az elérési úton [] karakterek lesznek.

Fájlok és könyvtárak mélységi keresésekor először a szülőkönyvtárat adja vissza, csak azután a benne lévő fájlokat, amit a childFirst() segítségével meg lehet fordítani.

Helyettesítő karakterek

A maszkban több speciális karaktert használhat:

  • * – tetszőleges számú tetszőleges karaktert helyettesít (kivéve /)
  • ** – tetszőleges számú tetszőleges karaktert helyettesít, beleértve a /-t is (azaz többszintű keresés lehetséges)
  • ? – egy tetszőleges karaktert helyettesít (kivéve /)
  • [a-z] – egy karaktert helyettesít a szögletes zárójelben lévő karakterlistából
  • [!a-z] – egy karaktert helyettesít a szögletes zárójelben lévő karakterlistán kívül

Használati példák:

  • img/?.png – egybetűs nevű fájlok 0.png, 1.png, x.png, stb.
  • logs/[0-9][0-9][0-9][0-9]-[01][0-9]-[0-3][0-9].log – YYYY-MM-DD formátumú naplók
  • src/**/tests/* – fájlok a src/tests, src/foo/tests, src/foo/bar/tests könyvtárakban és így tovább.
  • docs/**.md – minden .md kiterjesztésű fájl a docs könyvtár összes alkönyvtárában

Kizárás

Az exclude() metódussal kizárhat fájlokat és könyvtárakat a keresésből. Megadja a maszkot, amelynek a fájl nem felelhet meg. Példa *.txt fájlok keresésére, kivéve azokat, amelyek nevében X betű szerepel:

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

A bejárt alkönyvtárak kihagyásához használja az exclude()-ot:

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

Szűrés

A Finder több metódust kínál az eredmények szűrésére (azaz azok csökkentésére). Kombinálhatja őket és ismételten hívhatja.

A size() segítségével fájlméret szerint szűrünk. Így találunk 100 és 200 bájt közötti méretű fájlokat:

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

A date() metódus a fájl utolsó módosításának dátuma szerint szűr. Az értékek lehetnek abszolútak vagy relatívak az aktuális dátumhoz és időhöz képest, például így találjuk meg az elmúlt két hétben módosított fájlokat:

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

Mindkét függvény érti a >, >=, <, <=, =, !=, <> operátorokat.

A Finder lehetővé teszi az eredmények szűrését saját függvények segítségével is. A függvény paraméterként kap egy Nette\Utils\FileInfo objektumot, és true-t kell visszaadnia, hogy a fájl bekerüljön az eredmények közé.

Példa: olyan PHP fájlok keresése, amelyek tartalmazzák a Nette stringet (kis- és nagybetűk figyelmen kívül hagyásával):

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

Mélységi szűrés

Rekurzív kereséskor beállíthatja a maximális bejárási mélységet a limitDepth() metódussal. Ha beállítja a limitDepth(1)-et, csak az első alkönyvtárakat járja be, a limitDepth(0) kikapcsolja a mélységi bejárást, és a –1 érték törli a korlátot.

A Finder lehetővé teszi saját függvényekkel eldönteni, melyik könyvtárba lépjen be a bejárás során. A függvény paraméterként kap egy Nette\Utils\FileInfo objektumot, és true-t kell visszaadnia, hogy belépjen a könyvtárba:

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

Rendezés

A Finder több funkciót is kínál az eredmények rendezésére.

A sortByName() metódus az eredményeket fájlnevek szerint rendezi. A rendezés naturális, azaz helyesen kezeli a számokat a nevekben, és például a foo1.txt-t adja vissza a foo10.txt előtt.

A Finder lehetővé teszi a rendezést saját függvénnyel is. Paraméterként két Nette\Utils\FileInfo objektumot kap, és vissza kell adnia az <=> operátorral végzett összehasonlítás eredményét, azaz -1, 0 vagy 1. Például így rendezzük a fájlokat méret szerint:

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

Több különböző keresés

Ha több különböző fájlt kell találnia különböző helyeken, vagy más kritériumoknak megfelelőket, használja az append() metódust. Új Finder objektumot ad vissza, így lehetséges a metódushívásokat láncolni:

($finder = new Finder) // a $finder változóba elmentjük az első Findert!
	->files('*.php')   // a src/-ben *.php fájlokat keresünk
	->from('src')
	->append()
	->files('*.md')    // a docs/-ban *.md fájlokat keresünk
	->from('docs')
	->append()
	->files('*.json'); // az aktuális mappában *.json fájlokat keresünk

Alternatívaként használható az append() metódus egy konkrét fájl (vagy fájlok tömbjének) hozzáadására. Akkor ugyanazt a Finder objektumot adja vissza:

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

FileInfo

Nette\Utils\FileInfo egy fájlt vagy könyvtárat reprezentáló osztály a keresési eredményekben. Ez a SplFileInfo osztály kiterjesztése, amely információkat nyújt, mint például a fájlméret, utolsó módosítás dátuma, név, elérési út, stb.

Ezenkívül metódusokat biztosít a relatív elérési út visszaadására, ami hasznos a mélységi bejárás során:

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

Továbbá rendelkezésre állnak metódusok a fájl tartalmának olvasására és írására:

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

Eredmények visszaadása tömbként

Ahogy a példákban látható volt, a Finder implementálja az IteratorAggregate interfészt, így használhatja a foreach-et az eredmények bejárására. Úgy van programozva, hogy az eredmények csak a bejárás során töltődnek be, így ha nagy mennyiségű fájlja van, nem várja meg, amíg mind beolvasódik.

Az eredményeket objektumok tömbjeként is visszaadhatja, méghozzá a collect() metódussal. A tömb nem asszociatív, hanem numerikus.

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