<?php
$a = ['Jack', 'Sam', 'Tom'];
echo json_encode($a);
// 輸出:["Jack","Sam","Tom"] ← JSON Array
$b = ['0' => 'Jack', '1' => 'Sam', '2' => 'Tom'];
echo json_encode($b);
// 輸出:["Jack","Sam","Tom"] ← 依然是 JSON Array
$c = ['person-1' => 'Jack', 'person-2' => 'Sam'];
echo json_encode($c);
// 輸出:{"person-1":"Jack","person-2":"Sam"} ← JSON Object
PHP 陣列同時是「有序映射」與「列表」。json_encode 依據以下規則決定輸出:
0, 1, 2, ... n-1)→ 輸出 JSON Array這符合 JSON 標準:Array 必須為零基索引有序列表;Object 為鍵值對集合。
| PHP 輸出 | JS 解析後 | 可用操作 |
|---|---|---|
| JSON Array | JS Array ['Jack','Sam','Tom'] | .length, [0], .map(), .forEach() |
| JSON Object | JS Object {'person-1':'Jack',...} | ['person-1'], Object.keys(), for...in |
若前端期望 Object 卻收到 Array(或反之),直接導致 Cannot read property 'length' of undefined 或鍵遍歷失效。
JSON_FORCE_OBJECT 旗標(最簡單)<?php
echo json_encode($a, JSON_FORCE_OBJECT);
// {"0":"Jack","1":"Sam","2":"Tom"}
缺點:鍵變為字串 "0",非語義化鍵。
<?php
// 方案 A:手動指定鍵
$arr = ['user_1' => 'Jack', 'user_2' => 'Sam', 'user_3' => 'Tom'];
// 方案 B:自動生成語義鍵
$arr = array_combine(
array_map(fn($i) => "user_{$i}", range(1, count($a))),
$a
);
// 輸出:{"user_1":"Jack","user_2":"Sam","user_3":"Tom"}
<?php
// 統一回應格式
$response = [
'code' => 0,
'msg' => 'ok',
'data' => [
'list' => $a, // 這裡保持 Array 語義
'total' => count($a),
],
];
echo json_encode($response);
// {"code":0,"msg":"ok","data":{"list":["Jack","Sam","Tom"],"total":3}}
此法將「列表語義」保留在 data.list,外層永遠為 Object,前端統一解構 res.data.list,最穩健。
// JS
fetch('/api', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(['a', 'b', 'c']) // 發送 Array
});
// PHP
$data = json_decode(file_get_contents('php://input'), true);
// $data 為 ['a','b','c'] ← 正確還原為 PHP 索引陣列
// 但若 JS 發送 {0:'a',1:'b',2:'c'}(Object)
// PHP 同樣解碼為 ['a','b','c'] ← 因為 json_decode 第二參數 true 強制關聯陣列
json_decode($json, true) 總是返回 PHP 陣列(關聯陣列),無法區分原始 JSON Array/Object。若需保留類型資訊,須用 json_decode($json, false) 返回 stdClass 物件,或檢查原始 JSON 字串首字符 [ vs {。
JSON_FORCE_OBJECT 配合文檔說明。response.json(),依契約斷言 Array.isArray(res.data.list)。json_encode 將「連續零基整數鍵陣列」序列化為 JSON Array 是規格內行為,非 Bug。理解此規則,並在 API 設計層面統一語義(列表用 Array、實體用 Object、外層包裝 Object),即可徹底避免前後端類型不匹配。記住:契約大於慣例,顯式大於隱式。