Листинг директорий - это получение информации о каталогах и файлах для заданной родительской директории, а также возможность применить к этим данным различные фильтры для коррекции вывода.

В данном примере мы попробуем справиться с типовой задачей, которая появляется почти в каждом PHP проекте - получение списка директорий и(или) файлов. В примере используются несколько базовых и более сложный подход, с обозначением плюсов и минусов каждого приема. Первые три решения используют типовые функции PHP. Последнее более надежное решение с применением PHP итераторов SPL.

Для более наглядного представления используем структуру директорий, которая имеет следующий вид:

\-Application
|
\-User
|  \-data.apk
|  \-style.css
|  \-test.txt
|-readme.txt
|-script.php
|-serial.txt
|-test.html
|-test.js
Базовые решения
Первый набор примеров использует функции glob() и комбинации функций opendir(), readdir(), closedir(), а также функцию scandir().

Использование glob()

Пример использования php функции glob(), которая позволяет выполнять поиск пути по шаблону.
Функция glob($pattern,$flags) оперирует двумя аргументами:
  • $pattern (обязательный): строка шаблона поиска
  • $flags (не обязательный): один или несколько флагов
    • GLOB_MARK - Добавляет слеш к каждой возвращаемой директории.
    • GLOB_NOSORT - Возвращает файлы в том виде, в котором они содержатся в директории (без сортировки). Если этот флаг не указан, то имена сортируются по алфавиту.
    • GLOB_NOCHECK - Возвращает шаблон поиска, если с его помощью не был найден ни один файл.
    • GLOB_NOESCAPE - Обратные слеши не экранируют мета символы.
    • GLOB_BRACE - Раскрывает {a,b,c} для совпадения с 'a', 'b' или 'c'.
    • GLOB_ONLYDIR - Возвращает только директории, совпадающие с шаблоном.
    • GLOB_ERR - Останавливается при ошибках чтения (например, директории без права чтения), по умолчанию ошибки игнорируются.
Для поиска в директории всех файлов и директорий, имена которых заканчиваются на .txt, следует используется код:
<?php
$arFileList = glob("*.txt");
//Выводим результат
var_dump($arFileList);
?>
В выводе получим такой результат:
array(2) {
  [0]=>
  string(10) "readme.txt"
  [1]=>
  string(10) "serial.txt"
}
Если необходимо получить список файлов и директорий, имена которых начинаются на "te":
<?php
$arFileList = glob("te*");
//Выводим результат
var_dump($arFileList);
?>
В выводе получим такой результат:
array(2) {
  [0]=>
  string(9) "test.html"
  [1]=>
  string(7) "test.js"
}
Получение в списке только директорий с именами, содержащими "er":
<?php
$arFileList = glob("*er*", GLOB_ONLYDIR);
//Выводим результат
var_dump($arFileList);
?>
В выводе получим такой результат:
array(1) {
  [0]=>
  string(4) "User"
}
В последнем примере использован флаг GLOB_ONLYDIR в качестве второго аргумента функции. Поэтому в список попала, только директория "User" в имени, которой встречается "er". Функция glob() очень проста в использовании, но иногда она недостаточно гибкая. Нет флага для получения только файлов (без директорий), которые соответствуют шаблону.

Использование opendir(), readdir(), и closedir().

Следующий метод получения списка файлов и директорий, заключается в использовании PHP функций opendir(), readdir() и closedir().
Функция opendir() возвращает дескриптор открытой директории. После того как дескриптор получен, можно использовать функцию readdir(). При обращении к дескриптору функция readdir() выдает имя следующего файла или директории. Если все элементы содержащиеся в дескрипторе уже были перечислены, функция readdir() вернет false. Для закрытия дескриптора используем функцию closedir().

В отличие от использования php функции glob(), данный подход немного сложнее. Отсутствует возможность задать параметры фильтрации, которые помогают заранее формировать список возвращаемых имен файлов и директорий. Для получения необходимого списка файлов и директорий, фильтрацию должна быть выполнена самостоятельно.

Приведенный пример возвращает список имен файлов и директорий начинающихся на "Us":
<?php
$arFileList = array();
$resHandler = opendir(".");
if(is_resource($resHandler)):
	while($FileName = readdir($resHandler)):
		if(strpos($FileName,"Us")===0):
			$arFileList[] = $FileName;
		endif;
	endwhile;
	closedir($resHandler);
