Как стереть папку в PHP: основные подходы
Основные подходы к удалению каталогов
Наиболее эффективным считается рекурсивное удаление с проверками на каждом уровне. В PHP встроенная функция rmdir() удаляет только пустые каталоги. Для удаления непустой директории применяется рекурсивное обход с удалением файлов и подкаталогов.
Пример:
function removeDirectory($dir) {
if (!is_dir($dir)) {
return false;
}
$items = array_diff(scandir($dir), array('.', '..'));
foreach ($items as $item) {
$path = $dir . DIRECTORY_SEPARATOR . $item;
if (is_dir($path)) {
removeDirectory($path);
} else {
unlink($path);
}
}
return rmdir($dir);
}
Php удалить каталог (удаление каталога в php)
// Пример вызова:
// removeDirectory('/path/to/dir'); // true при успехе
Типичные ошибки:
Permission denied– недостаточно прав на чтение или запись. Решение: проверить владельца и установить праваchmod()внутри рекурсии.Directory not empty– это штатное поведениеrmdir(), для непустых каталогов обязательно предварительное удаление содержимого.- Бесконечная рекурсия при наличии символических ссылок, указывающих на родительский каталог. Решение: проверять
is_link()и удалять ссылки без рекурсии черезunlink().
Как удалить пустой каталог?
Применяется только rmdir($dir). Функция возвращает true при успехе, false при ошибке. Пример:
$dir = '/tmp/empty_folder';
if (is_dir($dir) && count(scandir($dir)) == 2) {
rmdir($dir);
}
Проблема: если каталог содержит скрытые файлы (например, .htaccess), scandir их увидит и условие не сработает. Решение: использовать glob($dir . '/*') или FilesystemIterator.
Как удалить каталог вместе со всем содержимым используя рекурсивную функцию?
Рекурсивное удаление – универсальный способ, работающий на любой платформе. Пример выше (в rbase) является базовым. Улучшенная версия с обработкой ошибок:
function deleteDir($dir) {
if (!is_dir($dir)) {
return false;
}
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS),
RecursiveIteratorIterator::CHILD_FIRST
);
foreach ($iterator as $fileinfo) {
if ($fileinfo->isDir()) {
rmdir($fileinfo->getRealPath());
} else {
unlink($fileinfo->getRealPath());
}
}
return rmdir($dir);
}
Ошибка: при использовании RecursiveDirectoryIterator без SKIP_DOTS возникает рекурсивный захват '.' и '..'. Всегда следует применять FilesystemIterator::SKIP_DOTS.
Как удалить каталог с помощью системной команды?
В Unix-подобных системах можно вызвать exec('rm -rf ' . escapeshellarg($dir)). Этот способ быстрый, но менее безопасный.
function deleteWithSystem($dir) {
$escaped = escapeshellarg($dir);
exec("rm -rf $escaped 2>&1", $output, $return_var);
return ($return_var === 0);
}
Проблемы: команда зависит от ОС, на Windows не работает. Также есть риск выполнения произвольных команд при неправильном экранировании. Требуется отключение exec() во многих хостингах.
Как удалить каталог с использованием SPL итераторов?
Более элегантный способ – RecursiveDirectoryIterator в сочетании с RecursiveIteratorIterator в режиме CHILD_FIRST. Пример уже описан в улучшенной рекурсивной функции. Дополнительно можно фильтровать типы файлов.
Пример с фильтрацией:
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS),
RecursiveIteratorIterator::CHILD_FIRST
);
foreach ($iterator as $file) {
if ($file->isFile() && in_array($file->getExtension(), ['tmp', 'log'])) {
unlink($file->getRealPath());
} elseif ($file->isDir()) {
rmdir($file->getRealPath());
}
}
rmdir($dir);
Ошибка: если в процессе итерации появляются новые файлы (например, параллельный процесс), итератор может пропустить их. Для критичных случаев применяется блокировка файловой системы.
Расширенные примеры удаления каталогов
Пример 1. Рекурсивное удаление с логированием ошибок
function rrmdir($dir, &$errors = []) {
if (!is_dir($dir)) {
$errors[] = "$dir не является директорией";
return false;
}
$items = new FilesystemIterator($dir, FilesystemIterator::SKIP_DOTS);
foreach ($items as $item) {
$path = $item->getPathname();
if ($item->isDir()) {
rrmdir($path, $errors);
} else {
if (!unlink($path)) {
$errors[] = "Не удалось удалить файл $path";
}
}
}
if (!rmdir($dir)) {
$errors[] = "Не удалось удалить директорию $dir";
return false;
}
return true;
}
// Вызов
$errors = [];
$result = rrmdir('/tmp/test', $errors);
// $result = true/false, $errors содержит массив проблем
Пример 2. Удаление с проверкой символических ссылок
function safeRemoveDir($dir) {
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS),
RecursiveIteratorIterator::CHILD_FIRST
);
foreach ($iterator as $fileinfo) {
$path = $fileinfo->getRealPath();
if ($fileinfo->isLink()) {
unlink($path);
} elseif ($fileinfo->isDir()) {
rmdir($path);
} else {
unlink($path);
}
}
return rmdir($dir);
}
// Предотвращает обход по ссылкам на родительский каталог. // При обнаружении ссылки происходит unlink, а не рекурсивный обход.
Пример 3. Использование DirectoryIterator для пошагового удаления
function deleteDirectoryByStep($dir, $limit = 100) {
$count = 0;
$iterator = new DirectoryIterator($dir);
foreach ($iterator as $fileinfo) {
if ($fileinfo->isDot()) continue;
$path = $fileinfo->getPathname();
if ($fileinfo->isDir()) {
deleteDirectoryByStep($path, $limit);
} else {
unlink($path);
$count++;
if ($count >= $limit) break;
}
}
if ($count == 0) {
rmdir($dir);
}
}
// Позволяет удалять большие каталоги частями для избежания тайм-аута. // После каждого вызова можно сохранять прогресс и продолжать.
Пример 4. Удаление через RecursiveRegexIterator для выборочной очистки
$dir = '/var/log/old';
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($dir)
);
$regex = new RecursiveRegexIterator($iterator, '/\.(log|txt)$/i', RecursiveRegexIterator::GET_MATCH);
foreach ($regex as $file) {
unlink($file[0]);
}
// После удаления всех файлов проверить, остались ли пустые папки
$emptyDirs = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS),
RecursiveIteratorIterator::SELF_FIRST
);
foreach ($emptyDirs as $dirInfo) {
if ($dirInfo->isDir() && is_dir_empty($dirInfo->getPathname())) {
rmdir($dirInfo->getPathname());
}
}
function is_dir_empty($d) {
return count(scandir($d)) == 2;
}
// Удаляются только файлы с расширениями .log и .txt, затем пустые папки. // Применяется для выборочной чистки логов.