Как стереть папку в PHP: основные подходы

Раздел: Программирование на 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, затем пустые папки.
// Применяется для выборочной чистки логов.
  

Удаление каталога в PHP - comments

En
Php удалить каталог (php)