前言
PHP mkdir() 預設不支援遞歸建立多層目錄(PHP 5.0+ 第三參數 $recursive 才支援),舊專案或特定環境常需自行實作。本文解析 ThinkPHP 來源的 mk_dir 遞歸建目函數,說明邏輯流程、潛在問題與現代替代方案。
原始實作程式碼
function mk_dir($dir, $mode = 0777) {
if (!is_dir($dir) || mkdir($dir, $mode)) {
if (!mk_dir(dirname($dir), $mode)) {
return false;
}
}
return mkdir($dir, $mode);
}
邏輯流程分析
- 終止條件:
is_dir($dir) 為真(目錄已存在)或 mkdir() 成功建立。
- 遞歸步驟:呼叫
dirname($dir) 取得父目錄,遞歸建立父目錄。
- 回傳值:最終對目標目錄執行
mkdir() 並回傳結果。
潛在問題與風險
| 問題 | 原因 | 影響 |
| 無限遞歸 | 根目錄 dirname('/') === '/' 導致自我呼叫 | 堆疊溢位、程式掛起 |
| 權限檢查不足 | 未檢查父目錄可寫入性 | 深層目錄建立失敗無明確錯誤 |
| 競態條件 | 多程序同時建立同目錄 | mkdir() 回傳 false 但目錄實際存在 |
| 模式位元組處理 | 未配合 umask | 實際權限與預期不符 |
改良版:加入根目錄守衛與錯誤處理
function mk_dir_improved($dir, $mode = 0777) {
// 正規化路徑
$dir = rtrim($dir, '/');
if ($dir === '' || $dir === '/') return true; // 根目錄守衛
if (is_dir($dir)) return true;
$parent = dirname($dir);
if ($parent !== $dir) { // 避免根目錄無限遞歸
if (!mk_dir_improved($parent, $mode)) {
return false;
}
}
// 建立目錄並處理競態條件
if (!mkdir($dir, $mode)) {
// 最後再確認一次是否已存在(可能被其他程序建立)
return is_dir($dir);
}
return true;
}
現代 PHP 最佳實踐(PHP 5.0+)
// 內建遞歸參數,簡潔高效
mkdir('/path/to/deep/dir', 0777, true);
優勢:C 層實作、原子性較好、無需自維護遞歸邏輯、支援所有平台。
總結
理解遞歸建目邏輯有助於維護舊代碼,但**新專案請直接使用 mkdir($path, $mode, true)**。若必須自行實作,務必加入根目錄守衛、競態條件處理與權限檢查,避免生產環境異常。
- 上一篇文章: PHP 判斷字串是否為合法 JSON 格式的最佳實踐
- 下一篇文章: 常用正則表達式集合:URL、Email、電話、整數驗證
Copyright ©
程式碼之家 All Rights Reserved