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

PHP 陣列轉 JSON 的正確姿勢:內建函數 vs 手工實現

     編輯:PHP 綜合知識

前言

將 PHP 陣列轉換為 JSON 字串是現代 Web 開發中最基礎的操作之一。雖然 PHP 5.2+ 內建 json_encode() 已能完美勝任,但理解其背後原理、常見陷阱(如中文 Unicode 轉義、關聯陣列判斷)以及如何在舊版本環境下手工實現,對於資深開發者仍有價值。

一、現代標準做法:json_encode()

<?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_* 常數速查表

常數作用建議場景
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 規範要求所有層級為物件

二、核心陷阱與解決方案

1. 關聯陣列 vs 索引陣列判斷機制

json_encode 依據鍵名決定輸出 [] 或 {}:

  • 連續數字鍵 0,1,2... → 輸出陣列 []
  • 非連續、非數字、或有缺口 → 輸出物件 {}
<?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"}
?>

2. 中文 Unicode 轉義問題

預設輸出 \u5f20\u4e09,雖符合 JSON 標準但難以閱讀、增加傳輸體積。**務必加上 JSON_UNESCAPED_UNICODE**。

3. 資源型別、物件無法直接編碼

<?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]}
?>

4. 循環引用導致錯誤

<?php
$a = ['self' => &$a]; // 循環引用
json_encode($a); // PHP 7.3+ 拋出 JsonException,舊版本死循環或返回 false
?>

三、舊版環境(PHP < 5.2)手工實現參考

僅供歷史維護專案參考,**新專案嚴禁使用**。

<?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);
}
?>

四、效能與最佳實踐總結

  1. 永遠使用內建 json_encode():C 層實現、高度優化、符合標準。
  2. 預設加上 JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES:解決 90% 的中文與路徑問題。
  3. API 輸出前統一封裝:建立 json_response($data, $code = 200) 助手函數,統一設定 Header、選項、錯誤處理。
  4. 大資料集串流輸出:若陣列極大(>10MB),考慮分批編碼或使用 JsonSerializable 配合生成器避免記憶體峰值。
  5. 錯誤處理:PHP 7.3+ 拋出 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 開發、資料序列化、快取存儲等場景中寫出更健壯、高效的程式碼。

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