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).