JSON(JavaScript Object Notation)已成為 Web API 數據交換的標準格式。PHP 提供 json_encode() 和 json_decode() 兩大核心函數,實現陣列/物件與 JSON 字符串的高效雙向轉換。本文系統梳理參數選項、編碼規則、常見陷阱與性能優化策略。
json_encode(mixed $value, int $flags = 0, int $depth = 512): string|false
| 常量 | 作用 | 推薦場景 |
|---|---|---|
JSON_UNESCAPED_UNICODE | 不義 Unicode 字符(中文直接輸出) | 強烈推薦,提升可讀性、減小體積 |
JSON_UNESCAPED_SLASHES | 不轉義 / 字符 | URL、路徑包含斜槿時 |
JSON_HEX_TAG | 轉義 < > & 為 \u003C 等 | 直接嵌入 HTML <script> 標籤時防 XSS |
JSON_HEX_APOS | 轉義 ' 為 \u0027 | 嵌入 HTML 屬性值(單引號包裹) |
JSON_HEX_QUOT | 轉義 " 為 \u0022 | 嵌入 HTML 屬性值(雙引號包裹) |
JSON_NUMERIC_CHECK | 數字字符串轉為數值類型 | 謹慎使用,可能導致精度丟失(如大整數、手機號) |
JSON_PRETTY_PRINT | 格式化縮進輸出 | 調試、日誌、配置文件生成 |
JSON_PRESERVE_ZERO_FRACTION | 保留浮點數 .0(如 1.0 不變 1) | 需嚴格區分整數/浮點的 API |
JSON_FORCE_OBJECT | 索引陣列強制輸出為物件 | API 要求物件格式 |
JSON_THROW_ON_ERROR | 錯誤拋出 JsonException(PHP 7.3+) | 現代錯誤處理,避免靜默失敗 |
<?php
$assoc = ['name' => '張三', 'age' => 25, 'tags' => ['php', 'json', 'api']];
$indexed = ['蘋果', '香蕉', '橙子'];
// 關聯陣列 → JSON 物件
echo json_encode($assoc, JSON_UNESCAPED_UNICODE);
// {"name":"張三","age":25,"tags":["php","json","api"]}
// 索引陣列 → JSON 陣列
echo json_encode($indexed, JSON_UNESCAPED_UNICODE);
// ["蘋果","香蕉","橙子"]
// 強制索引陣列輸出為物件
echo json_encode($indexed, JSON_UNESCAPED_UNICODE | JSON_FORCE_OBJECT);
// {"0":"蘋果","1":"香蕉","2":"橙子"}
// 格式化輸出(調試用)
echo json_encode($assoc, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
/*
{
"name": "張三",
"age": 25,
"tags": [
"php",
"json",
"api"
]
}
*/
?>
<?php
$data = [
'null' => null,
'true' => true,
'false' => false,
'int' => 123,
'float' => 3.14,
'inf' => INF,
'nan' => NAN,
'empty_arr' => [],
'empty_obj' => new stdClass(),
];
echo json_encode($data, JSON_UNESCAPED_UNICODE);
// {"null":null,"true":true,"false":false,"int":123,"float":3.14,"inf":null,"nan":null,"empty_arr":[],"empty_obj":{}}
// 注意:INF/NAN 會變為 null,空陣列為 [],空物件為 {}
?>
json_decode(string $json, bool $associative = false, int $depth = 512, int $flags = 0): mixed
false(預設):返回 stdClass 物件true:返回關聯陣列(強烈推薦,操作統一、性能略優)| 常量 | 作用 |
|---|---|
JSON_BIGINT_AS_STRING | 大整數作為字符串返回(防精度丟失,PHP 5.4+) |
JSON_OBJECT_AS_ARRAY | 等同 $associative=true(PHP 7.2+) |
JSON_THROW_ON_ERROR | 錯誤拋出 JsonException(PHP 7.3+) |
<?php
$json = '{"id":1234567890123456789,"name":"李四","active":true,"scores":[95,87,92]}';
// 返回物件(預設)
$obj = json_decode($json);
echo $obj->name; // 李四
echo $obj->id; // 1.2345678901235E+18 (精度丟失!)
// 返回關聯陣列(推薦)
$arr = json_decode($json, true);
echo $arr['name']; // 李四
// 大整數保持字符串(防精度丟失)
$arrSafe = json_decode($json, true, 512, JSON_BIGINT_AS_STRING);
echo $arrSafe['id']; // "1234567890123456789" (精確保留)
// 錯誤處理(PHP 7.3+)
try {
$data = json_decode($invalidJson, true, 512, JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
error_log("JSON 解析失敗: " . $e->getMessage());
}
?>
預設最大深度 512,超過返回 false 並觸發錯誤。可通過第三參數調整:
<?php
$deep = '[[[[[...]]]]]'; // 超過 512 層
json_decode($deep, true, 1024); // 提高限制
?>
<?php
function safeJsonDecode(string $json, bool $assoc = true): array|object|null {
try {
return json_decode($json, $assoc, 512, JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
error_log("JSON 解析錯誤: {$e->getMessage()} at position {$e->getCode()}");
return null; // 或拋出自定義異常
}
}
?>
<?php
$json = '{"a":1}';
$data = json_decode($json, true);
if (json_last_error() !== JSON_ERROR_NONE) {
error_log("JSON 錯誤: " . json_last_error_msg());
// 處理錯誤...
}
?>
| 陷阱 | 原因 | 解決方案 |
|---|---|---|
| 中文變 \uXXXX | 預設轉義 Unicode | 使用 JSON_UNESCAPED_UNICODE |
| 大整數精度丟失 | PHP 浮點數精度限制(IEEE 754) | 解碼用 JSON_BIGINT_AS_STRING;編碼端確保傳字符串 |
| 空陣列 [] vs 空物件 {} | PHP 陣列無法區分索引/關聯 | 用 JSON_FORCE_OBJECT 強制物件;或確保鍵非連續數字 |
| INF/NAN 變 null | JSON 標準不支援 | 序列化前檢查 is_finite(),轉為字符串或 null |
| 循環引用導致錯誤 | 物件/陣列相互引用 | 實現 JsonSerializable 接口控制輸出;或斷開循環 |
| BOM 頭導致解析失敗 | UTF-8 BOM (EF BB BF) 非 JSON 標準 | json_decode(ltrim($json, "\xEF\xBB\xBF")) |
<?php
function jsonResponse(mixed $data, int $code = 200, string $message = ''): void {
http_response_code($code);
header('Content-Type: application/json; charset=utf-8');
$payload = [
'code' => $code,
'message' => $message,
'data' => $data,
'timestamp' => time(),
];
echo json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
exit;
}
// 使用
jsonResponse(['user_id' => 123], 200, '登錄成功');
?>
<?php
class User implements JsonSerializable {
private int $id;
private string $name;
private string $password; // 敏感字段
private DateTime $createdAt;
public function jsonSerialize(): array {
return [
'id' => $this->id,
'name' => $this->name,
'created_at' => $this->createdAt->format('c'), // ISO 8601
// 故意不輸出 password
];
}
}
$user = new User(1, 'admin', 'secret', new DateTime());
echo json_encode($user, JSON_UNESCAPED_UNICODE);
// {"id":1,"name":"admin","created_at":"2024-01-15T10:30:00+08:00"}
?>
對於超大 JSON,使用 halaxa/json-machine 等流式解析庫,而非一次性 json_decode()。
json_encode($bigArray) 遠快於循環 json_encode($item)。掌握 json_encode/decode 的參數組合、錯誤處理、精度控制與序列化定制,能讓你構建高效、健壯、可互操作的 API 服務。現代 PHP 開發中,統一使用關聯陣列、啟用 UNESCAPED_UNICODE、善用 JSON_THROW_ON_ERROR 是專業的標誌。