递归函数需返回一维数组,先追加当前节点字段,再判空递归合并子节点;避免未判空访问children、无限递归及json解码为对象等常见错误。

递归函数怎么写才能正确展开嵌套的 category 数组
直接用 foreach 遍历一层就停,肯定得不到扁平结果。关键是要在子节点存在时,把子节点的扁平化结果拼到当前层级后面,而不是只处理当前层。返回值必须是「一维数组」,不能是 void 或 echo 输出。
常见错误现象:Notice: Undefined index: children(没判空就访问 children)、Maximum function nesting level of '256' reached(无限递归,比如子节点误指回父节点)。
实操建议:
- 入口函数先初始化一个空数组,作为最终结果容器
- 递归函数接收两个参数:当前节点数组 + 引用传入的结果数组(或直接 return 合并后的数组)
- 每次进入函数,先 push 当前节点的 id/name 等字段到结果中
- 再检查是否存在
children键且为非空数组,满足则 foreach 递归调用自身
用 return 拼接 vs 用 & 引用传参,哪种更安全
两种都可行,但引用传参稍易出错:如果忘记加 & 符号,或者在递归调用里漏传引用,结果数组就不会被修改。而 return 方式逻辑更线性,每层都 return 新数组,靠 array_merge 合并,调试时更容易单步验证中间结果。
示例(return 方式):
function flattenCategories($categories) {
$result = [];
foreach ($categories as $cat) {
$result[] = ['id' => $cat['id'], 'name' => $cat['name']];
if (!empty($cat['children']) && is_array($cat['children'])) {
$result = array_merge($result, flattenCategories($cat['children']));
}
}
return $result;
}
注意:array_merge 对索引数组会重排键名,若需保持顺序且依赖数字下标,可用 [] 追加代替 array_merge;但扁平化场景通常不依赖原始键。
遇到深层嵌套(>10 层)性能明显变慢怎么办
PHP 默认递归深度限制是 256,10 层本身不会触发,但大量数据 + 每层都 array_merge 会产生多次内存拷贝,时间复杂度接近 O(n²)。真正瓶颈往往在这里,不是递归本身。
优化方向:
- 改用「引用传参 +
array_push」避免反复合并数组 - 确保
children字段在数据库查询时已按需加载,不要在递归中再查库 - 若分类树固定且读多写少,考虑缓存扁平结果(如 Redis 存序列化数组),避免每次请求都递归
特别注意:不要在递归函数里做 DB 查询、curl 请求或文件读取——这些操作会随层数指数级放大开销。
JSON 输入含 children 但 PHP 中变成 stdClass 怎么办
用 json_decode($json) 默认返回对象,此时 $cat['children'] 会报错。必须加第二个参数 true: json_decode($json, true),否则连 empty($cat['children']) 都可能判断失准(对象的 empty 行为和数组不同)。
另一个坑:前端传来的 JSON 可能有字符串型数字 ID(如 "id": "123"),PHP 里当数组键用会自动转 int,但若后续拼接 URL 或 SQL,类型不一致可能引发隐式转换问题。建议在扁平化前统一 cast:(int)$cat['id']。
递归本身不难,难的是数据形态是否干净、边界是否兜住、副作用是否隔离。尤其要注意 children 字段的存在性、类型、空值定义,这三处出错占了实际项目中 80% 的问题。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











