PHP 陣列同時具備「有序列表」(Vector) 與「鍵值對應」(Map) 兩重特性,導致 json_encode() 輸出格式在陣列與物件之間切換,造成前後端介接困擾。本文深度解析判斷邏輯、強制輸出技巧與架構層面解決方案。
// ✅ 輸出 JSON 陣列 [] 的條件 (Vector):
// 1. 所有鍵為整數
// 2. 從 0 開始
// 3. 連續遞增 (0, 1, 2, 3...)
// 4. 順序不可變動
$vector = [12, 23, 18]; // [12,23,18]
$vector2 = [0 => 12, 1 => 23, 2 => 18]; // [12,23,18]
// ❌ 輸出 JSON 物件 {} 的情況 (Map):
$map1 = [0 => 12, 1 => 23, 3 => 18]; // 跳過 2 -> {"0":12,"1":23,"3":18}
$map2 = [1 => 12, 2 => 23]; // 不從 0 開始 -> {"1":12,"2":23}
$map3 = ['a' => 1, 'b' => 2]; // 字串鍵 -> {"a":1,"b":2}
$map4 = [2 => 'a', 0 => 'b']; // 順序錯誤 -> {"2":"a","0":"b"}
$data = [1, 2, 3];
// 1. 強制輸出物件
json_encode((object)$data); // {"0":1,"1":2,"2":3}
json_encode($data, JSON_FORCE_OBJECT); // {"0":1,"1":2,"2":3}
// 2. 保留分數精度 (PHP 7.1+)
json_encode(1.5, JSON_PRESERVE_ZERO_FRACTION); // 1.50
// 3. Unicode 不轉義 (PHP 5.4+)
json_encode('中文', JSON_UNESCAPED_UNICODE); // "中文" 而非 "\u4e2d\u6587"
// 4. 斜線不轉義 (PHP 5.4+)
json_encode('https://example.com', JSON_UNESCAPED_SLASHES);
// 5. 組合選項
json_encode($data, JSON_FORCE_OBJECT | JSON_UNESCAPED_UNICODE);
// 陷阱 1:array_filter 導致鍵值不連續
$arr = [1, 2, 3, 4, 5];
$filtered = array_filter($arr, fn($x) => $x > 2);
// $filtered = [2 => 3, 3 => 4, 4 => 5] -> 物件輸出!
// 修正:重新索引
$filtered = array_values(array_filter($arr, fn($x) => $x > 2));
// [3, 4, 5] -> 陣列輸出
// 陷阱 2:unset 導致空洞
$arr = [1, 2, 3];
unset($arr[1]);
// [0 => 1, 2 => 3] -> 物件輸出
// 陷阱 3:資料庫查詢結果預設為關聯陣列
$users = User::find()->asArray()->all();
// 通常包含 'id', 'name' 等字串鍵 -> 物件輸出
// 陷阱 4:數值字串鍵
$arr = ['0' => 'a', '1' => 'b']; // 字串鍵!
json_encode($arr); // {"0":"a","1":"b"} 物件
// 強制轉換為整數鍵
$arr = array_map('intval', array_keys($arr)); // 不正確
// 正確:
$arr = array_combine(array_map('intval', array_keys($arr)), array_values($arr));
// 定義明確型別類別
class JsonVector implements \JsonSerializable {
private $data = [];
public function __construct(array $items = []) {
$this->data = array_values($items); // 強制重新索引
}
public function add($item) {
$this->data[] = $item;
}
public function jsonSerialize() {
return $this->data; // 保證輸出陣列
}
}
class JsonMap implements \JsonSerializable {
private $data = [];
public function __construct(array $data = []) {
$this->data = $data;
}
public function set($key, $value) {
$this->data[$key] = $value;
}
public function jsonSerialize() {
return $this->data; // 保證輸出物件
}
}
// 使用
$vector = new JsonVector([1, 2, 3]);
$vector->add(4);
echo json_encode($vector); // [1,2,3,4] 保證陣列
$map = new JsonMap(['name' => 'John', 'age' => 30]);
echo json_encode($map); // {"name":"John","age":30} 保證物件
| 資料類型 | JSON 格式 | PHP 型別建議 | 範例 |
|---|---|---|---|
| 列表/集合 | 陣列 [] | JsonVector / 索引陣列 | 用戶列表、商品清單 |
| 單一實體/物件 | 物件 {} | JsonMap / 關聯陣列 | 用戶詳情、訂單資訊 |
| 字典/查找表 | 物件 {} | JsonMap | 設定選項、狀態碼對照 |
| 樹狀結構 | 巢狀物件/陣列 | 遞迴型別 | 選單結構、分類樹 |
{"code": 0, "data": [...], "message": ""},data 嚴格區分陣列/物件JSON_FORCE_OBJECT 會略微增加編碼時間 (需建立物件結構)array_values() 重新索引比強制轉物件更快json_encode(),收集完整資料結構後一次編碼理解 json_encode() 的 Vector/Map 判斷邏輯,是 PHP 後端與前端/行動端無縫對接的關鍵。架構層面引入明確型別封裝,能從根源消除格式不穩定問題。