PHP 提供豐富的檔案系統函式,但正確處理遞迴目錄建立、非空目錄刪除、大檔案複製、編碼與路徑跨平台等細節往往被忽略。本文整理實務上最常用的操作模式,提供可直接落地的函式範例。
<?php
// 單層目錄 (父目錄必須存在)
$dir = 'data/uploads';
if (!is_dir($dir)) {
if (mkdir($dir, 0755)) echo "建立成功";
else echo "建立失敗: " . error_get_last()['message'];
}
// 多層遞迴建立 (第三參數 recursive=true)
$deepDir = 'data/uploads/2024/05/user_123';
if (!is_dir($deepDir)) {
// 0755: 擁有者 rwx, 群組/其他 rx
// true: 遞迴建立父目錄
if (mkdir($deepDir, 0755, true)) echo "遞迴建立成功";
else echo "遞迴建立失敗";
}
?>
rmdir() 僅能刪除空目錄。刪除非空目錄需自行遞迴:
<?php
/**
* 遞迴刪除目錄及其所有內容
* @param string $dir 目錄路徑
* @return bool
*/
function deleteDirectory(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);
}
// 使用
deleteDirectory('data/uploads/2024'); // 刪除 2024 及其所有子目錄檔案
?>
<?php
$file = 'data/logs/app.log';
$content = date('Y-m-d H:i:s') . " INFO: Application started\n";
// 方法一: file_put_contents (簡單、自動建立檔案、支援 FILE_APPEND)
file_put_contents($file, $content, FILE_APPEND | LOCK_EX); // LOCK_EX 避免併發衝突
// 方法二: fopen/fwrite/fclose (需手動建立目錄、控制指標)
$fp = fopen($file, 'a'); // 'a' 附加模式,指標在末尾
if ($fp) {
fwrite($fp, $content);
fclose($fp);
}
?>
<?php
$file = 'data/temp.tmp';
if (is_file($file)) {
if (unlink($file)) echo "刪除成功";
else echo "刪除失敗 (權限或檔案被鎖定)";
} else {
echo "檔案不存在";
}
?>
<?php
/**
* 安全複製檔案
* @param string $src 來源路徑
* @param string $dst 目標路徑 (含檔名)
* @param int $bufferSize 緩衝區大小 (預設 1MB)
* @return bool
*/
function copyFile(string $src, string $dst, int $bufferSize = 1048576): bool {
if (!is_file($src)) return false;
// 確保目標目錄存在
$dstDir = dirname($dst);
if (!is_dir($dstDir)) {
if (!mkdir($dstDir, 0755, true)) return false;
}
$srcHandle = fopen($src, 'rb');
$dstHandle = fopen($dst, 'wb');
if (!$srcHandle || !$dstHandle) {
if ($srcHandle) fclose($srcHandle);
if ($dstHandle) fclose($dstHandle);
return false;
}
// 邊讀邊寫,避免大檔案佔用記憶體
while (!feof($srcHandle)) {
$buffer = fread($srcHandle, $bufferSize);
if ($buffer === false) { fclose($srcHandle); fclose($dstHandle); return false; }
fwrite($dstHandle, $buffer);
}
fclose($srcHandle);
fclose($dstHandle);
// 保留原檔案權限/時間 (可選)
// chmod($dst, fileperms($src));
// touch($dst, filemtime($src));
return true;
}
// 使用
copyFile('data/uploads/original.pdf', 'data/backups/2024/original.pdf');
?>
DIRECTORY_SEPARATOR 或正斜線 / (Windows PHP 也支援)。避免硬編碼反斜線 \。iconv('UTF-8', 'GBK//IGNORE', $path)。現代建議全系統統一 UTF-8 (Windows 10+ 已支援)。realpath($path) 解析相對路徑、符號連結,取得標準化絕對路徑。<?php
// 遞迴計算目錄大小 (位元組)
function getDirectorySize(string $dir): int {
$size = 0;
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS));
foreach ($iterator as $file) {
if ($file->isFile()) $size += $file->getSize();
}
return $size;
}
// 列出目錄樹結構 (陣列)
function listDirectoryTree(string $dir): array {
$tree = [];
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS));
foreach ($iterator as $path => $item) {
$relPath = substr($path, strlen($dir) + 1);
$tree[$relPath] = [
'type' => $item->isDir() ? 'dir' : 'file',
'size' => $item->isFile() ? $item->getSize() : 0,
'mtime' => $item->getMTime(),
];
}
return $tree;
}
?>
| 操作 | 推薦函式/模式 | 關鍵點 |
|---|---|---|
| 建立目錄 | mkdir($path, 0755, true) | 遞迴參數 true、權限 0755 |
| 刪除非空目錄 | RecursiveIteratorIterator(..., CHILD_FIRST) | 先刪子項、最後刪父目錄 |
| 寫入日誌/小檔案 | file_put_contents($file, $data, FILE_APPEND | LOCK_EX) | 原子附加、併發安全 |
| 複製大檔案 | fread/fwrite 迴圈 + 緩衝區 | 避免 copy() 或 file_get_contents 佔滿記憶體 |
| 路徑處理 | realpath(), DIRECTORY_SEPARATOR | 跨平台相容、解析符號連結 |
掌握這些核心模式,即能安全、高效地處理 PHP 專案中的各類檔案系統操作需求。