首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 遞歸刪除目錄及所有文件:完整實現與安全加固

     編輯:PHP 相關知識

核心實現

<?php
/**
 * 遞歸刪除目錄及其所有內容
 * @param string $path 目錄或文件路徑
 * @param bool $deleteRoot 是否刪除根目錄本身(false 時僅清空目錄)
 * @return bool 成功返回 true
 * @throws Exception 路徑不存在或權限不足
 */
function deleteDirectory(string $path, bool $deleteRoot = true): bool {
    // 規範化路徑
    $path = rtrim(realpath($path), DIRECTORY_SEPARATOR);
    if ($path === false || !file_exists($path)) {
        throw new Exception("路徑不存在:{$path}");
    }

    if (is_file($path) || is_link($path)) {
        return unlink($path);
    }

    // 目錄處理
    $items = new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS),
        RecursiveIteratorIterator::CHILD_FIRST // 先刪除子項
    );

    foreach ($items as $item) {
        if ($item->isFile() || $item->isLink()) {
            if (!@unlink($item->getPathname())) {
                throw new Exception("無法刪除文件:{$item->getPathname()}");
            }
        } elseif ($item->isDir()) {
            if (!@rmdir($item->getPathname())) {
                throw new Exception("無法刪除目錄:{$item->getPathname()}");
            }
        }
    }

    if ($deleteRoot) {
        return @rmdir($path);
    }
    return true;
}

// === 使用示例 ===
try {
    // 刪除整個目錄
    deleteDirectory('/var/www/uploads/temp');

    // 僅清空目錄,保留目錄本身
    deleteDirectory('/var/www/cache', false);

    echo "操作成功";
} catch (Exception $e) {
    echo "錯誤:" . $e->getMessage();
}
?>

傳統遞歸函數寫法(兼容老版本 PHP)

function deleteDirRecursive(string $dir): bool {
    if (!is_dir($dir)) {
        return is_file($dir) ? @unlink($dir) : false;
    }

    $handle = opendir($dir);
    while (($file = readdir($handle)) !== false) {
        if ($file === '.' || $file === '..') continue;
        $full = $dir . DIRECTORY_SEPARATOR . $file;
        if (is_dir($full)) {
            deleteDirRecursive($full);
        } else {
            @unlink($full);
        }
    }
    closedir($handle);
    return @rmdir($dir);
}

安全加固清單

生產環境推薦封裝類

class FileCleaner {
    private array $allowedRoots = [];
    private int $maxDepth = 10;
    private int $maxFiles = 10000;

    public function addAllowedRoot(string $path): self {
        $this->allowedRoots[] = rtrim(realpath($path), DIRECTORY_SEPARATOR);
        return $this;
    }

    public function delete(string $targetPath): array {
        $result = ['success' => false, 'deleted' => 0, 'errors' => []];
        $target = rtrim(realpath($targetPath), DIRECTORY_SEPARATOR);

        // 安全檢查
        if (!$target || !file_exists($target)) {
            $result['errors'][] = '目標路徑不存在';
            return $result;
        }
        if (!$this->isPathAllowed($target)) {
            $result['errors'][] = '路徑不在允許範圍內';
            return $result;
        }

        try {
            $this->doDelete($target, $result, 0);
            $result['success'] = true;
        } catch (Exception $e) {
            $result['errors'][] = $e->getMessage();
        }
        return $result;
    }

    private function isPathAllowed(string $path): bool {
        foreach ($this->allowedRoots as $root) {
            if (str_starts_with($path, $root)) return true;
        }
        return false;
    }

    private function doDelete(string $path, array &$result, int $depth): void {
        if ($depth > $this->maxDepth) {
            throw new Exception("超過最大遞歸深度 {$this->maxDepth}");
        }
        if ($result['deleted'] >= $this->maxFiles) {
            throw new Exception("超過最大文件數 {$this->maxFiles}");
        }

        if (is_file($path) || is_link($path)) {
            if (@unlink($path)) $result['deleted']++;
            return;
        }

        $items = new RecursiveIteratorIterator(
            new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS),
            RecursiveIteratorIterator::CHILD_FIRST
        );

        foreach ($items as $item) {
            $this->doDelete($item->getPathname(), $result, $depth + 1);
        }

        if (@rmdir($path)) $result['deleted']++;
    }
}

// 使用
$cleaner = new FileCleaner();
$cleaner->addAllowedRoot('/var/www/uploads');
$res = $cleaner->delete('/var/www/uploads/temp');
print_r($res);
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
風險防範措施
路徑遍歷攻擊使用 realpath() 解析絕對路徑,檢查是否在允許的根目錄下
誤刪關鍵目錄維護白名單目錄,禁止刪除系統目錄、項目核心目錄
權限不足導致部分刪除捕獲異常、記錄日誌、返回失敗狀態供上層處理
符號鏈接逃逸RecursiveDirectoryIterator::SKIP_DOTS 不跟隨鏈接,或顯式檢查 is_link()
大目錄執行超時設置 set_time_limit(0),或分批處理(佇列+後台任務)