Finder: fájlok keresése
Olyan 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ájlok0.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-DDformátumú naplóksrc/**/tests/*– fájlok asrc/tests,src/foo/tests,src/foo/bar/testskönyvtárakban és így tovább.docs/**.md– minden.mdkiterjesztésű fájl adocskö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 funkció érti a >, >=, <, <=, =,
!=, <> operátorokat.
A Finder lehetővé teszi az eredmények szűrését saját funkciók segítségével is. A funkció 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 funkciókkal eldönteni, melyik könyvtárba lépjen be a bejárás során. A funkció
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(fn($file) => $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 funkcióval 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
A 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 Nette\Utils\FileInfo 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();