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

PHP json_encode 中文亂碼/Null 終極解決方案:GBK/GB2312 編碼環境下的完美處理

     編輯:PHP 相關知識

json_encode 是 PHP 處理 JSON 的核心函數,但其只支持 UTF-8 編碼。在 GBK/GB2312 編碼的項目中,直接使用會導致中文變為 null 或產生 Unicode 轉義序列(如 \u4e2d\u6587)。本文提供從根因分析到多種解決方案的完整指南。

問題現象與根因

現象一:中文變 null

<?php
// 文件編碼:GBK
$arr = ['name' => '中文', 'value' => 'test'];
echo json_encode($arr);
// 輸出: {"name":null,"value":"test"}
?>

原因:json_encode 遇到非 UTF-8 字符直接返回 null(PHP 5.5+)或報錯(舊版本)。

現象二:UTF-8 環境下產生 Unicode 轉義

<?php
// 文件編碼:UTF-8
$arr = ['name' => '中文', 'value' => 'test'];
echo json_encode($arr);
// 輸出: {"name":"\u4e2d\u6587","value":"test"}
?>

原因:默認啟用 JSON_UNESCAPED_UNICODE 選項(PHP 5.4+)可禁用轉義。

方案一:編碼轉換(GBK 項目必用)

單字符串轉換

<?php
$data = "中文內容";
// GBK 轉 UTF-8,IGNORE 忽略無法轉換字符
$utf8 = iconv('GBK', 'UTF-8//IGNORE', $data);
// 或 GB2312
$utf8 = iconv('GB2312', 'UTF-8//IGNORE', $data);
echo json_encode(['content' => $utf8]);
?>

數組/對象遞歸轉換(生產級)

<?php
/**
 * 將任意變量從 GBK/GB2312 遞歸轉換為 UTF-8
 * @param mixed $data
 * @param string $fromEncoding 源編碼
 * @return mixed
 */
function toUtf8(mixed $data, string $fromEncoding = 'GBK'): mixed {
    if (is_array($data)) {
        $result = [];
        foreach ($data as $key => $value) {
            $result[toUtf8($key, $fromEncoding)] = toUtf8($value, $fromEncoding);
        }
        return $result;
    } elseif (is_object($data)) {
        $vars = get_object_vars($data);
        foreach ($vars as $key => $value) {
            $data->$key = toUtf8($value, $fromEncoding);
        }
        return $data;
    } elseif (is_string($data)) {
        return iconv($fromEncoding, 'UTF-8//IGNORE', $data);
    }
    return $data;
}

// 使用
$gbkData = ['name' => '測試', 'items' => ['項目一', '項目二']];
$utf8Data = toUtf8($gbkData);
echo json_encode($utf8Data, JSON_UNESCAPED_UNICODE);
// 輸出: {"name":"測試","items":["項目一","項目二"]}
?>

方案二:urlencode/urldecode 技巧(無需 iconv,兼容性極佳)

利用 urlencode 將中文編碼為 ASCII 安全字符,json_encode 後再 urldecode 還原。

<?php
function jsonEncodeChinese(mixed $data): string {
    if (is_array($data)) {
        foreach ($data as $key => $value) {
            $data[$key] = jsonEncodeChinese($value);
        }
        return $data;
    } elseif (is_string($data)) {
        return urlencode($data);
    }
    return $data;
}

function jsonDecodeChinese(string $json): mixed {
    $data = json_decode($json, true);
    return urldecode($data);
}

// 使用
$arr = ['name' => '中文測試', 'tags' => ['標籤一', '標籤二']];
$encoded = jsonEncodeChinese($arr);
$json = json_encode($encoded);
echo urldecode($json);
// 輸出: {"name":"中文測試","tags":["標籤一","標籤二"]}
?>

優點:不依賴 iconv 擴展、處理混合編碼更健壯、性能優於 iconv。
缺點:字符串長度增加約 3 倍(每個中文變 9 字符 %E4%B8%AD),大數據量需注意內存。

方案三:PHP 5.4+ 官方選項(UTF-8 環境首選)

<?php
$arr = ['name' => '中文', 'value' => 'test'];

// 關鍵選項:JSON_UNESCAPED_UNICODE (256)
echo json_encode($arr, JSON_UNESCAPED_UNICODE);
// 輸出: {"name":"中文","value":"test"}

// 組合常用選項
$options = JSON_UNESCAPED_UNICODE 
         | JSON_UNESCAPED_SLASHES   // 不轉義 /
         | JSON_HEX_TAG             // 轉義 < > 防 XSS
         | JSON_HEX_APOS            // 轉義 '
         | JSON_HEX_QUOT            // 轉義 "
         | JSON_HEX_AMP;            // 轉義 &

echo json_encode($arr, $options);
?>

方案四:現代 PHP 8.1+ JsonException 模式

