json_decode() 默认返回 stdClass 对象嵌套,需加第二个参数 true 才转为关联数组;safe_get() 可安全按路径取值,collect_by_key() 能递归收集指定键名的所有非嵌套值。

遇到嵌套数组或对象时,json_decode() 返回什么?
PHP 默认用 json_decode() 解析 AI 返回的 JSON(比如大模型返回的带多层 choices、tool_calls、content 的响应),结果通常是 stdClass 对象嵌套——不是数组,不能直接用 foreach 遍历所有键,也不能用 isset($obj->a->b->c) 安全访问深层字段。
常见错误现象:Trying to get property 'xxx' of non-object 或 Cannot use object of type stdClass as array。
- 加第二个参数
true:强制转成关联数组,更符合 PHP 习惯,也方便递归遍历 - 但要注意:AI 响应中可能混用对象和数组(如
"tools": [{"type": "function", "function": {...}}]),纯数组结构仍需逐层判断类型 - 如果原始 JSON 含空值、null、数字字符串,
json_decode()会如实保留,递归时得提前处理这些边界
怎么写一个安全的递归提取函数?
核心是统一处理「数组」和「对象」两种容器类型,并支持按路径取值(如 choices.0.message.content)或全量扁平化(如把所有 text 字段收进一个数组)。
推荐用递归 + 类型判断,不依赖第三方库:
function safe_get($data, $path, $default = null) {
if (!is_array($data) && !is_object($data)) return $default;
$keys = is_string($path) ? explode('.', $path) : (array)$path;
$current = $data;
foreach ($keys as $key) {
if (is_object($current)) {
$current = isset($current->$key) ? $current->$key : null;
} elseif (is_array($current)) {
$current = isset($current[$key]) ? $current[$key] : null;
} else {
return $default;
}
if ($current === null) return $default;
}
return $current;
}
- 这个
safe_get()能处理stdClass和数组混合结构,比如safe_get($resp, 'choices.0.message.content') - 不要直接用
$resp->choices[0]->message->content,AI 响应字段可能缺失(如流式响应里content初始为null) - 如果路径不确定(比如
tool_calls可能不存在,或长度为 0),必须每层检查,不能靠 try-catch
需要提取多个同名字段(如所有 content)怎么办?
AI 返回常含重复结构(如多轮对话的 messages 数组、多个 tool_calls),这时要「深度搜索」而非「精确路径」。
写一个递归收集器,只关心键名,不关心层级:
function collect_by_key($data, $target_key, &$results = []) {
if (is_object($data)) $data = (array)$data;
if (!is_array($data)) return $results;
foreach ($data as $k => $v) {
if ($k === $target_key && !is_array($v) && !is_object($v)) {
$results[] = $v;
}
if (is_array($v) || is_object($v)) {
collect_by_key($v, $target_key, $results);
}
}
return $results;
}
- 调用
collect_by_key($resp, 'content')就能拿到所有非嵌套的content字符串(跳过function.content这类子结构) - 如果想区分层级,比如只取
message.content,那就别用这个,改用safe_get()配合循环 - 注意性能:AI 响应可能很大(尤其带 base64 图片),避免无意义的全量遍历;先用
isset()或property_exists()快速判断是否存在目标分支
为什么不能直接 json_decode($json, true) 后就用 array_walk_recursive()?
array_walk_recursive() 看似省事,但它会跳过键名为数字的数组(如 ["a","b"] 中的 0、1),更糟的是——它完全忽略对象!只要 json_decode() 没加 true,返回的就是 stdClass,这个函数直接不处理。
- AI 响应里大量使用索引数组(
choices、tool_calls),array_walk_recursive()会漏掉它们的内容 - 即使加了
true,它也无法区分「真正要提取的content」和「工具参数里的content」,容易误取 - 它不提供上下文路径,你无法知道当前值来自哪一层,调试和容错都困难
递归解析的关键不在“怎么遍历”,而在“每一层是否明确知道数据形态和业务含义”。AI 接口文档再详细,实际响应也常有例外——留好 null 分支、预判字段缺失、避免硬编码路径,比追求代码短更重要。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











