rename() 是 PHP 檔案系統操作的核心函數,**同時具備重命名、移動檔案/目錄**功能。本文詳細說明語法、跨分區限制、封裝協定一致性、Windows/Linux 差異、資源串流上下文、以及**巧用 rename 實現刪除**等進階技巧與避坑指南。
rename(string $from, string $to, ?resource $context = null): bool
$from:現有檔案/目錄路徑。$to:新路徑(**父目錄必須存在**)。$context:串流上下文(如 FTP/SSH2 封裝協定需認證時)。true,失敗 false 並觸發 E_WARNING。| 場景 | 行為 | 備註 |
|---|---|---|
| 同目錄重命名 | 原子性修改目錄項 | 極快、無資料複製 |
| 同分區移動 | 修改目錄項,inode 不變 | Linux/Unix 瞬間完成 |
| 跨分區移動 (PHP < 4.3.3) | **失敗** | 舊版本不支援 |
| 跨分區移動 (PHP >= 4.3.3) | **複製+刪除** | 非原子、大檔案耗時、可能權限不足 |
| 目標已存在 | **覆蓋** (Linux) / **失敗** (Windows) | Windows 需先 unlink |
| 移動目錄 | 遞移整棵目錄樹 | 同分區原子,跨分區遞迴複製 |
<?php
// 重命名檔案
rename('images', 'pictures'); // 目錄重命名
// 移動檔案到子目錄 (需確保 uploads 存在)
rename('tmp/upload_123.jpg', 'uploads/2024/01/photo.jpg');
// 跨分區移動大檔案:建議改用 copy+unlink 可控錯誤處理
if (@rename($src, $dst)) {
echo '移動成功';
} else {
// 降級策略
if (copy($src, $dst)) { unlink($src); echo '複製移動成功'; }
else { echo '移動失敗'; }
}
$from 與 $to **必須使用相同封裝協定**,否則失敗:
rename('file.txt', 'ftp://user:pass@host/file.txt') ❌rename('ftp://user:pass@host/old.txt', 'ftp://user:pass@host/new.txt') ✅rename('/local/path', '/other/local/path') ✅Windows PHP 早期版本 unlink() 可能受限,可將檔案移至系統暫存目錄由 OS 清理:
$tmp = sys_get_temp_dir() . '/php_delete_' . uniqid();
rename($targetFile, $tmp); // 移至暫存區
// 註冊 shutdown function 或 cron 定期清理 $tmp 目錄
$ctx = stream_context_create([
'ftp' => ['username'=>'user', 'password'=>'pass', 'overwrite'=>true]
]);
rename('ftp://server/path/old.txt', 'ftp://server/path/new.txt', $ctx);
| 錯誤 | 常見原因 | 排查 |
|---|---|---|
| Permission denied | 目錄無寫入權限、目標檔案唯讀、Windows 目標被佔用 | 檢查 is_writable()、關閉佔用程式 |
| No such file or directory | 來源不存在、父目錄不存在 | dirname($to) 需預先 mkdir(..., true) |
| Is a directory | 嘗試將檔案重命名為現有目錄名 | 確認目標非目錄 |
| Cross-device link | 舊 PHP 版本跨分區 | 升級 PHP 或改 copy+unlink |
clearstatcache() 清除快取,確保 file_exists()/is_writable() 即時性。copy() + unlink()**,可加入進度回呼、斷點續傳邏輯。@unlink($to) 再 rename()**。is_link() 檢查)。掌握 rename() 完整行為邊界,能在檔案上傳整理、部署發佈、日誌輪轉、暫存清理等場景寫出**健壯、跨平台、高效能**的檔案系統代碼。