W jaki sposób listować zawartość katalogów w PHP?

Treść dodana: 08 grudnia 2015.

Częstym problemem w pracy z plikami jest konieczność listowania zawartości katalogów oraz plików w nich występujących. Wraz z wprowadzeniem do PHP biblioteki SPL znacząco skrócił się czas potrzebny na utworzenie kodu. Wiele czynności może być zautomatyzowanych, nie wymaga zbytniego wysiłku ze strony programisty. Do pracy z plikami bardzo przydatne stają się klasy SplFileInfo, DirectoryIterator, RecursiveDirectoryIterator. Niestety początkujący programiści nie do końca rozumieją ideę klas, dziedziczenia dlatego z bibliotek tych rzadko korzystają. Być może kilka przykładów rozjaśni temat.

Listowanie zawartości bieżącego katalogu

<?php
// ustawienie kontroli błędów
// pominę ten blok w kolejnych przykładach
error_reporting(E_ALL);
ini_set('display_errors', 'on');
ini_set('display_startup_errors', 'on');

try {
    $directoryIterator = new DirectoryIterator(__DIR__);
    foreach ($directoryIterator as $dir) {
        echo '|- ' . $dir->getFilename() . '<br>';
    }
} catch (Exception $e) {
    echo $e->getMessage();
}

Kod taki spowoduje wyświetlenie wszystkich plików i katalogów z bieżącego folderu (stała magiczna DIR). Dodatkowo przechwycone zostaną wszystkie wyjątki a ich zawartość wyświetlona na ekranie. Wynikiem naszego kodu stanie się przykładowo:

|- directory.php // nasz plik uruchomieniowy
|- Test // przykładowy katalog
|- .ukryty // plik ukryty w systemie uniksowym (nazwa zaczyna się od kropki)
|- . // bieżący katalog
|- .. // katalog wyżej w hierarchii

Nie do końca interesuje nas informacja o katalogu bieżącym i jednym wyżej. Możemy je usunąć metodą isDot():

try {
    $directoryIterator = new DirectoryIterator(__DIR__);
    foreach ($directoryIterator as $dir) {
        if (!$dir->isDot()) {
            echo '|- ' . $dir->getFilename() . '<br>';
        }
    }
} catch (Exception $e) {
    echo $e->getMessage();
}

Już lepiej, zostały tylko pliki i katalogi główne.

|- directory.php
|- Test
|- .ukryty

Od wersji PHP 5.3 istnieje także FilesystemIterator rozszerzający DirectoryIterator. Dodano do niego kilka masek oraz stałych. Różnią się też sposobem zwracania danych dla każdej iteracji – FilesystemIterator tworzy nową instancję SplFileInfo podczas gdy DirectoryIterator przesuwa wewnętrzny wskaźnik o 1 (kluczem jest indeks numeryczny).

try {
    // domyślne flagi, zgodnie z dokumentacją to:
    // int $flags = FilesystemIterator::KEY_AS_PATHNAME | FilesystemIterator::CURRENT_AS_FILEINFO |
    // FilesystemIterator::SKIP_DOTS ]
    $filesystemIterator = new FilesystemIterator(__DIR__);
    foreach ($filesystemIterator as $key => $dir) {
        echo '|- ' . $dir->getFilename() .' - klucz:'. $key. '<br>';
    }
} catch (Exception $e) {
    echo $e->getMessage();
}
|- directory.php - klucz: [ścieżka]/directory.php
|- Test - klucz: [ścieżka]/Test
|- .ukryty - klucz: [ścieżka]/.ukryty

Z racji tego iż domyślnie ustawiona jest flaga FilesystemIterator::SKIP_DOTS katalogi wirtualne (. oraz ..) są automatycznie usuwane.

Listowanie zawartości katalogu i podkatalogów

Rzeczą naturalną jest że katalogi zawierają podkatalogi z dowolną liczbą plików (ograniczoną przez możliwości naszego systemu). Aby je wylistować potrzebny nam będzie RecursiveDirectoryIterator (od wersji PHP 5.3 rozszerza FilesystemIterator):

try {
    $rdi = new RecursiveDirectoryIterator(__DIR__, RecursiveDirectoryIterator::SKIP_DOTS);
    $rii = new RecursiveIteratorIterator($rdi, RecursiveIteratorIterator::SELF_FIRST);
    foreach ($rii as $dir) {
        echo '|- ' . str_pad($dir->getFilename(), $rii->getDepth() + strlen($dir->getFilename()), '*', STR_PAD_LEFT) . '<br>';
    }

} catch (Exception $e) {
    echo $e->getMessage();
}
|- directory.php
|- Test
|- *data
|- **1.php
|- *sub
|- **5.php
|- **4.php
|- *1.php
|- *2.php
|- *3.php
|- .ukryty

Należy pamiętać że listowanie dużej liczby plików może znacząco zająć czas maszyny.

Wypisanie drzewa katalogów

Dzięki wbudowanej klasie RecursiveTreeIterator w łatwy sposób możemy wylistować drzewo katalogowe:

try {
    $rdi = new RecursiveDirectoryIterator(__DIR__, RecursiveDirectoryIterator::SKIP_DOTS);
    $rii = new RecursiveTreeIterator($rdi, RecursiveIteratorIterator::SELF_FIRST);
    foreach ($rii as $dir) {
        echo $dir . '<br>';
    }

} catch (Exception $e) {
    echo $e->getMessage();
}
|-[ścieżka]/directory.php
|-[ścieżka]/Test
| |-[ścieżka]/Test/data
| | \-[ścieżka]/Test/data/1.php
| |-[ścieżka]/Test/sub
| | |-[ścieżka]/Test/sub/5.php
| | \-[ścieżka]/Test/sub/4.php
| |-[ścieżka]/Test/1.php
| |-[ścieżka]/Test/2.php
| \-[ścieżka]/Test/3.php
\-[ścieżka]/.ukryty

Komentarze

Nie ma jeszcze żadnych komentarzy do wyświetlenia. Może chcesz zostać pierwszą osobą która podzieli się swoją opinią?

Dodaj komentarz

*
Nazwa zostanie wyświetlona wraz z komentarzem. Możesz też utworzyć nowe konto w serwisie, dzięki czemu uzyskasz dodatkową funkcjonalność.
*
Akceptowana jest ograniczona składnia Textile. Wszystkie tagi HTML zostaną usunięte.