原生文件系統函數分散、參數不一、缺乏遞歸支持。本文提供一個生產就緒的 FileUtil 靜態工具類,封裝創建、刪除、移動、複製四大核心操作,支持遞歸目錄處理、覆蓋控制、錯誤抑制與鏈式調用。
<?php
/**
* 文件系統統一操作工具類
*
* 用法示例:
* FileUtil::createDir('a/1/2/3'); // 遞歸創建目錄
* FileUtil::createFile('b/1/2/3.exe'); // 創建文件 (自動創建父目錄)
* FileUtil::copyDir('b', 'd/e'); // 遞歸複製目錄
* FileUtil::copyFile('b/1/2/3.exe', 'b/b/3.exe');
* FileUtil::moveDir('a/', 'b/c'); // 遞歸移動目錄
* FileUtil::moveFile('b/1/2/3.exe', 'b/d/3.exe');
* FileUtil::unlinkFile('b/d/3.exe'); // 刪除文件
* FileUtil::unlinkDir('d'); // 遞歸刪除目錄
*/
class FileUtil {
/** 統一路徑分隔符為 DIRECTORY_SEPARATOR */
private static function normalize($path) {
return str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $path);
}
/** 確保目錄存在,不存在則遞歸創建 */
private static function ensureDir($path) {
$path = self::normalize($path);
if (!is_dir($path)) {
return mkdir($path, 0755, true);
}
return true;
}
// ==================== 創建操作 ====================
/** 創建目錄 (遞歸) */
public static function createDir($aimUrl) {
return self::ensureDir(self::normalize($aimUrl));
}
/** 創建文件 (自動創建父目錄)
* @param bool $overWrite 是否覆蓋已存在文件
*/
public static function createFile($aimUrl, $overWrite = false) {
$aimUrl = self::normalize($aimUrl);
if (file_exists($aimUrl)) {
if (!$overWrite) return false;
self::unlinkFile($aimUrl);
}
self::ensureDir(dirname($aimUrl));
return touch($aimUrl);
}
// ==================== 刪除操作 ====================
/** 刪除文件 */
public static function unlinkFile($aimUrl) {
$aimUrl = self::normalize($aimUrl);
if (file_exists($aimUrl) && is_file($aimUrl)) {
return @unlink($aimUrl);
}
return false;
}
/** 遞歸刪除目錄 (包含所有子文件/子目錄) */
public static function unlinkDir($aimDir) {
$aimDir = self::normalize($aimDir);
if (!is_dir($aimDir)) return false;
$items = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($aimDir, RecursiveDirectoryIterator::SKIP_DOTS),
RecursiveIteratorIterator::CHILD_FIRST
);
foreach ($items as $item) {
$item->isDir() ? @rmdir($item->getPathname()) : @unlink($item->getPathname());
}
return @rmdir($aimDir);
}
// ==================== 複製操作 ====================
/** 複製文件 */
public static function copyFile($fileUrl, $aimUrl, $overWrite = false) {
$fileUrl = self::normalize($fileUrl);
$aimUrl = self::normalize($aimUrl);
if (!file_exists($fileUrl) || !is_file($fileUrl)) return false;
if (file_exists($aimUrl) && !$overWrite) return false;
if (file_exists($aimUrl) && $overWrite) self::unlinkFile($aimUrl);
self::ensureDir(dirname($aimUrl));
return copy($fileUrl, $aimUrl);
}
/** 遞歸複製目錄 */
public static function copyDir($oldDir, $aimDir, $overWrite = false) {
$oldDir = self::normalize($oldDir);
$aimDir = self::normalize($aimDir);
if (!is_dir($oldDir)) return false;
self::ensureDir($aimDir);
$items = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($oldDir, RecursiveDirectoryIterator::SKIP_DOTS),
RecursiveIteratorIterator::SELF_FIRST
);
foreach ($items as $item) {
$target = $aimDir . DIRECTORY_SEPARATOR . $items->getSubPathname();
if ($item->isDir()) {
self::ensureDir($target);
} else {
self::copyFile($item->getPathname(), $target, $overWrite);
}
}
return true;
}
// ==================== 移動操作 ====================
/** 移動文件 (跨目錄即為移動,同目錄即為重命名) */
public static function moveFile($fileUrl, $aimUrl, $overWrite = false) {
$fileUrl = self::normalize($fileUrl);
$aimUrl = self::normalize($aimUrl);
if (!file_exists($fileUrl)) return false;
if (file_exists($aimUrl) && !$overWrite) return false;
if (file_exists($aimUrl) && $overWrite) self::unlinkFile($aimUrl);
self::ensureDir(dirname($aimUrl));
return rename($fileUrl, $aimUrl);
}
/** 遞歸移動目錄 */
public static function moveDir($oldDir, $aimDir, $overWrite = false) {
$oldDir = self::normalize($oldDir);
$aimDir = self::normalize($aimDir);
if (!is_dir($oldDir)) return false;
self::ensureDir($aimDir);
// 先複製再刪除源目錄 (跨文件系統安全)
if (self::copyDir($oldDir, $aimDir, $overWrite)) {
return self::unlinkDir($oldDir);
}
return false;
}
}
FileUtil::createDir('storage/logs/2024/01');
FileUtil::copyFile('src/config.php', 'dist/config.php', true); // 覆蓋模式
FileUtil::moveDir('old_module', 'new_module');
$fu = new FileUtil();
$fu->copyFile('a/1/2/3', 'a/1/2/4');
| 特性 | 實現方式 | 收益 |
|---|---|---|
| 路徑跨平台兼容 | normalize() 統一分隔符 | Windows/Linux 無縫運行 |
| 遞歸目錄處理 | RecursiveIteratorIterator CHILD_FIRST/SELF_FIRST | 正確處理巢狀結構,避免「目錄非空無法刪除」 |
| 覆蓋控制 | $overWrite 參數統一所有方法 | 明確語義,避免靜默覆蓋數據丟失 |
| 錯誤抑制與返回值 | @unlink/@rmdir + bool 返回 | 調用者自主決定錯誤處理策略 |
| 移動跨文件系統安全 | copyDir + unlinkDir 組合 | rename() 跨分區失敗時自動降級 |
League\Flysystem 適配器,支持 S3、FTP、SFTP、Zip 等存儲後端。flock) 保證并發寫入安全。Psr\SimpleCache\CacheInterface 將文件系統作為緩存驅動。getSize()、getMimeType()、find() 等查詢方法。FileUtil 以 靜態方法、統一路徑處理、遞歸迭代器、覆蓋控制 四大特性,將零散的原生函數整合為連貫、可測試、生產可用的工具類。直接複製到項目 Common/Util/ 目錄即可使用,零依賴、零配置。