首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP JSON 深度解析:編碼解碼、中文處理、大數字精度與效能優化

     編輯:PHP 相關知識

PHP JSON 深度解析:編碼解碼、中文處理、大數字精度與效能優化

JSON 是現代 Web 交換資料的標準格式。PHP 5.2+ 內建 json_encode/json_decode,但實際開發中常遇到中文轉義、大數字精度損失、巢狀深度限制、記憶體溢出等問題。本文系統梳理 PHP JSON 操作的核心機制、常見陷阱與最佳實踐。

一、基礎語法與資料類型對應

<?php
// PHP 陣列/物件 → JSON
$arr = ['id' => 1, 'name' => '張三', 'tags' => ['php', 'json'], 'active' => true];
echo json_encode($arr); 
// {"id":1,"name":"\u5f20\u4e09","tags":["php","json"],"active":true}

// JSON → PHP 物件 (預設)
$json = '{"id":1,"name":"張三"}';
$obj = json_decode($json);
echo $obj->name; // 張三

// JSON → PHP 關聯陣列 (第 2 參數 true)
$arr = json_decode($json, true);
echo $arr['name']; // 張三
PHP 類型JSON 類型說明
陣列 (索引 0,1,2...)Array []連續數字鍵
陣列 (關聯鍵)Object {}字串鍵或非連續數字鍵
物件 (stdClass)Object {}public 屬性輸出
整數/浮點數Number科學計數法可能
字串StringUTF-8 必須
true/falsetrue/false
nullnull
resource/類別實例不可序列化需實作 JsonSerializable

二、中文處理:Unicode 轉義 vs 直接輸出

<?php
$data = ['msg' => '中文測試'];

// PHP 5.4+ 預設輸出 Unicode 轉義 (\uXXXX)
echo json_encode($data); 
// {"msg":"\u4e2d\u6587\u6d4b\u8bd5"}

// PHP 5.4+ 使用 JSON_UNESCAPED_UNICODE 直接輸出中文
echo json_encode($data, JSON_UNESCAPED_UNICODE); 
// {"msg":"中文測試"}

// PHP 5.3 及以下:需自行實作或升級 PHP
// 參考本文結尾的 polyfill 方案

最佳實踐:生產環境建議啟用 JSON_UNESCAPED_UNICODE,優點:

  • 減少傳輸體積(中文 3 字節 vs \uXXXX 6 字節)
  • 日誌可讀性強,除錯方便
  • 現代瀏覽器/客戶端完美支援 UTF-8 JSON

三、常用編碼選項組合

<?php
$options = JSON_UNESCAPED_UNICODE 
         | JSON_UNESCAPED_SLASHES   // 不轉義 / (預設會轉 \/)
         | JSON_HEX_TAG             // < > 轉 \u003C \u003E (防 XSS)
         | JSON_HEX_APOS            // ' 轉 \u0027
         | JSON_HEX_QUOT            // " 轉 \u0022
         | JSON_HEX_AMP             // & 轉 \u0026
         | JSON_PRESERVE_ZERO_FRACTION; // 1.0 保持輸出 1.0 而非 1

echo json_encode($data, $options);

四、大數字精度陷阱與解決方案

<?php
$json = '{"order_id": 1234567890123456789, "amount": 99999999999999999.99}';

// 預設解碼:浮點數精度損失
$obj = json_decode($json);
var_dump($obj->order_id); // float(1.2345678901235E+18) 精度丟失!
var_dump($obj->amount);   // float(1.0E+17) 精度丟失!

// 方案 1:JSON_BIGINT_AS_STRING (PHP 5.4+),大整數以字串返回
$obj = json_decode($json, false, 512, JSON_BIGINT_AS_STRING);
var_dump($obj->order_id); // string(19) "1234567890123456789" ✓
var_dump($obj->amount);   // string(20) "99999999999999999.99" ✓

// 方案 2:搭配 true 返回關聯陣列
$arr = json_decode($json, true, 512, JSON_BIGINT_AS_STRING);
// $arr['order_id'] 為字串,需自行轉換或用 bcmath/gmp 運算

注意:JSON_BIGINT_AS_STRING 僅影響超出 PHP 整數範圍的數值(64 位系統為 > 2^63-1)。浮點數同樣以字串返回,避免精度損失。

