Feladat
Legyen a feladat a következő: Jelenítsük meg adott könyvtárban lévő összes alkönyvtárat, valamint azon fájlokat, amelyek .jpg, .JPG, .png, .PNG kiterjesztésűek. A megoldás CLI-ben jelenítse meg a hierarchiát.
Első lépések
Először csak járjuk be a teljes könyvtár hierarchiát és írjuk ki a fájlok nevét.
|
|
A RecursiveDirectoryIterator
beépített osztály, a megadott könyvtárat lehet bejárni a segítségével. A konstruktorban beállítottam még, hogy a ‘.’ és a ‘..’ fájlokat hagyja ki. Ahhoz, hogy egy RecursiveIterator
interfészt implementáló osztályt (ilyen a RecursiveDirectoryIterator
is) egy ciklussal bejárhassuk, használhatjuk a RecursiveIteratorIterator
osztályt. Ez a paraméterben megadott módon járja be rekurzívan az iterátort és minden iterációban lekérhetjük például, hogy hanyadik szinten vagyunk. A kiíratás hatására megjelennek egymás alatt a fájlok.
Szűrés
A következő feladat legyen az, hogy csak a megfelelő fájlokat jelenítsük meg. Itt rögtön jöhet a triviális megoldás, miszerint a ciklus törzsében egy if szerkezettel ellenőrizzük az aktuális fáljt. Ez itt még működik is, de minél többféle és minél hosszabb, és minél többször ismétlődő ilyen feltétel vizsgálatokat írunk, annál inkább megéri ezeket iterátorokban megoldani. Miről is van szó.
A PHP-ben van egy RegexIterator
osztály, ami a FilterIterator
osztályból származik. Utóbbiban az accept()
metódus minden iterációban meghívódik és az általa visszaadott bool
érték dönti el, hogy az adott érték megfelelő-e. Ha nem, akkor azonnal a következőre ugrik. A RegexIterator
osztály ezt használja ki: reguláris kifejezésre vizsgál illeszkedést. Ebből létrehoztam egy egyszerű fájl kiterjesztést ellenőrző osztályt:
|
|
Módosítsuk is a kódunkat:
|
|
A program lefutását követően láthatjuk, hogy megjelentek a képernyőn a megfelelő formátumú fájlok. Már csak a könyvtárakat kell megjeleníteni, valamint ráncba kell szedni a megjelenést.
Megjelenítés… helyett probléma
A megjelenítéshez használhatjuk a RecursiveTreeIterator
osztályt, azonban ez RecursiveIterator
-t vár konstruktorban. Ez érthető, viszont az előbb elkészített ExtensionIterator
Iterátor interfészt implementál. Vagyis ez azt jelenti, hogy “elvesztettük” a RecursiveDirectoryIterator
által adott rekurzivitást.
Sikerrel csak akkor fogunk járni, ha sikerül RecursiveIterator
objektumot készítenünk, ami egyben szűri a fájltípusokat. Valamint a már meglévő szűrő osztályunkat se szeretnénk átírni, se kidobni, ugyanis az tökéletesen működik, viszont nekünk többre van szükségünk. Készítsünk tehát egy osztályt, ami ebből származik és implementálja a RecursiveIterator
interfészt:
|
|
A konstruktor nem túl érdekes, egyedül a type hintet módosítottam. Viszont bejött három metódus. A getChildren()
és a hasChildren()
az interfész miatt szükségesek. Szűrés esetén is akkor vannak egy fájlnak gyerekei, mint szűrés nélkül, így a hasChildren()
metódusban egyszerűen visszaadjuk a belső iterátor (az általunk használni kívánt RecursiveDirectoryIterator
) hasChildren()
kiértékelését. A getChildren()
metódusban viszont bejön a rekurzivitás: ha a belső iterátor getChildren()
metódusával térnénk vissza, akkor az alkönyvtárakban már nem működne a szűrés, ezért példányosítjuk újra az osztályt és ezzel térünk vissza (a getChildren()
-nek RecursiveIterator
-t kell visszaadnia). Már csak egy feladatunk van hátra: mivel az accept()
meghívódik abban az esetben is, amikor az aktuális elem egy könyvtár, ezért amennyiben az nem illeszkedik a reguláris kifejezésre, nem fogjuk listázáskor látni. Fogadjuk el, ha az könyvtár, vagy ha nem az, hívjuk meg az ősosztály accept()
metódusát, ami elvégzi az illeszkedés vizsgálatot.
Ha módosítjuk a kiíratást, akkor mind a könyvtárak, mind a képek megjelennek:
|
|
Megjelenítés
Utolsó feladatként a formázás van hátra. Ehhez a RecursiveTreeIterator
-t használom.
|
|
A RecursiveDirectoryIterator
példányosításánál megadtam, hogy bejárásnál a kulcs a fájlnév legyen, így a RecursiveTreeIterator
-nál már felhasználhatom a kulcsot (alapból a második paraméter RecursiveTreeIterator::BYPASS_KEY
, ami nekünk pont nem jó).
Végszó
Szerintem jól szemlélteti ez a példa az iterátorok egymásba ágyazhatóságát, amivel végeredményben sokkal átláthatóbb kódot kapunk. Ha még egy szűrésre szükségünk lenne, az mindösszesen még egy sort jelentene (az osztály esetleges elkészítésén kívül).