| 函數 | 角色 | 輸入/輸出 |
glob(pattern) | 文件查找器:按模式匹配路徑,返回絕對/相對路徑數組 | 輸入:模式字符串 → 輸出:['file1.txt', 'dir/file2.jpg', ...] |
array_map(callback, array) | 批量執行器:將回調函數應用到數組每個元素,返回結果數組 | 輸入:'unlink' + 路徑數組 → 執行 unlink('file1.txt')... 返回 [true, true, ...] |
執行流程:
glob('*') 掃描當前目錄,返回所有非隱藏文件/目錄路徑數組
array_map 遍歷數組,對每個路徑調用 unlink() 刪除文件
- 若遇到目錄,
unlink() 返回 false 並拋出 Warning,但不中斷後續執行
glob() 模式匹配規則速查
| 模式 | 含義 | 示例匹配 |
|---|---|---|
| * | 任意字符(除 / 外),不匹配以 . 開頭的隱藏文件 | a.txt, sub/b.php |
| ?.txt | 單字符 + .txt | 1.txt, a.txt |
| [abc].php | 字符集合 | a.php, b.php |
| *.{jpg,png,gif} | 大括號分組(需 GLOB_BRACE 標誌) | img.jpg, pic.png |
| **/*.log | 遞歸匹配(需 GLOB_BRACE + PHP 5.3+,實測兼容性一般) | 深層目錄下的 .log |
進階版:健壯的目錄清理函數
<?php
/**
* 安全刪除目錄下所有文件(可選遞歸、過濾模式、錯誤收集)
*
* @param string $dir 目標目錄(絕對路徑)
* @param string $pattern glob 模式,默認 '*' 所有文件
* @param bool $recursive 是否遞歸子目錄
* @return array ['deleted' => int, 'failed' => [], 'skipped_dirs' => int]
*/
function cleanDirectory(string $dir, string $pattern = '*', bool $recursive = false): array {
$dir = rtrim($dir, '/\\') . '/';
if (!is_dir($dir)) throw new InvalidArgumentException("目錄不存在: $dir");
$flags = GLOB_NOSORT; // 不排序,提升大目錄性能
if ($recursive) {
// 遞歸模式:使用 RecursiveDirectoryIterator(比 glob ** 更可靠)
$files = new RegexIterator(
new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS)),
'/^.+/'
);
$paths = iterator_to_array($files, false); // false: 不用路徑作鍵,避免重複覆蓋
} else {
// 非遞歸:glob 直接匹配
$paths = glob($dir . $pattern, $flags);
}
$result = ['deleted' => 0, 'failed' => [], 'skipped_dirs' => 0];
foreach ($paths as $path) {
if (is_dir($path)) {
$result['skipped_dirs']++;
continue; // 跳過目錄,unlink 不能刪目錄
}
if (@unlink($path)) { // @ 抑制 Warning,自行捕獲失敗
$result['deleted']++;
} else {
$result['failed'][] = $path;
}
}
return $result;
}
// 使用示例
$res = cleanDirectory('/var/www/html/cache', '*.tmp', true);
echo "刪除 {$res['deleted']} 個文件,跳過 {$res['skipped_dirs']} 個目錄";
if ($res['failed']) {
error_log("刪除失敗: " . print_r($res['failed'], true));
}
?>
常見坑點與避雷指南
| 坑點 | 現象 | 根因 | 修復 |
|---|---|---|---|
| **隱藏文件遺漏** | .gitignore, .env 未刪 | glob('*') 不匹配點開頭 | glob('{*,.*}', GLOB_BRACE) 或 FilesystemIterator |
| **子目錄報警告** | Warning: unlink(): Is a directory | glob 返回目錄,unlink 只能刪文件 | is_file($path) && unlink($path) 判斷 |
| **權限失敗靜默** | 刪除失敗無感知 | array_map 忽略返回值,unlink 只發 Warning | 捕獲返回值或 error_get_last() 記錄失敗 |
| **大目錄性能** | glob 卡死、內存飆升 | 返回巨大數組佔用內存 | GLOB_NOSORT + DirectoryIterator 流式處理 |
| **遞歸無限循環** | 符號鏈接導致死循環 | RecursiveDirectoryIterator 默認跟隨鏈接 | FilesystemIterator::FOLLOW_SYMLINKS 關閉 |
現代化替代:Symfony Finder / Laravel File Facade
// Laravel / Symfony Finder(表達力強、鏈式調用、自帶遞歸/過濾/大小/時間)
use Symfony\Component\Finder\Finder;
$finder = Finder::create()
->files() // 只要文件
->name('*.log') // 模式
->in('/var/log') // 目錄
->date('< 30 days ago'); // 修改時間 30 天前
foreach ($finder as $file) {
$file->delete(); // SplFileInfo::delete() 內部 unlink
}
// Laravel 簡寫
use Illuminate\Support\Facades\File;
File::cleanDirectory(storage_path('logs')); // 遞歸刪除目錄下所有文件,保留目錄結構
總結
array_map('unlink', glob('*')) 是 PHP 中「表達力最強、代碼最短」的批量刪除寫法,完美詮釋了「函數式編程思想在腳本語言中的實踐」。但生產環境請務必升級為帶錯誤收集、類型判斷、遞歸支持、權限容錯的健壯版本。記住:glob 找路徑,array_map 執行動作,is_file 保安全,錯誤收集保可觀測——這才是專業工程師的寫法。
- 上一篇文章: PHP 實用函數精選:foreach 變體、array_fill、數組輸出與日志記錄
- 下一篇文章: PHP 實現 QQ 登錄 OAuth 1.0a 完整類庫解析
Copyright ©
程式碼之家 All Rights Reserved