endif;
//Выводим результат
var_dump($arFileList);
?>
В выводе получим такой результат:
array(1) {
  [0]=>
  string(4) "User"
}
Следующий пример выведет только файлы, содержащиеся в заданном каталоге.
<?php
$arFileList = array();
$resHandler = opendir(".");
if(is_resource($resHandler)):
	while($FileName = readdir($resHandler)):
		if(is_file($FileName)):
			$arFileList[] = $FileName;
		endif;
	endwhile;
	closedir($resHandler);
endif;
//Выводим результат
var_dump($arFileList);
?>
В выводе получим такой результат:
array(5) {
  [0]=>
  string(10) "script.php"
  [1]=>
  string(7) "test.js"
  [2]=>
  string(9) "test.html"
  [3]=>
  string(10) "serial.txt"
  [4]=>
  string(10) "readme.txt"
}

Использование scandir().

Для завершения посмотрим пример использования php функции scandir(). У нее есть только один обязательный атрибут - путь к директории для чтения. Результатом работы функции является массив файлов и директорий, расположенных по заданному в аргументе пути. Как и в предыдущем примере для получения отфильтрованного списка файлов и директорий необходимо выполнить ее самостоятельно. Визуально решение получается более коротким и не требуется управление дескрипторами.

Пример показывает, как получить список файлов и каталогов, имена которых начинаются на "te":
<?php
$arFileName = scandir(".");
$arFileList = array();
foreach($arFileName as $FileName):
	if(strpos($FileName,"te")===0):
		$arFileList[] = $FileName;
	endif;
endforeach;
//Выводим результат
var_dump($arFileList);
?>
В выводе получим такой результат:
array(2) {
  [0]=>
  string(9) "test.html"
  [1]=>
  string(7) "test.js"
}
Продвинутое решение c использованием PHP SPL
Более надежное решение с применением SPL итераторов FilesystemIterator, RecursiveDirectoryIterator и GlobIterator.

Использование итераторов SPL.

Рассмотрим использование итераторов SPL. До того как приступить к решению задачи, немного познакомимся с php библиотекой SPL и итераторами. SPL библиотека предоставляет из себя специализированные наборы классов для объектно ориентированных структур данных, итераторов, дескрипторов файлов и прочее.

Основное преимущество итераторов заключается в том, что они являются классами и их можно расширить используя стандартный механизм php наследования классов. Еще один плюс заключается в том, что итераторы имеют собственные методы, которые могут быть полезны при решении типовых задач и все они расположены в одном месте. Посмотрим на примере использования FilesystemIterator и сравним с readdir(). Оба метода используют цикл, но в случае readdir() получится обрабатывать только строку, а FilesystemIterator может работать с объектом. В котором может содержатся дополнительная информацию о файле или директории такие как владелец, размер, права доступа и так далее.

Разумеется у PHP есть возможности для получения этой информации с помощью функций, filesize(), fileowner() и других. Но PHP как и любой язык программирования имеет свойства меняться. В PHP5 все больше прослеживается стремление задействовать концепции ООП. Поэтому лучше использовать современные методы работы с языком программирования.

Рассмотрим использование FilesystemIterator, RecursiveDirectoryIterator и GlobIterator. Первый итератор наследуется от DirectoryIterator, а остальные от FilesystemIterator. Все они имеют один и тот же конструктор, который принимает два аргумента:
  • $path (обязательный): путь к пункту файловой системы, над которым совершаются операции
  • $flags (не обязательный): один или несколько флагов
    • FilesystemIterator::CURRENT_AS_PATHNAME Заставляет метод FilesystemIterator::current() вернуть путь.
    • FilesystemIterator::CURRENT_AS_FILEINFO Заставляет метод FilesystemIterator::current() вернуть экземпляр SplFileInfo.
    • FilesystemIterator::CURRENT_AS_SELF Заставляет метод FilesystemIterator::current() вернуть $this (FilesystemIterator).
    • FilesystemIterator::CURRENT_MODE_MASK Маскирует FilesystemIterator::current()
    • FilesystemIterator::KEY_AS_PATHNAME Заставляет метод FilesystemIterator::key() вернуть путь.
    • FilesystemIterator::KEY_AS_FILENAME Заставляет метод FilesystemIterator::key() вернуть имя файла.
    • FilesystemIterator::FOLLOW_SYMLINKS Заставляет метод RecursiveDirectoryIterator::hasChildren() следовать символическим ссылкам.
    • FilesystemIterator::KEY_MODE_MASK Маскирует FilesystemIterator::key()
    • FilesystemIterator::NEW_CURRENT_AND_KEY Тоже, что FilesystemIterator::KEY_AS_FILENAME | FilesystemIterator::CURRENT_AS_FILEINFO.
    • FilesystemIterator::SKIP_DOTS Пропускает точечные файлы (. and ..).
    • FilesystemIterator::UNIX_PATHS Заставляет все пути использовать обратный слеш в Unix-стиле, независимо от настроек системы по умолчанию.
