Информация о каталоге в PHP: обзор инструментов для разработчиков

Раздел: Разработка каталогов на PHP -> Информация о каталоге

Основные способы получения информации о каталоге в PHP

Как получить полную информацию о файлах внутри каталога, включая размер, дату модификации и права доступа, используя современный объектно-ориентированный подход?

Наиболее эффективным решением является использование класса DirectoryIterator. Он предоставляет удобный интерфейс для итерации по записям каталога, а каждый объект итератора реализует SplFileInfo, что даёт доступ к метаданным файла без дополнительных вызовов stat().


<?php
$path = '/var/www/project';
$iterator = new DirectoryIterator($path);

foreach ($iterator as $fileinfo) {
    if ($fileinfo->isDot()) continue; // пропустить . и ..
    
    $name = $fileinfo->getFilename();
    $size = $fileinfo->getSize();
    $perms = substr(sprintf('%o', $fileinfo->getPerms()), -4);
    $mtime = date('Y-m-d H:i:s', $fileinfo->getMTime());
    
    echo "$name | $size bytes | $perms | $mtime\n";
}
?>
    

Возможные проблемы: Если путь не существует или нет прав на чтение, выбрасывается исключение UnexpectedValueException. Следует оборачивать код в try-catch. Также следует помнить, что DirectoryIterator включает записи . и .., которые необходимо отфильтровывать.

Типичная ошибка: Использование $fileinfo->getPathname() как строки без проверки isDot(). Это может привести к ложным результатам при попытке открыть '.' в качестве файла.

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

Как просто получить массив имён файлов и папок без дополнительных атрибутов?

Функция scandir() возвращает отсортированный массив записей каталога. Это самый быстрый способ получить плоский список.


<?php
$files = scandir('/path/to/dir');
$files = array_diff($files, array('.', '..'));
print_r($files);
?>
    

Проблемы: scandir() не предоставляет никакой информации о файлах, кроме имени. Порядок сортировки по умолчанию алфавитный; для другого порядка нужно передавать второй параметр. Ошибка при отсутствии каталога возвращает false, а не исключение.

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

Как отфильтровать файлы по шаблону, например, все изображения .jpg?

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


<?php
$images = glob('/path/to/photos/*.{jpg,jpeg,png}', GLOB_BRACE);
foreach ($images as $img) {
    echo basename($img) . " - " . filesize($img) . " bytes\n";
}
?>
    

Проблемы: glob() не рекурсивна по умолчанию. Для обхода подкаталогов требуется GLOB_RECURSIVE (доступен с PHP 8.1) или ручной вызов. Также при большом количестве файлов может быть медленнее итераторов.

Типичная ошибка: Ожидание, что GLOB_BRACE работает во всех системах – на Windows может потребоваться явное указание расширений.

Случай использования: Когда нужно быстро собрать файлы определённого типа для пакетной обработки (например, все изображения, которые нужно ресайзить).

Как получить информацию о файлах с помощью низкоуровневых функций opendir/readdir?

Классический подход: opendir(), затем readdir() в цикле, и для каждого элемента вызывать stat() или fileinfo. Это даёт полный контроль, но требует больше кода.


<?php
$dh = opendir('/var/www');
if ($dh) {
    while (($file = readdir($dh)) !== false) {
        if ($file == '.' || $file == '..') continue;
        $path = '/var/www/' . $file;
        $stat = stat($path);
        echo $file . ' - size: ' . $stat['size'] . ', mode: ' . substr(sprintf('%o', $stat['mode']), -4) . "\n";
    }
    closedir($dh);
}
?>
    

Проблемы: Необходимо вручную управлять ресурсом и закрывать дескриптор. Ошибка открытия каталога не генерирует исключение, а возвращает false. Для каждого файла выполняется отдельный системный вызов stat(), что при больших каталогах может быть медленнее DirectoryIterator.

Случаи использования: Когда требуется совместимость с очень старыми версиями PHP или необходимо очень специфическое управление потоком чтения.

Как рекурсивно получить информацию обо всех файлах внутри вложенных каталогов?

Класс RecursiveDirectoryIterator в комбинации с RecursiveIteratorIterator позволяет обойти всё дерево каталогов.


<?php
$path = '/var/www';
$rdi = new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS);
$iterator = new RecursiveIteratorIterator($rdi, RecursiveIteratorIterator::SELF_FIRST);

foreach ($iterator as $fileinfo) {
    if ($fileinfo->isFile()) {
        echo $fileinfo->getPathname() . "\n";
    }
}
?>
    

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

Типичная ошибка: Забывание установить SKIP_DOTS приводит к появлению '.' и '..'.

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

Расширенные практические примеры

Пример 1: Подсчёт общего размера каталога (рекурсивный)

Используем RecursiveDirectoryIterator для суммирования размеров всех файлов.

Пример

<?php
function dirSize($path) {
    $totalSize = 0;
    $rdi = new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS);
    $iterator = new RecursiveIteratorIterator($rdi, RecursiveIteratorIterator::LEAVES_ONLY);
    
    foreach ($iterator as $fileinfo) {
        if ($fileinfo->isFile()) {
            $totalSize += $fileinfo->getSize();
        }
    }
    return $totalSize;
}

echo 'Размер /var/www: ' . dirSize('/var/www') . ' bytes';
?>
    
Размер /var/www: 128937459 bytes
    

Пояснение: Флаг LEAVES_ONLY гарантирует, что мы обращаемся только к файлам, пропуская каталоги. Такой подход точнее, чем суммирование по всем элементам.

Пример 2: Вывод дерева каталогов с отступами

Рекурсивно обходим структуру и форматируем вывод с учётом уровня вложенности.

Пример

<?php
function listDirTree($path, $indent = '') {
    $dir = new DirectoryIterator($path);
    foreach ($dir as $fileinfo) {
        if ($fileinfo->isDot()) continue;
        $name = $fileinfo->getFilename();
        $size = $fileinfo->isFile() ? ' (' . $fileinfo->getSize() . ' B)' : '';
        echo $indent . ($fileinfo->isDir() ? '[DIR] ' : '[FILE] ') . $name . $size . "\n";
        if ($fileinfo->isDir() && !$fileinfo->isLink()) {
            listDirTree($fileinfo->getPathname(), $indent . '    ');
        }
    }
}

listDirTree('/var/www');
?>
    
[FILE] index.php (2048 B)
[DIR] public
    [FILE] style.css (4096 B)
    [DIR] images
        [FILE] logo.png (10240 B)
    

Пояснение: Использован рекурсивный вызов для подкаталогов. Важно учитывать символические ссылки, чтобы избежать бесконечных циклов – в примере проверка !$fileinfo->isLink().

Пример 3: Поиск всех файлов с определённым расширением с помощью итератора

Используем RegexIterator для фильтрации.

Пример

<?php
$path = '/var/www';
$rdi = new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS);
$rit = new RecursiveIteratorIterator($rdi, RecursiveIteratorIterator::SELF_FIRST);
$regex = new RegexIterator($rit, '/\.php$/i', RecursiveRegexIterator::GET_MATCH);

foreach ($regex as $pathname => $matches) {
    echo $pathname . "\n";
}
?>
    
/var/www/index.php
/var/www/includes/functions.php
/var/www/admin/config.php
    

Пояснение: RegexIterator применяет регулярное выражение к текущему пути. Параметр GET_MATCH возвращает совпадение. Это удобная альтернатива glob() для рекурсивного поиска.

Пример 4: Получение информации о файлах в формате JSON

Собираем метаданные и сериализуем в JSON для API.

Пример

<?php
function getDirInfoJson($path) {
    $result = [];
    $iterator = new DirectoryIterator($path);
    foreach ($iterator as $info) {
        if ($info->isDot()) continue;
        $result[] = [
            'name' => $info->getFilename(),
            'type' => $info->isDir() ? 'dir' : 'file',
            'size' => $info->getSize(),
            'mtime' => $info->getMTime(),
            'perms' => $info->getPerms()
        ];
    }
    return json_encode($result, JSON_PRETTY_PRINT);
}

echo getDirInfoJson('/var/www');
?>
    
[
    {
        "name": "index.php",
        "type": "file",
        "size": 2048,
        "mtime": 1680000000,
        "perms": 33188
    },
    {
        "name": "public",
        "type": "dir",
        "size": 4096,
        "mtime": 1680000100,
        "perms": 16877
    }
]
    

Пояснение: Для вывода прав доступа числовой код можно преобразовать в строку, но в JSON удобнее оставить числом. Абсолютные метки времени можно потом преобразовать на клиенте.

Информация о каталоге PHP - comments

En
Catalog php info (php)