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

PHP 遞迴刪除目錄與檔案完整實作(支援類型過濾與安全防護)

     編輯:PHP 相關知識

專案部署、快取清理、臨時檔管理常需「刪除目錄下所有內容」。單純 rmdir() 僅能刪空目錄,unlink() 僅能刪檔案。本文提供生產級 delDir() 函式,支援副檔名過濾、符號連結保護、錯誤處理與測試案例。

核心函式

/**
 * 遞迴刪除目錄內容(可選擇保留目錄本身)
 * @param string $dir         目標目錄絕對路徑
 * @param array|string|null $filter  副檔名過濾:'php'、['php','log']、null(全刪)
 * @param bool $deleteSelf    是否刪除目錄本身(預設 true)
 * @return array              ['deleted' => int, 'errors' => array]
 */
function delDir($dir, $filter = null, $deleteSelf = true) {
    $result = ['deleted' => 0, 'errors' => []];

    if (!is_dir($dir)) {
        $result['errors'][] = "Not a directory: $dir";
        return $result;
    }

    $dir = rtrim($dir, '/\\');
    $items = scandir($dir);

    foreach ($items as $item) {
        if ($item === '.' || $item === '..') continue;
        $path = "$dir/$item";

        // 符號連結:只刪連結,不追蹤目標
        if (is_link($path)) {
            if (@unlink($path)) $result['deleted']++;
            else $result['errors'][] = "Unlink symlink failed: $path";
            continue;
        }

        if (is_dir($path)) {
            // 遞迴處理子目錄
            $sub = delDir($path, $filter, true);
            $result['deleted'] += $sub['deleted'];
            $result['errors'] = array_merge($result['errors'], $sub['errors']);
        } elseif (is_file($path)) {
            // 檔案過濾邏輯
            $ext = strtolower(pathinfo($path, PATHINFO_EXTENSION));
            $match = false;
            if ($filter === null) {
                $match = true;
            } elseif (is_array($filter)) {
                $match = in_array($ext, array_map('strtolower', $filter));
            } elseif (is_string($filter)) {
                $match = ($ext === strtolower($filter));
            }

            if ($match) {
                if (@unlink($path)) $result['deleted']++;
                else $result['errors'][] = "Unlink file failed: $path";
            }
        }
    }

    // 刪除空目錄本身
    if ($deleteSelf) {
        if (@rmdir($dir)) $result['deleted']++;
        else $result['errors'][] = "Rmdir failed: $dir";
    }

    return $result;
}

使用範例

// 1. 刪除整個快取目錄
$r = delDir('/var/www/cache');
echo "Deleted {$r['deleted']} items. Errors: " . count($r['errors']);

// 2. 僅刪除 .tmp 和 .log 檔案,保留目錄結構
$r = delDir('/var/www/storage', ['tmp', 'log'], false);

// 3. 刪除單一檔案(傳入檔案路徑也支援)
$r = delDir('/tmp/test.php');  // 內部會判斷是檔案直接 unlink

安全強化清單

風險防護措施
誤刪專案核心目錄 呼叫前務必 realpath() 正規化,並與白名單比對
符號連結跳出預期目錄 is_link() 僅刪連結檔,不遞迴進入目標
權限不足導致部分殘留 回傳 errors 陣列供記錄/告警,不靜默失敗
併發執行競態條件 關鍵目錄加 flock() 或使用獨佔鎖檔
超大目錄遞迴超時/記憶體溢出 改用 RecursiveDirectoryIterator + 生成器分批處理

現代化替代方案(PHP 8.0+)

use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;

function delDirModern($dir, $filter = null) {
    $iterator = new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS),
        RecursiveIteratorIterator::CHILD_FIRST
    );

    foreach ($iterator as $item) {
        if ($filter && !$item->isDir()) {
            $ext = strtolower($item->getExtension());
            if (!in_array($ext, (array)$filter)) continue;
        }
        $item->isDir() ? rmdir($item->getPathname()) : unlink($item->getPathname());
    }
    rmdir($dir);
}

舊版相容用遞迴函式,新專案建議使用 SPL 迭代器,程式碼更簡潔、記憶體效率更高、自動處理 CHILD_FIRST 順序。

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