PHP 檔案上傳核心函數經歷三個階段:早期 copy() + is_uploaded_file() 組合 → PHP 4.0.3+ 引入 move_uploaded_file() → 現代最佳實踐結合驗證、隨機命名、儲存抽象。理解演進脈絡有助於避開舊代碼陷阱。
| 函數 | 用途 | 特點 | 適用版本 |
|---|---|---|---|
copy(src, dest) | 複製任意檔案 | 不驗證來源,可複製本地任意檔案,安全風險高 | 所有版本 |
is_uploaded_file(filename) | 驗證是否為 HTTP POST 上傳 | 需搭配 copy() 使用,兩步驟易漏驗證 | PHP 4.0.3+ |
move_uploaded_file(filename, dest) | 驗證並移動上傳檔案 | 原子性驗證+移動,不可被繞過,現代標準做法 | PHP 4.0.3+ |
<?php
$path = './uploadfiles/';
if (is_uploaded_file($filename)) { // 注意:傳入 $filename 而非 $_FILES['filename']['name']
$file1 = $_FILES['filename']['name'];
$file2 = $path . time() . $file1;
$flag = 1;
}
if ($flag) $result = copy($file1, $file2); // 風險:$file1 可能被竄改為任意路徑
if ($result) echo '上傳成功!';
?>
<?php
$path = './uploadfiles/';
if ($_FILES['filename']['name']) {
$file1 = $_FILES['filename']['name'];
$file2 = $path . time() . $file1;
$flag = 1;
}
if ($flag) {
// 關鍵:第一參數必須是 tmp_name(伺服器暫存路徑)
$result = move_uploaded_file($_FILES['filename']['tmp_name'], $file2);
}
if ($result) echo '上傳成功!';
?>
move_uploaded_file 強制要求 tmp_name,確保操作的是伺服器管控的暫存檔,而非用戶可控的原始檔名copy() 可複製任意可讀檔案,配合路徑遍歷可讀取敏感檔;move_uploaded_file 內核層級驗證上傳來源<form enctype="multipart/form-data" action="upload.php" method="post">
<input type="file" name="filename">
<input type="submit" value="送出檔案">
</form>
move_uploaded_file 而非 copy$_FILES['file']['error'] === UPLOAD_ERR_OKfinfo_file() 二進位驗證bin2hex(random_bytes(16)))upload_max_filesize、post_max_size、memory_limit從 copy() 到 move_uploaded_file() 的演進,體現 PHP 安全機制的成熟。現代開發應堅持使用最新標準 API,並建立多層防禦體系。