表單資料在 PHP 中的傳遞機制演進史與最佳實踐
PHP 處理表單資料的方式經歷了重大變遷:從 register_globals 自動建立全域變數,到 $HTTP_POST_VARS、再到現代 $_POST/$_GET/$_REQUEST 超全域陣列。本文詳細對比各版本差異、安全風險與遷移指南。
歷史版本演進時間軸
| PHP 版本 | 機制 | 特徵 | 狀態 |
| PHP 3 / 4.0-4.1 | register_globals = On | 表單欄位自動變全域變數 $name | 極度危險,PHP 4.2 預設關閉,PHP 5.4 移除 |
| PHP 4.1+ | 超全域陣列 | $_POST、$_GET、$_REQUEST、$_FILES | 現代標準,PHP 8 仍沿用 |
| PHP 4.1-5.3 | 長陣列別名 | $HTTP_POST_VARS、$HTTP_GET_VARS | PHP 5.4 移除,需 register_long_arrays |
| PHP 5.4+ | 移除舊機制 | 僅保留超全域陣列 | 強制現代化 |
舊版全域變數模式 (已淘汰,僅供理解舊碼)
<?php
// PHP 4.1 前,register_globals=On 時
// 表單 自動建立 $ship 變數
echo "Ship = $ship<br>";
echo "Tripdate = $tripdate<br>";
echo "Exploration = $exploration<br>";
echo "Contact = $contact<br>"; // 未勾選 checkbox 變數不存在
?>
致命缺陷**:
- 變數來源不可追蹤 (GET/POST/COOKIE/SESSION 混淆)
- 未初始化變數可被 URL 覆蓋 (
?admin=1 竊取權限)
- 命名衝突導致邏輯錯誤
現代超全域陣列標準用法
<?php
// 明確來源、類型安全、可過濾
$name = $_POST['name'] ?? ''; // 僅接受 POST
$page = $_GET['page'] ?? 1; // 僅接受 GET
$token = $_REQUEST['csrf_token'] ?? ''; // 接受 GET/POST/COOKIE (慎用)
// 檔案上傳專用
$file = $_FILES['avatar'] ?? null;
if ($file && $file['error'] === UPLOAD_ERR_OK) {
move_uploaded_file($file['tmp_name'], "uploads/{$file['name']}");
}
// 多選欄位 (name="colors[]") 自動成陣列
$colors = $_POST['colors'] ?? []; // ['red', 'blue']
?>
表單編碼類型與 PHP 解析對應
| enctype | 適用場景 | PHP 讀取方式 |
| application/x-www-form-urlencoded (預設) | 一般文字表單 | $_POST 自動解析 |
| multipart/form-data | 含檔案上傳 | $_POST + $_FILES |
| text/plain | 極少用 | 需手動解析 file_get_contents('php://input') |
| application/json (AJAX) | API 請求 | json_decode(file_get_contents('php://input'), true) |
安全處理最佳實踐
- 輸入過濾:
filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL)
- 輸出編碼:
htmlspecialchars($input, ENT_QUOTES, 'UTF-8') 防 XSS
- CSRF 保護:表單隱藏欄位
<input name="csrf" value="= $_SESSION['csrf'] ?>"> 驗證
- 檔案上傳驗證:MIME 類型白名單、大小限制、隨機化檔名、移除執行權限
- 大小限制:
post_max_size、upload_max_filesize、max_input_vars 防止 DoS
遷移舊專案檢查清單
# 1. 搜尋 register_globals 相關
grep -r "register_globals" /path/to/project
# 2. 搜尋長陣列別名
grep -r "HTTP_POST_VARS\|HTTP_GET_VARS\|HTTP_SERVER_VARS" /path/to/project
# 3. 搜尋直接使用未定義變數 (潛在 register_globals 依賴)
# 靜態分析工具
phpstan analyse src --level 5
psalm --show-info=false
# 4. 替換策略
# $var → $_POST['var'] 或 $_GET['var'] 或 $_REQUEST['var']
# 建議明確指定來源,避免 $_REQUEST
現代框架封裝對照
| 框架 | 請求物件 | 輸入獲取 | 驗證 |
| Laravel | Illuminate\Http\Request | $request->input('name') | FormRequest 類別 |
| Symfony | Symfony\Component\HttpFoundation\Request | $request->request->get('name') | Validator 元件 |
| Slim | Psr\Http\Message\ServerRequestInterface | $request->getParsedBody()['name'] | 敬請自行整合 |