將 PHP 陣列轉換為 JSON 字串是現代 Web 開發中最基礎的操作之一。雖然 PHP 5.2+ 內建 json_encode() 已能完美勝任,但理解其背後原理、常見陷阱(如中文 Unicode 轉義、關聯陣列判斷)以及如何在舊版本環境下手工實現,對於資深開發者仍有價值。
<?php
$data = [
'id' => 1001,
'name' => '張三',
'tags' => ['php', 'json', 'api'],
'score' => 98.5,
'active' => true
];
// 基本用法
$json = json_encode($data);
echo $json;
// 輸出: {"id":1001,"name":"\u5f20\u4e09","tags":["php","json","api"],"score":98.5,"active":true}
// 常用選項組合(推薦生產環境使用)
$options = JSON_UNESCAPED_UNICODE // 中文不轉義,直接輸出可讀字元
| JSON_UNESCAPED_SLASHES // 不轉義斜線 /
| JSON_NUMERIC_CHECK; // 數值字串自動轉數值型(謹慎使用)
$jsonPretty = json_encode($data, $options | JSON_PRETTY_PRINT);
echo $jsonPretty;
/*
{
"id": 1001,
"name": "張三",
"tags": ["php", "json", "api"],
"score": 98.5,
"active": true
}
*/
?>
| 常數 | 作用 | 建議場景 |
|---|---|---|
| JSON_UNESCAPED_UNICODE | 不將 Unicode 字元轉為 \uXXXX | 含中文/多國語言資料,**強烈建議永遠開啟** |
| JSON_UNESCAPED_SLASHES | 不轉義 / 為 \/ | 輸出 URL、正則路徑時 |
| JSON_PRETTY_PRINT | 美化縮排輸出 | 除錯、日誌、API 文檔 |
| JSON_NUMERIC_CHECK | 將數值字串 "123" 轉為 123 | **慎用**:可能誤轉手機號、身分證等長數字串 |
| JSON_PRESERVE_ZERO_FRACTION | 保留浮點數 .0 (如 1.0 不變 1) | 需嚴格區分整數/浮點的 API |
| JSON_FORCE_OBJECT | 強制索引陣列輸出為物件 {} | API 規範要求所有層級為物件 |
json_encode 依據鍵名決定輸出 [] 或 {}:
[]{}<?php
$arr1 = ['a', 'b', 'c']; // [0=>'a',1=>'b',2=>'c'] → ["a","b","c"]
$arr2 = [1 => 'a', 2 => 'b']; // 鍵從 1 開始 → {"1":"a","2":"b"}
$arr3 = [0 => 'a', 2 => 'c']; // 缺口 → {"0":"a","2":"c"}
$arr4 = ['name' => 'John']; // 字串鍵 → {"name":"John"}
// 強制索引陣列輸出為物件
echo json_encode($arr1, JSON_FORCE_OBJECT); // {"0":"a","1":"b","2":"c"}
?>
預設輸出 \u5f20\u4e09,雖符合 JSON 標準但難以閱讀、增加傳輸體積。**務必加上 JSON_UNESCAPED_UNICODE**。
<?php
$fp = fopen('test.txt', 'r');
echo json_encode($fp); // null,並觸發 Warning
class User { public $name = 'Tom'; }
echo json_encode(new User()); // {"name":"Tom"} ✓
// 實作 JsonSerializable 介面自訂輸出
class ApiResponse implements JsonSerializable {
private $data;
public function __construct($data) { $this->data = $data; }
public function jsonSerialize() { return ['status'=>'ok', 'data'=>$this->data]; }
}
echo json_encode(new ApiResponse([1,2,3])); // {"status":"ok","data":[1,2,3]}
?>
<?php $a = ['self' => &$a]; // 循環引用 json_encode($a); // PHP 7.3+ 拋出 JsonException,舊版本死循環或返回 false ?>
僅供歷史維護專案參考,**新專案嚴禁使用**。
<?php
/**
* 簡易手工 JSON 編碼(不支援所有邊界情況)
* @param mixed $value
* @return string
*/
function legacy_json_encode($value) {
if (is_null($value)) return 'null';
if (is_bool($value)) return $value ? 'true' : 'false';
if (is_int($value) || is_float($value)) return (string)$value;
if (is_string($value)) return '"' . _escape_string($value) . '"';
if (is_array($value)) {
$isAssoc = _is_associative($value);
$parts = [];
if ($isAssoc) {
foreach ($value as $k => $v) {
$parts[] = '"' . _escape_string((string)$k) . '":' . legacy_json_encode($v);
}
return '{' . implode(',', $parts) . '}';
} else {
foreach ($value as $v) {
$parts[] = legacy_json_encode($v);
}
return '[' . implode(',', $parts) . ']';
}
}
if (is_object($value)) {
// 簡單處理:轉為關聯陣列
return legacy_json_encode((array)$value);
}
return 'null'; // resource 等不可序列化型別
}
function _escape_string($str) {
// 僅處理基本跳脫,實際需符合 RFC 8259
return str_replace(
['\\', '"', "\n", "\r", "\t", "\b", "\f"],
['\\\\', '\\"', '\\n', '\\r', '\\t', '\\b', '\\f'],
$str
);
}
function _is_associative(array $arr): bool {
if (empty($arr)) return false;
$keys = array_keys($arr);
return $keys !== range(0, count($arr) - 1);
}
?>
json_encode():C 層實現、高度優化、符合標準。JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES:解決 90% 的中文與路徑問題。json_response($data, $code = 200) 助手函數,統一設定 Header、選項、錯誤處理。JsonSerializable 配合生成器避免記憶體峰值。JsonException,建議 try-catch 捕獲並記錄 json_last_error_msg()。<?php
// 生產環境建議的封裝函數
function json_encode_safe($data, int $options = 0): string {
$defaultOptions = JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES;
$flags = $defaultOptions | $options;
$result = json_encode($data, $flags);
if ($result === false) {
throw new RuntimeException('JSON 編碼失敗: ' . json_last_error_msg());
}
return $result;
}
?>
PHP 陣列轉 JSON 看似簡單,實則涉及編碼標準、型別映射、效能考量等多層面知識。掌握 json_encode 的選項組合與常見坑點,能讓你在 API 開發、資料序列化、快取存儲等場景中寫出更健壯、高效的程式碼。