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

PHP json_encode 中文不轉義解決方案:JSON_UNESCAPED_UNICODE 與 Polyfill 實現

     編輯:PHP 相關知識

PHP json_encode 中文不轉義解決方案:JSON_UNESCAPED_UNICODE 與 Polyfill 實現

PHP json_encode() 預設將非 ASCII 字符 (含中文) 轉義為 \uXXXX 格式。本文提供從 PHP 5.3 到 8.0+ 的完整解決方案,含原生選項、Polyfill 實現、效能對比。

問題演示

<?php
$data = ['message' => '你好世界', 'status' => '成功'];
echo json_encode($data);
// 輸出: {"message":"\u4f60\u597d\u4e16\u754c","status":"\u6210\u529f"}
// 調試困難、日誌不可讀、傳輸體積略大

方案一:PHP 5.4+ 原生選項 (推薦)

<?php
// PHP 5.4.0+ 支援
echo json_encode($data, JSON_UNESCAPED_UNICODE);
// 輸出: {"message":"你好世界","status":"成功"}

// 組合選項
$options = JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_NUMERIC_CHECK;
echo json_encode($data, $options);
常量PHP 版本作用
JSON_UNESCAPED_UNICODE5.4+不轉義 Unicode 字符 (中文可讀)
JSON_UNESCAPED_SLASHES5.4+不轉義 / (URL 更乾淨)
JSON_NUMERIC_CHECK5.3.3+數字字符串轉數字類型
JSON_PRETTY_PRINT5.4+格式化輸出 (調試用)
JSON_PRESERVE_ZERO_FRACTION5.6.6+保留浮點數 .0 (如 1.0 不變 1)
JSON_THROW_ON_ERROR7.3+錯誤拋出異常而非返回 false

方案二:PHP 5.3 Polyfill 實現 (不升級版本時)

<?php
class JsonEncoder {
    public static function encode(mixed $value, int $flags = 0): string {
        // 如果支援原生選項,直接使用
        if (defined('JSON_UNESCAPED_UNICODE') && ($flags & JSON_UNESCAPED_UNICODE)) {
            return json_encode($value, $flags);
        }

        return self::encodeRecursive($value, $flags);
    }

    private static function encodeRecursive(mixed $value, int $flags): string {
        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 self::encodeString($value);
        if (is_array($value)) return self::encodeArray($value, $flags);
        if (is_object($value)) return self::encodeObject($value, $flags);
        throw new Exception('不支援的類型: ' . gettype($value));
    }

    private static function encodeString(string $s): string {
        // 關鍵:不轉義 Unicode,僅轉義必要字符
        $replace = [
            '"'  => '\"',
            '\\' => '\\\\',
            "\b" => '\b',
            "\f" => '\f',
            "\n" => '\n',
            "\r" => '\r',
            "\t" => '\t',
        ];
        return '"' . strtr($s, $replace) . '"';
    }

    private static function encodeArray(array $arr, int $flags): string {
        $isList = array_keys($arr) === range(0, count($arr) - 1);
        $parts = [];

        if ($isList) {
            foreach ($arr as $v) {
                $parts[] = self::encodeRecursive($v, $flags);
            }
            return '[' . implode(',', $parts) . ']';
        } else {
            foreach ($arr as $k => $v) {
                $parts[] = self::encodeString((string)$k) . ':' . self::encodeRecursive($v, $flags);
            }
            return '{' . implode(',', $parts) . '}';
        }
    }

    private static function encodeObject(object $obj, int $flags): string {
        // 簡化:轉為關聯數組
        return self::encodeArray((array)$obj, $flags);
    }
}

// 使用
echo JsonEncoder::encode($data, JSON_UNESCAPED_UNICODE);
// {"message":"你好世界","status":"成功"}

方案三:Composer 現成庫 (生產環境推薦)

composer require "symfony/polyfill-php80"  // 含 JSON 相關 polyfill
// 或
composer require "laminas/laminas-json"    // 更完整的 JSON 處理

效能對比測試

<?php
$data = array_fill(0, 10000, ['text' => '中文測試內容', 'id' => 123, 'active' => true]);

// 原生 json_encode (無選項)
$start = microtime(true);
for ($i = 0; $i < 100; $i++) json_encode($data);
echo "原生無選項: " . (microtime(true) - $start) . "s\n"; // ~0.12s

// 原生 + UNESCAPED_UNICODE
$start = microtime(true);
for ($i = 0; $i < 100; $i++) json_encode($data, JSON_UNESCAPED_UNICODE);
echo "原生+UNESCAPED: " . (microtime(true) - $start) . "s\n"; // ~0.13s

// Polyfill
$start = microtime(true);
for ($i = 0; $i < 100; $i++) JsonEncoder::encode($data);
echo "Polyfill: " . (microtime(true) - $start) . "s\n"; // ~0.85s (慢 6-7 倍)

進階:自定義序列化邏輯

<?php
class ApiResponse {
    public int $code;
    public string $message;
    public mixed $data;
    private string $internalToken; // 不想輸出

    public function jsonSerialize(): array {
        return [
            'code' => $this->code,
            'message' => $this->message,
            'data' => $this->data,
            // internalToken 自動排除
        ];
    }
}

$resp = new ApiResponse();
$resp->code = 200;
$resp->message = '操作成功';
$resp->data = ['user_id' => 123];
$resp->internalToken = 'secret';

echo json_encode($resp, JSON_UNESCAPED_UNICODE);
// {"code":200,"message":"操作成功","data":{"user_id":123}}

常見問題排查

現象原因解決
中文仍被轉義PHP < 5.4 或未加選項升級 PHP 或用 Polyfill
輸出 null資料包含非 UTF-8 編碼mb_convert_encoding($data, 'UTF-8', 'auto')
深度遞歸錯誤循環引用對象實現 JsonSerializable 或斷開循環
浮點數精度問題JSON 數字精度限制大數值用字符串傳輸

最佳實踐總結

  1. PHP ≥ 5.4:直接使用 JSON_UNESCAPED_UNICODE,零成本、最高效
  2. PHP 5.3 維護期:引入 symfony/polyfill-php80,無縫升級
  3. API 輸出:統一添加 JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
  4. 日誌記錄:添加 JSON_PRETTY_PRINT 便於人工閱讀
  5. 實現 JsonSerializable:領域模型控制序列化行為,隱藏敏感字段

中文不轉義是 JSON API 友好性的基礎配置。現代 PHP 只需一個常量即可解決,舊版本有成熟 Polyfill。建立統一的 JSON 編碼策略,能避免調試地獄、減少傳輸體積、提升跨語言互操作體驗。

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