JSON 是現代 Web API 事實標準。PHP 5.2+ 內建 json_encode/json_decode,但舊專案可能運行在 PHP 5.1 或需支援舊版本。本文涵蓋版本相容策略、常用選項旗標、錯誤處理、效能優化與安全防護。
<?php
/**
* 統一 JSON 編碼介面(自動偵測內建函式或回退 polyfill)
*/
function json_encode_compat(mixed $value, int $flags = 0, int $depth = 512): string|false {
if (function_exists('json_encode')) {
return json_encode($value, $flags, $depth);
}
// 回退:載入 polyfill(如 Services_JSON)
require_once __DIR__ . '/vendor/Services_JSON.php';
$json = new Services_JSON(SERVICES_JSON_LOOSE_TYPE);
return $json->encode($value);
}
/**
* 統一 JSON 解碼介面
*/
function json_decode_compat(string $json, bool $assoc = false, int $depth = 512, int $flags = 0): mixed {
if (function_exists('json_decode')) {
return json_decode($json, $assoc, $depth, $flags);
}
require_once __DIR__ . '/vendor/Services_JSON.php';
$json = new Services_JSON(SERVICES_JSON_LOOSE_TYPE);
return $json->decode($json, $assoc);
}
| 旗標 | 版本 | 用途 | 推薦場景 |
|---|---|---|---|
| JSON_HEX_TAG | 5.3 | < > 轉 \u003C \u003E | 嵌入 HTML/JS 防 XSS |
| JSON_HEX_AMP | 5.3 | & 轉 \u0026 | 同上 |
| JSON_HEX_APOS | 5.3 | ' 轉 \u0027 | 同上 |
| JSON_HEX_QUOT | 5.3 | " 轉 \u0022 | 同上 |
| JSON_UNESCAPED_UNICODE | 5.4 | 不轉義中文/Unicode,直接輸出 | 中文 API 必開,減少體積、提升可讀性 |
| JSON_UNESCAPED_SLASHES | 5.4 | 不轉義 / | URL、路徑輸出 |
| JSON_NUMERIC_CHECK | 5.3 | 數字字串轉數值型別 | 謹慎使用,可能改變語意(如手機號、ID) |
| JSON_PRETTY_PRINT | 5.4 | 格式化縮排輸出 | 除錯、日誌、配置檔 |
| JSON_PRESERVE_ZERO_FRACTION | 5.6 | 保留浮點數 .0 | 精確數值傳輸 |
| JSON_THROW_ON_ERROR | 7.3 | 錯誤拋出 JsonException | 現代專案必開,避免靜默失敗 |
| JSON_INVALID_UTF8_IGNORE | 7.2 | 忽略無效 UTF-8 字元 | 髒資料清洗 |
| JSON_INVALID_UTF8_SUBSTITUTE | 7.2 | 無效 UTF-8 替換為 U+FFFD | 同上 |
// API 回應標準旗標(PHP 7.3+)
$flags = JSON_UNESCAPED_UNICODE
| JSON_UNESCAPED_SLASHES
| JSON_THROW_ON_ERROR;
// 除錯/日誌旗標
$debugFlags = $flags | JSON_PRETTY_PRINT;
// 使用
try {
$json = json_encode($data, $flags);
echo $json;
} catch (JsonException $e) {
error_log('JSON 編碼失敗: ' . $e->getMessage());
http_response_code(500);
echo '{"error":"Internal Server Error"}';
}
// PHP 7.3+ 推薦
json_decode($json, true, 512, JSON_THROW_ON_ERROR);
// PHP 7.2 及以下:手動檢查
$data = json_decode($json, true);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new RuntimeException('JSON 解析錯誤: ' . json_last_error_msg());
}
// 常見錯誤代碼
// JSON_ERROR_NONE = 0 正常
// JSON_ERROR_DEPTH = 1 堆疊深度超限
// 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 無限大/非數值
// JSON_ERROR_UNSUPPORTED_TYPE = 8 不支援型別(如 resource)
json_encode 結果(Redis、APCu、檔案快取)。json_encode,再手動拼接 [,](罕見需求)。null、內部欄位、遞迴參考。opcache.preload 載入常用類別,減少編譯開銷。json_decode(substr($input, 0, 1_000_000)) 防止巨大 JSON 耗盡記憶體。is_array, isset, filter_var),不要信任客戶端資料。| 陷阱 | 現象 | 解法 |
|---|---|---|
| 中文變 \uXXXX | 預設 escape Unicode | 加 JSON_UNESCAPED_UNICODE |
| 數字字串變數值 | JSON_NUMERIC_CHECK | 移除該旗標,手動轉型 |
| 資源型別失敗 | resource 無法序列化 | 預先轉陣列/物件 |
| 循環參考 | 遞迴導致錯誤 | 實作 JsonSerializable 或斷開循環 |
| 浮點精度 | 0.1+0.2=0.30000000000000004 | JSON_PRESERVE_ZERO_FRACTION 或字串化 |
掌握 json_encode/decode 的旗標組合、錯誤處理、版本相容、安全限制,是 PHP 開發者的基本功。現代專案(PHP 7.3+)請務必開啟 JSON_THROW_ON_ERROR + JSON_UNESCAPED_UNICODE + JSON_UNESCAPED_SLASHES,並建立統一封裝函式,讓全專案 JSON 行為一致、可測、可維護。