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

PHP 遞歸刪除目錄與檔案:安全、可控、支援符號連結

     編輯:PHP 相關知識

PHP 遞歸刪除目錄與檔案:安全、可控、支援符號連結

刪除非空目錄需遞歸清空內容再 rmdir。本文提供生產級刪除函數,處理符號連結不誤刪目標、錯誤捕獲、可選預覽模式。

核心實作

<?php
/**
 * 遞歸刪除目錄內容並刪除目錄本身
 * @param string $dirPath 目標目錄絕對路徑
 * @param bool   $dryRun  true=預覽模式(僅列出將刪除項), false=實際刪除
 * @return array ['deleted'=>[], 'errors'=>[]]
 */
function deleteDirectoryRecursive($dirPath, $dryRun = false) {
    $dirPath = rtrim(realpath($dirPath), '/');
    if ($dirPath === false || !is_dir($dirPath)) {
        return ['deleted' => [], 'errors' => ["Path not found or not a directory: $dirPath"]];
    }

    $deleted = [];
    $errors = [];

    $items = new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator($dirPath, FilesystemIterator::SKIP_DOTS),
        RecursiveIteratorIterator::CHILD_FIRST // 先刪子項再刪父目錄
    );

    foreach ($items as $item) {
        $path = $item->getPathname();
        $isLink = $item->isLink();
        $isDir = $item->isDir();

        // 符號連結:僅刪除連結本身,不遞歸刪除目標
        if ($isLink) {
            if (!$dryRun) {
                if (!unlink($path)) $errors[] = "Failed to unlink symlink: $path";
                else $deleted[] = "symlink:$path";
            } else {
                $deleted[] = "symlink:$path (dry-run)";
            }
            continue;
        }

        if ($isDir) {
            if (!$dryRun) {
                if (!rmdir($path)) $errors[] = "Failed to rmdir: $path";
                else $deleted[] = "dir:$path";
            } else {
                $deleted[] = "dir:$path (dry-run)";
            }
        } else {
            if (!$dryRun) {
                if (!unlink($path)) $errors[] = "Failed to unlink file: $path";
                else $deleted[] = "file:$path";
            } else {
                $deleted[] = "file:$path (dry-run)";
            }
        }
    }

    // 最後刪除根目錄本身
    if (!$dryRun) {
        if (!rmdir($dirPath)) $errors[] = "Failed to rmdir root: $dirPath";
        else $deleted[] = "dir:$dirPath";
    } else {
        $deleted[] = "dir:$dirPath (dry-run)";
    }

    return ['deleted' => $deleted, 'errors' => $errors];
}

// 使用範例
$result = deleteDirectoryRecursive('/var/www/temp/cache', false);
echo "Deleted: " . count($result['deleted']) . " items\n";
if ($result['errors']) {
    echo "Errors:\n" . implode("\n", $result['errors']) . "\n";
}

關鍵安全特性

特性說明
CHILD_FIRST 遍歷保證先刪除檔案/子目錄,父目錄最後刪除,避免 "Directory not empty"
符號連結保護isLink() 判斷,僅 unlink 連結檔案,不跟隨刪除目標內容
實際路徑解析realpath() 消除相對路徑、符號連結干擾,防止誤刪項目根目錄
預覽模式dryRun=true 先跑一次看清單,確認無誤再實刪
錯誤收集不中斷單項失敗記錄繼續,最終彙總報告,避免半刪狀態無感知

一鍵清空目錄但保留目錄本身

function clearDirectory($dirPath) {
    $dirPath = rtrim(realpath($dirPath), '/');
    $items = new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator($dirPath, FilesystemIterator::SKIP_DOTS),
        RecursiveIteratorIterator::CHILD_FIRST
    );
    foreach ($items as $item) {
        $path = $item->getPathname();
        if ($item->isLink() || !$item->isDir()) unlink($path);
        else rmdir($path);
    }
}

替代方案:系統指令 (需權限審慎)

// 僅限受信任路徑、已驗證輸入、關閉 shell 逃逸
exec('rm -rf ' . escapeshellarg($dirPath) . ' 2>&1', $out, $code);

強烈不建議 在 Web 請求中直接呼叫 exec,極易導致命令注入、權限越界。純 PHP 實作更安全、可移植、可測試。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved