在產生靜態檔案、上傳目錄、快取目錄等場景,必須確認目錄是否可寫。is_writable() 僅能檢查單一檔案/目錄,且受限於 Web 伺服器執行身分(通常為 www-data、nobody)。本文提供「建立測試檔」法判斷目錄寫入權限,並延伸至遞迴檢查子目錄與檔案的完整方案。
$filename = 'test.txt';
if (is_writable($filename)) {
echo '可寫入';
} else {
echo '不可寫入';
}
缺點:目錄必須存在且為實體檔案/目錄;無法檢查「空目錄」是否可建立檔案。
若能在目錄成功 fopen(..., 'w') 建立檔案並寫入,即代表有寫入權限。測試完畢立即 fclose() + unlink() 清理。
function check_dir_iswritable($dir_path) {
// 正規化路徑分隔符
$dir_path = str_replace('\\', '/', $dir_path);
// 1. 確認是目錄且存在
if (!is_dir($dir_path)) return 0;
// 2. 嘗試在當前目錄建立測試檔
$testFile = $dir_path . '/.write_test_' . uniqid();
$fp = @fopen($testFile, 'w');
if (!$fp) return 0;
fclose($fp);
@unlink($testFile);
// 3. 掃描子項目
$dh = opendir($dir_path);
while (($item = readdir($dh)) !== false) {
if ($item === '.' || $item === '..') continue;
$fullPath = $dir_path . '/' . $item;
if (is_file($fullPath)) {
// 檔案:直接檢查 is_writable
if (!is_writable($fullPath)) return 0;
} elseif (is_dir($fullPath)) {
// 子目錄:遞迴檢查(同樣用建立測試檔法)
$subTest = $fullPath . '/.write_test_' . uniqid();
$fp2 = @fopen($subTest, 'w');
if (!$fp2) return 0;
fclose($fp2);
@unlink($subTest);
// 遞迴
if (!check_dir_iswritable($fullPath)) return 0;
}
}
closedir($dh);
return 1; // 全部可寫
}
set_time_limit(1000); // 避免深層目錄超時
$targetDir = '/var/www/html/uploads';
if (check_dir_iswritable($targetDir)) {
echo "目錄 $targetDir 及所有子項目皆可寫入 ✅";
} else {
echo "目錄 $targetDir 存在不可寫入項目 ❌";
}
uniqid() 避免並發衝突。@:僅用於 fopen/unlink 避免警告干擾流程,正式環境建議改用 try-catch + 例外處理。fileperms() 檢查 0755/0775 等權限位元,但實際能否寫入仍取決於進程擁有者與 ACL,故「實測建檔」最可靠。is_writable 僅檢查 ACL,實測建檔法同樣適用。「實測建立暫存檔」是判斷目錄可寫性最可靠的方法。封裝為遞迴函式並搭配唯一測試檔名、錯誤抑制、資源釋放,即可構建健壯的權限檢查工具,適用於靜態頁生成、檔案上傳、快取目錄驗證等核心場景。