Pobieranie listy plików o wybranym rozszerzeniu w PHP

Aby pobrać listę plików o wybranym rozszerzeniu z danego katalogu najprościej jest użyć funkcji [glob](http://pl.php.net/manual/en/function.glob.php)

glob("*.jpg");

Jeśli chcemy pobrać pliki z danego katalogu i jego podkatalogów trzeba sobie dopisać wersję rekurencyjną funkcji glob:

function glob_recur($pattern='*', $flags = 0, $path = '')
{
    $paths = glob($path.'*', GLOB_MARK|GLOB_ONLYDIR|GLOB_NOSORT);
    $files = glob($path.$pattern, $flags);
    foreach ($paths as $path) {
        $files = array_merge($files, glob_recur($pattern, $flags, $path));
    }
    return $files;
}

Funkcja glob umożliwia użycie tzw. wilkardów czyli w najprostszym ujęciu pytanik „?” zastępuje dowolny znak, a gwiazdka „*” dowolny ciąg znaków. Wilkardy to nie wyrażenia regularne i szybko odczujemy różnicę choćby w przypadku, kiedy chcielibyśmy pobrać pliki o rozszerzeniu np. css i js. W takiej sytuacji musielibyśmy dwa razy wywołać funkcję glob. Do dyspozycji mamy jednak funkcję [scandir](http://pl.php.net/manual/en/function.scandir.php), która ułatwia listowanie katalogu.

function get_files_from_dir($path, $extensions = array())
{
    $entries = scandir($path.'/');
    $files = array();
    foreach($entries as $entry)
    {
        if ($entry == '.' || $entry == '..') continue;
 
        $_path = $path.'/'.$entry;
        if(is_file($_path)) {
            if (!empty($extensions))
            {
                $ext = pathinfo($filename, PATHINFO_EXTENSION);
                if (!in_array($ext, $extensions)) continue;
            }
            $files[] = $_path;
        }
    }
    return $files;
}

Scandir wybierze wszystkie pliki i katalogi z zadanej lokalizacji. Odróżnienie plików od katalogów to prosty tekst przy użyciu funkcji is_file lub is_dir z kolei wydobycie rozszerzenia z pliku można przeprowadzić na kilka sposobów:

$ext = pathinfo($filename, PATHINFO_EXTENSION);
// lub
$ext = preg_replace('/.*\.([\d\w]{2,5})/i', '$1', $filename); //zakładam, że rozszerzenie ma od 2 do 5 znaków
// lub 
$ext = substr($filename , strrpos($filename , '.') +1);

Modyfikacja funkcji get_files_from_dir tak aby działała rekurencyjnie wymaga jedynie niewielkiej zmiany:

function get_files_from_dir_recur($path, $extensions = array())
{
    $entries = scandir($path.'/');
    $files = array();
    foreach($entries as $entry)
    {
        if ($entry == '.' || $entry == '..') continue;
 
        $_path = $path.'/'.$entry;
        if(is_file($_path)) {
            if (!empty($extensions))
            {
                $ext = pathinfo($filename, PATHINFO_EXTENSION);
                if (!in_array($ext, $extensions)) continue;
            }
            $files[] = $_path;
        } else if (is_dir($_path)) {
            $files = array_merge($files, self::get_files_from_dir_recur($path, $extensions));
        }
    }
    return $files;
}

W zasadzie to samo można osiągnąć za pomocą iteratorów z biblioteki [SPL](http://www.php.net/~helly/php/ext/spl/):

function get_files_from_dir($path, $extensions = array())
{
    $files = array();
    $it = new DirectoryIterator($path);
    foreach($it as $file) {
        $filename = $file->getFilename();
        $ext = pathinfo($filename, PATHINFO_EXTENSION);
        if ($file->isFile() && (empty($extensions) || in_array($ext, $extensions))) {
            $files[] = $file->getPathname();
        }
    }
    return $files;
}

i wersja rekurencyjna

function get_files_from_dir_recur($path, $extensions = array())
{
    $files = array();
    $it = new RecursiveDirectoryIterator($path);
    foreach(new RecursiveIteratorIterator($it) as $file) {
        $filename = $file->getFilename();
        $ext = pathinfo($filename, PATHINFO_EXTENSION);
        if ($file->isFile() && (empty($extensions) || in_array($ext, $extensions))) {
            $files[] = $file->getPathname();
        }
    }
    return $files;
}

Funkcja glob jest jest najprostsza w użyciu. Funkcja z użyciem scandir sprawdza się kiedy glob zawodzi i jest szybsza od iteratorów, które dla każdego znalezionego pliku powołują do życia obiekt.

Kiedy sięgać po iteratory i czy w ogóle? W podanych przeze mnie przykładach interesują nas jedynie ścieżki do plików, tymczasem iteratory zwracają pliki w formie obiektów [SplFileInfo](http://pl.php.net/manual/en/class.splfileinfo.php) z których można wyciągnąć dużo więcej informacji o plikach. Niekiedy może się to przydać i wtedy użycie iteratorów jest zasadne. Jeśli jednak chcemy otrzymać jedynie listę plików wtedy scandir jest lepszym rozwiązaniem bo szybszym.

Funkcja scandir i iteratory są dostępne dopiero od 5 wersji PHP. W PHP4 zawartość katalogu możymy odczytać dzięki funkcji [opendir](http://pl.php.net/manual/en/function.opendir.php).

function get_files_from_dir($path, $extensions = array()) {
    $files = array();
    if (!is_dir($path)) {
        return $files;
    }
    if ($dh = opendir($path)) {
        while (($file = readdir($dh)) !== false) {
            $ext = pathinfo($file, PATHINFO_EXTENSION);
            if (empty($extensions) || in_array($ext, $extensions)) {
                $files[] = $path.'/'.$file;
            }
        }
        closedir($dh);
    }
    return $files;
}
Posted in PHP | Tagged byZbigniew Heintze