Различие в данных итераторах заключается в их использовании для навигации по заданному пути.

FilesystemIterator

Использовать FilesystemIterator очень просто.
Пример показывает поиск всех файлов и каталогов, имена которых начинаются на "te".
<?php
$obIterator = new FilesystemIterator(".");
$arFileList = array();
foreach($obIterator as $obFile):
	if(strpos($obFile->getFilename(),"te")===0):
		$arFileList[] = $obFile->getFilename();
	endif;
endforeach;
//Выводим результат
var_dump($arFileList);
?>
В выводе получим такой результат:
array(2) {
  [0]=>
  string(7) "test.js"
  [1]=>
  string(9) "test.html"
}
Пример использования другого итератора RegexIterator для поиска всех файлов и каталогов, имена которых заканчиваются на "t.js" или "t.php". Итератор RegexIterator используется для фильтрации результата и использует механизм регулярных выражений.
<?php
$obIterator = new FilesystemIterator(".");
$rxIterator = new RegexIterator($obIterator,'/t\.(php|js)$/');
$arFileList = array();
foreach($rxIterator as $obFile):
	$arFileList[] = $obFile->getFilename();
endforeach;
//Выводим результат
var_dump($arFileList);
?>
В выводе получим такой результат:
array(2) {
  [0]=>
  string(10) "script.php"
  [1]=>
  string(7) "test.js"
}

RecursiveDirectoryIterator

Итератор RecursiveDirectoryIterator обеспечивает интерфейс для рекурсивного прохода по директориям файловой системы. Он имеет несколько полезных методов, таких как getChildren() и hasChildren(), которые возвращают итератор для текущего места, если это директория, и проверяют, является ли текущая точка входа директорией.

Пример демонстрирует использование RecursiveDirectoryIterator и getChildren().
<?php
$obIterator = new RecursiveDirectoryIterator('.');
$rxIterator = new RegexIterator($obIterator->getChildren(), '/t\.(txt|css)$/');
$arFileList = array();
foreach($rxIterator as $obFile):
	$arFileList[] = $obFile->getFilename();
endforeach;
//Выводим результат
var_dump($arFileList);
?>
В выводе получим такой результат в данном случае - это файл из директории "User":
array(1) {
  [0]=>
  string(8) "test.txt"
}

GlobIterator

Итератор GlobIterator выполняет проход по файловой аналогично функции glob(). Первый атрибут может включать шаблон имени.

Пример демонстрирует использование GlobIterator с тем же результатом, что и ранее.
<?php
$obIterator = new GlobIterator("te*");
$arFileList = array();
foreach($obIterator as $obFile):
	$arFileList[] = $obFile->getFilename();
endforeach;
//Выводим результат
var_dump($arFileList);
?>
В выводе получим такой результат:
array(2) {
  [0]=>
  string(10) "/test.html"
  [1]=>
  string(8) "/test.js"
}

Заключение

В приведенных выше примерах были рассмотрены различные методы PHP для достижения одной и той же цели: получение списка файлов и директорий.
Из примеров можно выделить следующие основные моменты:
  • Простое решение на основе функции glob() недостаточно гибкое.
  • Более сложное решение на основе функций opendir(), readdir(), и closedir() требует дополнительной фильтрации, но более гибкое.
  • Функция scandir() работает без обработки дескриптора, но требует дополнительной фильтрации при необходимости.
  • Используя подход ООП следует применять библиотеку SPL. Дополнительно можно расширить классы собственными методами.
  • Итератор GlobIterator имеет функцию предварительной фильтрации. Другие используют RegexIterator.