<?php
$arr = ['name' => '中文', 'data' => [1, 2, 3]];

try {
    $json = json_encode($arr, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR);
    echo $json;
} catch (JsonException $e) {
    error_log("JSON 編碼失敗: " . $e->getMessage());
    // 降級處理
    echo json_encode(['error' => 'encoding_failed']);
}
?>

完整對比表

方案適用場景PHP 版本性能依賴推薦指數
iconv 轉 UTF-8GBK/GB2312 老項目4.0.5+中iconv 擴展⭐⭐⭐⭐
urlencode 技巧混合編碼、無 iconv 環境4.0+高無⭐⭐⭐⭐⭐
JSON_UNESCAPED_UNICODEUTF-8 新項目5.4+最高無⭐⭐⭐⭐⭐
JSON_THROW_ON_ERROR現代異常處理8.1+最高無⭐⭐⭐⭐

生產環境最佳實踐:統一 JSON 服務類

<?php
class JsonService {
    private static $defaultOptions = JSON_UNESCAPED_UNICODE 
                                   | JSON_UNESCAPED_SLASHES 
                                   | JSON_HEX_TAG 
                                   | JSON_HEX_APOS 
                                   | JSON_HEX_QUOT 
                                   | JSON_HEX_AMP;
    
    public static function encode(mixed $data, int $options = 0, bool $autoUtf8 = true): string {
        if ($autoUtf8 && !self::isUtf8($data)) {
            $data = self::convertToUtf8($data);
        }
        
        $finalOptions = self::$defaultOptions | $options;
        
        if (PHP_VERSION_ID >= 80100) {
            $finalOptions |= JSON_THROW_ON_ERROR;
            return json_encode($data, $finalOptions);
        }
        
        $json = json_encode($data, $finalOptions);
        if (json_last_error() !== JSON_ERROR_NONE) {
            throw new RuntimeException('JSON 編碼失敗: ' . json_last_error_msg());
        }
        return $json;
    }
    
    public static function decode(string $json, bool $assoc = true, int $depth = 512): mixed {
        if (PHP_VERSION_ID >= 80100) {
            return json_decode($json, $assoc, $depth, JSON_THROW_ON_ERROR);
        }
        $result = json_decode($json, $assoc, $depth);
        if (json_last_error() !== JSON_ERROR_NONE) {
            throw new RuntimeException('JSON 解碼失敗: ' . json_last_error_msg());
        }
        return $result;
    }
    
    private static function isUtf8(mixed $data): bool {
        if (is_string($data)) {
            return mb_check_encoding($data, 'UTF-8');
        }
        if (is_array($data)) {
            foreach ($data as $v) {
                if (!self::isUtf8($v)) return false;
            }
        }
        return true;
    }
    
    private static function convertToUtf8(mixed $data): mixed {
        // 優先嘗試檢測編碼,常見 GBK/GB2312
        $encodings = ['GBK', 'GB2312', 'BIG5'];
        foreach ($encodings as $enc) {
            if (self::tryConvert($data, $enc)) {
                return self::doConvert($data, $enc);
            }
        }
        // 降級:urlencode 技巧
        return self::urlencodeRecursive($data);
    }
    
    private static function tryConvert(mixed $data, string $from): bool {
        if (is_string($data)) {
            @iconv($from, 'UTF-8', $data);
            return iconv_get_last_error() === ICONV_ERR_SUCCESS;
        }
        return true;
    }
    
    private static function doConvert(mixed $data, string $from): mixed {
        if (is_array($data)) {
            return array_map(fn($v) => self::doConvert($v, $from), $data);
        }
        if (is_string($data)) {
            return iconv($from, 'UTF-8//IGNORE', $data);
        }
        return $data;
    }
    
    private static function urlencodeRecursive(mixed $data): mixed {
        if (is_array($data)) {
            return array_map([self::class, 'urlencodeRecursive'], $data);
        }
        return is_string($data) ? urlencode($data) : $data;
    }
}

// 使用
echo JsonService::encode(['msg' => '你好世界', 'code' => 200]);
// {"msg":"你好世界","code":200}
?>

避坑指南

  1. 文件編碼統一:新項目強制使用 UTF-8 無 BOM,編輯器/IDE 設置統一
  2. 數據庫連接編碼:PDO 設置 charset=utf8mb4,MySQLi 執行 SET NAMES utf8mb4
  3. HTML 頁面聲明:<meta charset="UTF-8">
  4. API 響應頭:Content-Type: application/json; charset=utf-8
  5. 不要手動拼接 JSON:永遠使用 json_encode,防止注入與語法錯誤

徹底解決 json_encode 中文問題的關鍵在於:源頭統一 UTF-8。老項目若無法全面轉碼,請使用 JsonService 封裝類,在邊界處統一處理編碼轉換,業務代碼保持純淨。

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