PHP Directory
PHP Directory: полное справочное руководство для PHP-разработчиков.
PHP поставляется с набором встроенных функций для работы с директориями, которые позволяют скриптам проверять, создавать, читать и удалять папки в файловой системе сервера — без обращения к операционной системе напрямую. На этой странице описано, что делает каждая основная функция, когда её применять и каких распространённых ошибок следует избегать.
Это дополнение к PHP File Handling и более широкому справочнику PHP Filesystem: файлы находятся внутри директорий, поэтому обе темы почти всегда используются вместе.
Зачем нужны операции с директориями
Практически каждое нетривиальное приложение обращается к файловой системе. Функции для работы с директориями необходимы для:
- Организации загруженных файлов — сортировки загрузок пользователей по папкам на основе пользователя или даты (см. PHP File Upload).
- Создания структуры проекта на лету — генерации папок кэша, логов или экспорта при первом обращении к ним.
- Перебора содержимого — получения списка всех шаблонов, изображений или файлов данных в папке для пакетной обработки.
- Очистки — удаления временных директорий после завершения задачи.
Поскольку эти функции входят в ядро PHP, они одинаково работают на Linux, macOS и Windows (везде принимается / в качестве разделителя), что делает их более переносимыми по сравнению с вызовом mkdir или ls через exec().
Основные функции для работы с директориями
| Функция | Назначение |
|---|---|
mkdir($path, $mode, $recursive) | Создать директорию. Передайте true для $recursive, чтобы создать все отсутствующие родительские папки. |
rmdir($path) | Удалить директорию — она должна быть пустой. |
is_dir($path) | Вернуть true, если путь существует и является директорией. |
scandir($path) | Вернуть массив всех элементов директории (включая . и ..). |
opendir() / readdir() / closedir() | Открыть дескриптор директории и читать элементы по одному. |
getcwd() | Вернуть текущую рабочую директорию. |
chdir($path) | Изменить текущую рабочую директорию. |
scandir() vs. opendir()
Обе функции выводят содержимое директории, но подходят для разных задач:
scandir()загружает все элементы в массив сразу. Код получается лаконичным и легко сортируется, но расходует память пропорционально числу файлов. Лучше всего подходит для папок малого и среднего размера.opendir()/readdir()передают элементы по одному. Это обеспечивает постоянный расход памяти даже для директорий с десятками тысяч файлов. Лучше всего подходит для очень больших папок.
Обе функции включают специальные записи . (текущая директория) и .. (родительская директория) — их почти всегда нужно пропускать.
Пример: создание директории и вывод её содержимого
Этот скрипт проверяет, существует ли директория, создаёт её (вместе с отсутствующими родительскими папками) если нет, а затем выводит её содержимое:
<?php
$dir = 'uploads/images';
// Create the directory and any missing parents if it doesn't exist
if (!is_dir($dir)) {
mkdir($dir, 0755, true);
echo "Directory created: $dir\n";
}
// List directory contents, skipping the . and .. entries
$files = scandir($dir);
echo "Contents of $dir:\n";
foreach ($files as $file) {
if ($file !== '.' && $file !== '..') {
echo "- $file\n";
}
}
?>Третий аргумент mkdir() (true) — это флаг рекурсивного создания: без него mkdir('uploads/images') завершится ошибкой, если uploads ещё не существует. 0755 — это восьмеричный код разрешений (владелец может читать/писать/выполнять, остальные — читать/выполнять) и игнорируется в Windows.
Пример: потоковое чтение с opendir()
Для больших директорий читайте элементы по одному, не формируя полный массив:
<?php
$dir = __DIR__;
if ($handle = opendir($dir)) {
while (($entry = readdir($handle)) !== false) {
if ($entry !== '.' && $entry !== '..') {
$type = is_dir("$dir/$entry") ? 'dir ' : 'file';
echo "[$type] $entry\n";
}
}
closedir($handle);
}
?>Обратите внимание на строгое сравнение !== false: файл с буквальным именем "0" является ложным значением, поэтому нестрогое условие while ($entry = readdir(...)) завершится раньше времени. Всегда сравнивайте явно с false.
Удаление директорий
rmdir() удаляет только пустую директорию. Чтобы удалить директорию с файлами, сначала очистите её содержимое — обычно с помощью рекурсивного вспомогательного метода:
<?php
function removeDir(string $dir): void {
foreach (scandir($dir) as $entry) {
if ($entry === '.' || $entry === '..') {
continue;
}
$path = "$dir/$entry";
is_dir($path) ? removeDir($path) : unlink($path);
}
rmdir($dir);
}
?>Этот код перебирает каждый элемент, рекурсивно обходя поддиректории и удаляя файлы через unlink(), поэтому к моменту вызова rmdir() директория уже пуста.
Распространённые ошибки
mkdir()без флага рекурсивного создания завершается ошибкой, если какая-либо родительская папка отсутствует. Передавайтеtrueпри создании вложенных путей.- Разрешения вычитаются с учётом
umask— фактический режим равен$mode & ~umask(), поэтому0777может стать0755. rmdir()на непустой папке возвращаетfalseи выдаёт предупреждение. Сначала очистите её.scandir()включает.и..— фильтруйте их перед обработкой.- Относительные пути зависят от
getcwd(), то есть от рабочей директории скрипта, а не от расположения файла. Используйте__DIR__для привязки путей к текущему файлу.
Работа с директориями: схема
graph TD
A[Check Directory] --> B{Exists?}
B -->|No| C[mkdir]
B -->|Yes| D[scandir / opendir]
C --> E[Create Subdirectories]
D --> F[Read Entries]
F --> G[Process Files]
G --> H[close / rmdir]Итог
Функции PHP для работы с директориями обеспечивают переносимый встроенный способ управления папками: mkdir() и rmdir() создают и удаляют их, is_dir() проверяет их существование, а scandir() или opendir()/readdir() выводят их содержимое. Используйте scandir() для небольших папок из-за его простоты, а потоковое чтение через opendir() — для больших, чтобы избежать лишних затрат памяти. Комбинируйте эти функции с функциями для работы с файлами PHP и обработкой файлов для создания надёжных функций загрузки, кэширования и экспорта.