五、巢狀深度限制與錯誤處理

<?php
// 預設最大深度 512,超過返回 null 並觸發 json_last_error()
$deep = '[' . str_repeat('[', 600) . ']' . str_repeat(']', 600) . ']';
$result = json_decode($deep);
var_dump($result); // null
var_dump(json_last_error()); // JSON_ERROR_DEPTH
var_dump(json_last_error_msg()); // "Maximum stack depth exceeded"

// 自訂深度 (PHP 5.3+ 第 3 參數)
$result = json_decode($deep, false, 1000); // 成功

// 完整錯誤檢查模式
function safeJsonDecode(string $json, bool $assoc = false, int $depth = 512, int $options = 0) {
    $result = json_decode($json, $assoc, $depth, $options);
    $err = json_last_error();
    if ($err !== JSON_ERROR_NONE) {
        throw new RuntimeException('JSON 解析失敗: ' . json_last_error_msg() . " (code: $err)");
    }
    return $result;
}

六、JsonSerializable 介面:自訂物件序列化

<?php
class User implements JsonSerializable {
    private $id;
    private $name;
    private $password; // 敏感欄位不輸出
    private $createdAt;
    
    public function __construct(int $id, string $name, string $password, DateTime $createdAt) {
        $this->id = $id; $this->name = $name; $this->password = $password; $this->createdAt = $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, '張三', 'secret123', new DateTime());
echo json_encode($user, JSON_UNESCAPED_UNICODE);
// {"id":1,"name":"張三","created_at":"2024-01-15T10:30:00+08:00"}

七、效能優化:大資料集處理

場景問題解決方案
超大 JSON 字串 (> 10MB)記憶體峰值高、解析慢使用流式解析器 halaxa/json-machine、salsify/json-streaming-parser
大量物件序列化CPU 佔用高分批處理、json_encode 替代為 igbinary + msgpack (二進制)
頻繁 API 回應重複編碼開銷快取序列化結果、使用 zend-json (C 擴充套件)

流式解析範例 (json-machine)

<?php
require 'vendor/autoload.php';
use JsonMachine\JsonMachine;

$items = JsonMachine::fromFile('huge_data.json'); // 生成器,記憶體常數
foreach ($items as $key => $item) {
    // 逐項處理,如寫入資料庫
    if ($key > 10000) break; // 測試用
}

八、PHP 5.3 兼容 Polyfill (無 JSON_UNESCAPED_UNICODE)

<?php
if (!defined('JSON_UNESCAPED_UNICODE')) {
    define('JSON_UNESCAPED_UNICODE', 256);
    function json_encode_utf8($value, $options = 0, $depth = 512) {
        if (($options & JSON_UNESCAPED_UNICODE) && version_compare(PHP_VERSION, '5.4.0') < 0) {
            // 簡易替代:先編碼,再將 \uXXXX 還原為 UTF-8 字元
            $encoded = json_encode($value, $options & ~JSON_UNESCAPED_UNICODE, $depth);
            return preg_replace_callback('/\\\\u([0-9a-fA-F]{4})/', function($m) {
                return mb_convert_encoding(pack('H*', $m[1]), 'UTF-8', 'UTF-16BE');
            }, $encoded);
        }
        return json_encode($value, $options, $depth);
    }
}

九、常見錯誤速查表

錯誤代碼常見原因排查方向
JSON_ERROR_SYNTAX尾隨逗號、單引號、未轉義控制字元、BOM用 jsonlint.com 驗證、檢查檔案編碼 UTF-8 無 BOM
JSON_ERROR_UTF8字串含非 UTF-8 位元組mb_check_encoding($str, 'UTF-8')、iconv('GBK', 'UTF-8//IGNORE', $str)
JSON_ERROR_DEPTH巢狀過深增加第 3 參數、重構資料結構
JSON_ERROR_RECURSION物件循環引用實作 JsonSerializable 避免循環
JSON_ERROR_INF_OR_NAN浮點數 INF/NAN序列化前檢查 is_finite()

熟練掌握 PHP JSON 的各項特性與選項,能讓你在 API 開發、配置管理、資料交換等場景中避開坑洞,編寫健壯高效的代碼。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved