FileUtil 完整類別碼
<?php
/**
* 檔案與目錄操作工具類
*
* 使用範例:
* FileUtil::createDir('a/1/2/3'); // 遞迴建立目錄
* FileUtil::createFile('b/1/2/3.txt'); // 遞迴建立檔案
* FileUtil::copyDir('src', 'dst/backup'); // 複製整個目錄樹
* FileUtil::copyFile('src/a.txt', 'dst/a.txt'); // 複製單一檔案
* FileUtil::moveDir('old', 'new/location'); // 移動目錄 (含刪除來源)
* FileUtil::moveFile('old/a.txt', 'new/a.txt'); // 移動檔案
* FileUtil::unlinkDir('temp'); // 遞迴刪除目錄
* FileUtil::unlinkFile('temp/a.txt'); // 刪除檔案
*/
class FileUtil {
/** 遞迴建立目錄 */
public static function createDir($path) {
$path = str_replace('\\', '/', $path);
$current = '';
foreach (explode('/', $path) as $dir) {
$current .= $dir . '/';
if (!is_dir($current) && !mkdir($current)) {
return false;
}
}
return true;
}
/** 建立檔案 (含父目錄) */
public static function createFile($path, $overwrite = false) {
if (file_exists($path)) {
if (!$overwrite) return false;
self::unlinkFile($path);
}
self::createDir(dirname($path));
return touch($path);
}
/** 遞迴複製目錄 */
public static function copyDir($src, $dst, $overwrite = false) {
$src = rtrim(str_replace('\\', '/', $src), '/') . '/';
$dst = rtrim(str_replace('\\', '/', $dst), '/') . '/';
if (!is_dir($src)) return false;
if (!is_dir($dst)) self::createDir($dst);
$handle = opendir($src);
while (($file = readdir($handle)) !== false) {
if ($file === '.' || $file === '..') continue;
$srcPath = $src . $file;
$dstPath = $dst . $file;
if (is_dir($srcPath)) {
self::copyDir($srcPath, $dstPath, $overwrite);
} else {
self::copyFile($srcPath, $dstPath, $overwrite);
}
}
closedir($handle);
return true;
}
/** 複製單一檔案 */
public static function copyFile($src, $dst, $overwrite = false) {
if (!file_exists($src)) return false;
if (file_exists($dst)) {
if (!$overwrite) return false;
self::unlinkFile($dst);
}
self::createDir(dirname($dst));
return copy($src, $dst);
}
/** 遞迴移動目錄 (複製後刪除來源) */
public static function moveDir($src, $dst, $overwrite = false) {
$result = self::copyDir($src, $dst, $overwrite);
if ($result) self::unlinkDir($src);
return $result;
}
/** 移動單一檔案 */
public static function moveFile($src, $dst, $overwrite = false) {
if (!file_exists($src)) return false;
if (file_exists($dst)) {
if (!$overwrite) return false;
self::unlinkFile($dst);
}
self::createDir(dirname($dst));
return rename($src, $dst);
}
/** 遞迴刪除目錄 */
public static function unlinkDir($path) {
$path = rtrim(str_replace('\\', '/', $path), '/') . '/';
if (!is_dir($path)) return false;
$handle = opendir($path);
while (($file = readdir($handle)) !== false) {
if ($file === '.' || $file === '..') continue;
$full = $path . $file;
is_dir($full) ? self::unlinkDir($full) : self::unlinkFile($full);
}
closedir($handle);
return rmdir($path);
}
/** 刪除檔案 */
public static function unlinkFile($path) {
if (file_exists($path)) {
return unlink($path);
}
return false;
}
}
?>
使用方式
<?php
// 靜態呼叫 (推薦)
FileUtil::copyDir('/var/www/html', '/backup/html_'.date('Ymd'));
// 實例呼叫
$fu = new FileUtil();
$fu->copyFile('source.txt', 'dest.txt');
?>
關鍵設計細節
| 方法 | 關鍵點 | 錯誤處理 |
| createDir | 逐層建立、相容 Windows/Unix 路徑 | mkdir 失敗回傳 false |
| copyDir/moveDir | 遞迴處理子目錄、統一路徑分隔符 | opendir 失敗回傳 false |
| unlinkDir | 後序遍歷:先刪內容再刪目錄本身 | rmdir 失敗回傳 false |
| 覆蓋控制 | `$overwrite` 參數統一控制衝突行為 | 不覆蓋時直接回傳 false |
進階擴充建議
- 權限保留:`copy()` 不保留權限,需 `chmod($dst, fileperms($src))`
- 符號連結處理:`is_link()` 檢測並決定複製連結或目標
- 大檔案支援:`copyFile` 改用 `stream_copy_to_stream` 避免記憶體溢位
- 進度回調:長時間操作提供 `$callback($current, $total)` 介面
- 例外處理:PHP 7+ 建議拋出 `RuntimeException` 取代布林回傳
替代方案:SPL FilesystemIterator
<?php
// 現代 PHP 更優雅的寫法
function copyDirModern($src, $dst) {
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($src, FilesystemIterator::SKIP_DOTS),
RecursiveIteratorIterator::SELF_FIRST
);
foreach ($iterator as $item) {
$target = $dst . DIRECTORY_SEPARATOR . $iterator->getSubPathName();
$item->isDir() ? mkdir($target) : copy($item, $target);
}
}
?>