Iteratory w PHP - część 1: DirectoryIterator
W w pierwszej części artykułu o iteratorach w PHP zajmiemy się omówieniem wbudowanej w PHP5 klasy DirectoryIterator, służącej do przeglądania plików w katalogu oraz wykonywania na nich różnych operacji sprawdzających.
Dotychczas w wersjach starszych niż PHP5, aby odczytać listę plików w katalogu oraz wykonać na nich jakieś operacje należało użyć takiej konstrukcji:
$handle=opendir("katalog");
while($file=readdir($handle)) {
// tutaj lista operacji na pliku
}
Opcjonalnie jeśli już się posiadało PHP5, można było skorzystać z nowej funkcji jaką jest scandir:
$files=scandir("katalog");
foreach($files as $file) {
// tutaj lista operacji na pliku
}
Obie powyższe metody mają jednak taką wadę, że aby użyć filtrowania plików np. po rozmiarze należałoby sprawdzić rozmiar przy użyciu filesize($file), co nie jest zbytnio wygodne oraz jest bardziej typowe dla nieobiektowych programów. Innym problemem jest przeglądanie rekurencyjne katalogów, którym zajmiemy się w dalszej części artykułu, podczas omawiana RecursiveDirectoryIterator.
DirectoryIterator
W swoim podstawowym użyciu DirectoryIterator przypomina użycie funkcji scandir:
$di=new DirectoryIterator("katalog");
foreach($di as $file) {
// operacje na pliku
}
Wygląda prawie identycznie, jednak przewaga DirectoryIterator ukazuje się poprzez metody, jakie posiada $file:
- string getFilename() - pobiera nazwę pliku
- string getPath() - pobiera ścieżkę do katalogu, w którym plik się znajduje
- string getPathname() - pobiera ścieżkę do pliku
- int getSize() - pobiera rozmiar pliku
- string getType() - pobiera typ pliku (nie jest to jednak typ MIME)
- int getATime() - pobiera timestamp ostatniego dostępu do pliku (jeśli dostępny)
- int getCTime() - pobiera timestamp ostatniej zmiany pliku (jeśli dostępny)
- int getMTime() - pobiera timestamp ostatniej modyfikacji pliku (jeśli dostępny)
- int getOwner() - pobiera właściciela pliku
- int getGroup() - pobiera grupę pliku
- int getPerms() - pobiera uprawnienia do pliku
- bool isDir() - sprawdza czy przetwarzany $file nie jest katalogiem
- bool isDot() - sprawdza czy przetwarzany $file nie jest .. lub .
- bool isReadable() - sprawdza, czy można czytać z pliku
- bool isWritable() - sprawdza, czy można pisać do pliku
- bool isExecutable() - sprawdza, czy plik można wykonać
- bool isLink() - sprawdza, czy plik nie jest linkiem
Jak widać, lista dostępnych metod jest spora i można przy jej pomocy w łatwy sposób zrobić wiele rzeczy, jak np. wspomniane wcześniej filtrowanie plików w katalogu:
$di=DirectoryIterator("katalog");
print("Wszystkie pliki w katalogu "katalog" mniejsze od 500 KB:<br/>");
foreach($di as $file) {
if(!$file->isDot() and !$file->isDir() and $file->getSize() < (1024*500)) {
print $file->getFilename() . " (rozmiar: " . $file->getSize() . ")<br/>";
}
}
Programowanie z użyciem DirectoryIterator zamiast scandir lub readdir wydaje mi się nieco bardziej intuicyjne. Natomiast znacząca przewaga iteratorów nad wspomnianymi metodami ujawnia się w przypadku chęci rekurencyjnego przechodzenia po plikach. Tutaj do akcji wkracza RecursiveDirectoryIterator.
RecursiveDirectoryIterator
Rekursywne przechodzenie do podkatalogów i operowanie na plikach przy użyciu standardowych metod może nastarczać problemów. Rozwiązaniem może okazać się RecursiveDirectoryIterator. Działa on na podobnej zasadzie zasadzie jak DirectoryIterator oraz dostarcza taki sam zestaw metod, dzięki czemu nie ma znaczenia czy operujemy na pliku w obecnym katalogu, czy na jego podkatalogu lub podkatalogu 2 stopnia.
RecursiveDirectoryIterator do poprawnego działania wymaga opakowania go w RecursiveIteratorIterator. Jest to specjalna klasa do obsługi iteratorów rekursywnych.
W praktyce użycie RecursiveDirectoryIterator wygląda tak:
$rdi=new RecursiveIteratorIterator(new RecursiveDirectoryIterator("katalog"));
foreach($rdi as $file) {
print $rdi->getPathname() . "<br/>";
}
Powyższy kod wyświetli nam ścieżki wszystkich plików w katalogu "katalog" oraz jego podkatalogach.
Wydajność
W komentarzach do wpisu matipl zapytał o wydajność poszczególnych rozwiązań, więc postanowiłem to sprawdzić i teraz prezentuję wyniki.
Skrypt testowy miał za zadanie wyświetlić wszystkie pliki w katalogu Windows, które nie są katalogami (sprawdzał to) oraz wypisać ich rozmiar.
Na wykresie umieściłem wyniki 5 uruchomień skryptu. Do mierzenia czasu wykonywania użyłem funkcji microtime.

Jak widać, najwolniejszą metodą, jest metoda pierwsza, z opendir. Najszybsza jest metoda druga - scandir, DirectoryIterator jest on niej niewiele wolniejsz (średnio o 4ms).
Subskrybuj kanał RSS bloga
matipl