現代 API 多以 `Content-Type: application/json` 發送數據,PHP `$_POST` **不會自動解析 JSON Body**,需讀取 `php://input` 或處理傳統表單字段中的 JSON 字符串。本文涵蓋兩種接收方式、魔術引號兼容、錯誤處理、安全建議。
場景一:表單字段傳 JSON 字符串 (傳統兼容)
<?php
// 前端:
$jsonString = $_POST['txt_json'] ?? '';
// 兼容魔術引號 (PHP < 5.4 已移除,舊項目可能仍開啟)
if (ini_get('magic_quotes_gpc') === '1') {
$jsonString = stripslashes($jsonString);
}
$data = json_decode($jsonString, true); // 關鍵:第二參數 true 返回關聯數組
if (json_last_error() !== JSON_ERROR_NONE) {
die('JSON 解析失敗: ' . json_last_error_msg());
}
var_dump($data); // ['name' => 'Tom', 'age' => 18]
?>
場景二:標準 JSON API (推薦) — 讀取 php://input
<?php
// Content-Type: application/json
// Body: {"name":"Tom","age":18}
$raw = file_get_contents('php://input'); // 獲取原始請求體
if (empty($raw)) {
die('請求體為空');
}
$data = json_decode($raw, true);
if (json_last_error() !== JSON_ERROR_NONE) {
http_response_code(400);
die('JSON 解析錯誤: ' . json_last_error_msg());
}
// 業務邏輯
echo json_encode(['status' => 'ok', 'received' => $data]);
?>
關鍵差異對比
| 特性 | $_POST['field'] | php://input |
| 適用 Content-Type | application/x-www-form-urlencoded, multipart/form-data | **任意** (application/json, text/xml, 等) |
| 自動解析 | 是 (PHP 內置) | 否 (需手動 json_decode/xml_parse) |
| 可讀性 | 多次讀取安全 | **只能讀取一次** (流),需變量保存 |
| 魔術引號影響 | 受 `magic_quotes_gpc` 影響 | 不受影響 |
| 大文件上傳 | 受 `post_max_size` 限制 | 同,但可配合流式處理 |
通用封裝函數 (兼容兩種場景)
<?php
function getJsonInput(): array {
// 1. 嘗試 php://input (標準 API)
$raw = file_get_contents('php://input');
if (!empty($raw)) {
$data = json_decode($raw, true);
if (json_last_error() === JSON_ERROR_NONE) return $data;
}
// 2. 回退:表單字段 txt_json (兼容舊前端)
$jsonStr = $_POST['txt_json'] ?? '';
if ($jsonStr !== '') {
if (ini_get('magic_quotes_gpc') === '1') {
$jsonStr = stripslashes($jsonStr);
}
$data = json_decode($jsonStr, true);
if (json_last_error() === JSON_ERROR_NONE) return $data;
}
// 3. 失敗
http_response_code(400);
die(json_encode(['error' => 'Invalid JSON input', 'code' => 400]));
}
// 使用
$data = getJsonInput();
// $data 保證是關聯數組
?>
安全與健壯性清單
- ✅ **始終用 `json_decode($str, true)`** 返回陣列,避免 `stdClass` 對象屬性訪問差異。
- ✅ **檢查 `json_last_error()`** 捕獲語法錯誤、UTF-8 非法序列、深度超限等。
- ✅ **限制請求體大小**:`ini_set('post_max_size', '2M')` / Nginx `client_max_body_size` / `$_SERVER['CONTENT_LENGTH']` 檢查。
- ✅ **驗證 Content-Type**:`if (strpos($_SERVER['CONTENT_TYPE'] ?? '', 'application/json') !== 0) { /* 拒絕或警告 */ }`
- ✅ **輸入驗證**:解析後對 `$data` 進行結構/類型/範圍驗證 (可用 `respect/validation`、`laminas/validator`)。
- ⚠️ **魔術引號** `magic_quotes_gpc` 已在 PHP 5.4 移除,現代項目無需兼容,但舊項目保留 `stripslashes` 無害。
常見錯誤代碼速查
| 常量 | 值 | 含義 |
| JSON_ERROR_NONE | 0 | 無錯誤 |
| JSON_ERROR_DEPTH | 1 | 嵌套深度超限 (默認 512) |
| JSON_ERROR_STATE_MISMATCH | 2 | 狀態不匹配 (編碼器邏輯錯) |
| JSON_ERROR_CTRL_CHAR | 3 | 控制字符錯誤 (未轉義) |
| JSON_ERROR_SYNTAX | 4 | 語法錯誤 (最常見) |
| JSON_ERROR_UTF8 | 5 | 非法 UTF-8 序列 |
| JSON_ERROR_RECURSION | 6 | 遞歸引用 (編碼時) |
| JSON_ERROR_INF_OR_NAN | 7 | Inf/NaN 數值 (編碼時) |
| JSON_ERROR_UNSUPPORTED_TYPE | 8 | 不支持類型 (如 resource) |