Итерация файлов и каталогов в PHP

Источник: «Iterate files and directories in PHP – Fast tips»
Недавно мне пришлось решать задачи, требующие знания сырых функций PHP, таких, как итерация файлов и каталогов. По мере развития все более высоких уровней абстракции мы часто перестаём помнить, как работать на низком уровне. В реальности это имеет множество последствий, особенно с точки зрения производительности.

По мере проведения этих оптимизаций я замечаю, что моё приложение поддерживает растущие нагрузки, несмотря на снижение потребления аппаратных ресурсов. Надеюсь, что приведённые ниже фрагменты помогут не только молодым разработчикам, но и опытным профессионалам, которые ищут идеи по оптимизации производительности.

В PHP существует несколько вариантов итерации/перебора файлов и каталогов:

Метод 1: Использование glob()

Функция glob извлекает файлы и каталоги, соответствующие шаблону. Она полезна, когда необходимо задать некое правило для извлечения элементов из структуры файловой системы.

Для получения только каталогов можно также передать второй параметр в качестве константы.

<?php
$directory = 'path/to/directory'; /// Заменить на фактический путь к каталогу
// Извлечение файлов и каталогов, соответствующих шаблону
$items = glob($directory . '/*');
foreach ($items as $item) {
if (is_file($item)) {
echo "File: {$item}\n";
}
if (is_dir($item)) {
echo "Directory: {$item}\n";
}
}
// Итерация по директориям
$directories = glob($directory . '/*', GLOB_ONLYDIR);
foreach ($directories as $dir) {
echo "Directory: $dir\n";
}

Способ 2: Использование класса RecursiveDirectoryIterator

Очевидно, что это более объектно-ориентированный подход.

<?php
$directory = 'path/to/directory'; // Заменить на фактический путь к каталогу
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($directory, RecursiveDirectoryIterator::SKIP_DOTS),
RecursiveIteratorIterator::SELF_FIRST
);
foreach ($iterator as $item) {
$path = $item->getPathname();
if ($item->isFile()) {
echo "File: {$path}\n";
} elseif ($item->isDir()) {
echo "Directory: {$path}\n";
}
}

Метод 3: Использование scandir()

Функция scandir() — это ещё одна возможность итерации файлов и каталогов в PHP. Она возвращает массив имён файлов и каталогов, включая текущий каталог (.) и родительский каталог (..).

<?php
$directory = 'path/to/directory'; // Заменить на фактический путь к каталогу
$entries = scandir($directory);
foreach ($entries as $entry) {
if ($entry !== '.' && $entry !== '..') {
$path = $directory . '/' . $entry;
if (is_file($path)) {
echo "File: {$path}\n";
} elseif (is_dir($path)) {
echo "Directory: {$path}\n";
}
}
}

В данном примере цикл foreach перебирает все записи, исключая текущий каталог (.) и родительский каталог (..). Переменная $path строится путём конкатенации пути ($directory) и имени файла или каталога ($entry).

Метод 4: Использование генераторов

Генераторы в PHP могут использоваться для итерационного перебора файлов и каталогов. Генераторы обеспечивают эффективный способ генерации последовательности значений на лету, что удобно при работе с большими наборами файлов или каталогов. Узнать больше о генераторах на практических примерах можно в статье: PHP генераторы — практический пример

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

<?php
// Функция, возвращающая yield
function iterateFiles($directory) {
$files = scandir($directory);
foreach ($files as $file) {
if ($file !== '.' && $file !== '..') {
$path = $directory . '/' . $file;
if (is_file($path)) {
yield $path;
}
}
}
}
$directory = 'path/to/directory'; // Заменить на фактический путь к каталогу
$fileGenerator = iterateFiles($directory);
foreach ($fileGenerator as $file) {
echo "File: $file\n";
}

Напомним, что любая функция, возвращающая yield, является генератором. Как объясняется в официальной документации:

Когда вызывается генератор, он возвращает объект, который можно итерировать. Когда вы итерируете этот объект (например, в цикле foreach), PHP вызывает методы итерации объекта каждый раз, когда вам нужно новое значение, после чего сохраняет состояние генератора и при следующем вызове возвращает следующее значение.

Сохранение и возобновление состояния генератора позволяет сверхэффективно использовать системную память.

Дополнительные материалы

Предыдущая Статья

Использование UUID для предотвращения атак методом перебора

Следующая Статья

Избегайте AOP: Array-Oriented Programming