<?php
/**
* 遞歸刪除目錄及所有內容
* @param string $dir 目錄路徑
* @return bool
*/
function deleteDirRecursive(string $dir): bool {
if (!is_dir($dir)) return false;
$items = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS),
RecursiveIteratorIterator::CHILD_FIRST // 先刪子項,再刪父目錄
);
foreach ($items as $item) {
if ($item->isDir()) {
rmdir($item->getPathname());
} else {
unlink($item->getPathname());
}
}
return rmdir($dir);
}
// 使用
deleteDirRecursive('/path/to/target_dir');
優點:純 PHP、跨平台、可注入日誌/鉤子、無命令注入風險。
缺點:超大目錄(萬級文件)可能超時/內存壓力。
<?php
function deleteDirSystem(string $dir): bool {
$dir = escapeshellarg($dir); // 必須轉義!
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
$cmd = "rmdir /s /q $dir";
} else {
$cmd = "rm -rf $dir";
}
exec($cmd, $output, $returnVar);
return $returnVar === 0;
}
⚠️ 嚴重警告:
$dir 來自用戶輸入且未 escapeshellarg,可導致 rm -rf / 災難。rm/rmdir。建議:生產環境禁用,僅限受控 CLI 腳本、已知安全路徑。
<?php
function deleteDirSpl(string $dir): bool {
if (!is_dir($dir)) return false;
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS),
RecursiveIteratorIterator::CHILD_FIRST
);
foreach ($iterator as $file) {
$path = $file->getPathname();
$file->isDir() ? rmdir($path) : unlink($path);
}
return rmdir($dir);
}
// 進階:帶進度回調
function deleteDirWithProgress(string $dir, callable $onFileDelete = null): bool {
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS),
RecursiveIteratorIterator::CHILD_FIRST
);
foreach ($iterator as $file) {
$path = $file->getPathname();
if ($file->isDir()) {
rmdir($path);
} else {
unlink($path);
$onFileDelete?.($path);
}
}
return rmdir($dir);
}
| 場景 | 推薦方法 | 理由 |
|---|---|---|
| 常規 Web 請求(<1000 文件) | SPL 迭代器 / 遞歸函數 | 安全、可控、純 PHP |
| 超大目錄(萬級+) | CLI 腳本 + SPL + ignore_user_abort + set_time_limit(0) | 避免 Web 超時,可分批、記錄進度 |
| 臨時目錄清理(已知安全路徑) | 系統命令(謹慎) | 極速,但需嚴格驗證路徑 |
| 需要事務性(刪文件+刷DB) | SPL 迭代器 + 回調/事件 | 可在循環中執行業務邏輯 |
<?php
function safeDeleteDir(string $dir): bool {
$realDir = realpath($dir);
$allowedBase = realpath('/var/www/app/uploads'); // 業務允許的根目錄
// 1. 路徑必須在允許範圍內
if ($realDir === false || strpos($realDir, $allowedBase) !== 0) {
error_log("非法刪除嘗試: $dir");
return false;
}
// 2. 防止刪除關鍵目錄
$protected = ['.', '..', '/', '/var', '/etc', '/home'];
if (in_array($realDir, $protected, true)) return false;
return deleteDirSpl($realDir);
}
?>