PHP 5.4 之前 json_encode() 強制將 Unicode 字元(含中文)轉為 \uXXXX 格式。若對接系統(如 Java)簽名驗證基於原始字串,轉義會導致簽名不匹配。PHP 5.4+ 引入 JSON_UNESCAPED_UNICODE 選項,舊版本需自行實作。
<?php
/**
* 跨版本 JSON 編碼,支援中文不轉義
* PHP 5.4+ 使用原生選項,舊版本手動實作
*/
function json_encode_ex($var, int $options = 0): string {
// PHP 5.4+ 直接使用原生
if (version_compare(PHP_VERSION, '5.4.0', '>=')) {
return json_encode($var, $options | JSON_UNESCAPED_UNICODE);
}
// --- PHP 5.3 及以下 手動實作 ---
static $replacements = [
['\\', '/', "\n", "\t", "\r", "\b", "\f", '"'],
['\\\\', '\\/', '\\n', '\\t', '\\r', '\\b', '\\f', '\\"']
];
// 純量值
if (is_scalar($var)) {
if (is_bool($var)) return $var ? 'true' : 'false';
if (is_null($var)) return 'null';
if (is_float($var)) return (string)$var;
if (is_int($var)) return (string)$var;
// 字串:僅轉義控制字元與雙引號,保留 Unicode
return '"' . str_replace($replacements[0], $replacements[1], (string)$var) . '"';
}
// 非陣列/物件拋異常
if (!is_array($var) && !is_object($var)) {
throw new InvalidArgumentException('json_encode_ex: 不支援的型別 ' . gettype($var));
}
// 判斷是關聯陣列還是索引陣列
$isAssoc = false;
$expectedKey = 0;
foreach ($var as $k => $v) {
if (!is_int($k) || $k !== $expectedKey++) {
$isAssoc = true;
break;
}
}
$parts = [];
if ($isAssoc) {
foreach ($var as $k => $v) {
// 鍵名必須為字串
$keyStr = json_encode_ex((string)$k);
$parts[] = $keyStr . ':' . json_encode_ex($v);
}
return '{' . implode(',', $parts) . '}';
} else {
foreach ($var as $v) {
$parts[] = json_encode_ex($v);
}
return '[' . implode(',', $parts) . ']';
}
}
// --- 使用範例 ---
$data = [
'orderCode' => '1401160935542399',
'creator' => '生活用品測試小屋',
'items' => ['商品A', '商品B'],
'meta' => (object)['id' => 123, 'tag' => '測試']
];
echo json_encode_ex($data);
// 輸出: {"orderCode":"1401160935542399","creator":"生活用品測試小屋","items":["商品A","商品B"],"meta":{"id":123,"tag":"測試"}}
?>
| 處理項目 | 說明 |
|---|---|
| Unicode 保留 | 不做 \uXXXX 轉換,直接輸出 UTF-8 位元組 |
| 控制字元轉義 | 僅轉義 \\ / \n \t \r \b \f " 符合 JSON RFC |
| 關聯/索引陣列判斷 | 鍵為連續整數 0..n-1 視為索引陣列,否則為物件 |
| 物件支援 | stdClass 視為關聯陣列處理 |
| 遞歸深度 | PHP 預設無限制,極深結構可能觸發堆疊溢位 |
pecl install jsonc(社群維護的 JSON 擴展)或使用 composer require symfony/polyfill-php54 但該套件不包含 JSON_UNESCAPED_UNICODE polyfill。<?php
$test = ['中文' => '測試', 'emoji' => '😀', 'nested' => ['a' => 1]];
$json = json_encode_ex($test);
echo $json . "\n";
// {"中文":"測試","emoji":"😀","nested":{"a":1}}
// 反序列化驗證
$back = json_decode($json, true);
assert($back === $test, 'Round-trip 失敗');
echo "Round-trip OK\n";
?>
json_encode_ex() 以 < 50 行代碼解決舊版本 PHP 中文轉義問題,適合無法升級 PHP 的維護專案。新專案請直接使用 PHP 7.4+ 並加上 JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES 獲得最佳體驗。