递归遍历是最直接的拍平方式,需手动实现并严格判断children键存在性、是否为非空数组,推荐返回新数组;大数据量时可用generator避免内存爆炸,而array_walk_recursive会丢失结构信息。

递归遍历是最直接的拍平方式
PHP 没有内置的「多维树拍平」函数,array_merge(...$arr) 或 array_values() 都只处理一层。真正可行的是手动递归——尤其当树节点结构固定(比如每个元素含 id、name、children)时,递归最可控。
注意:别用 foreach 套 foreach 硬写 3 层就停,深度不确定时必然漏数据。必须递归到底。
- 每次进递归前,把当前节点(不含 children)推入结果数组
- 检查是否存在
children键且为非空数组,再递归调用自身 - 避免修改原数组,用引用传参或返回新数组都可,推荐返回新数组更安全
- 如果节点字段名不统一(比如有的叫
sub、有的叫nested),先做一次标准化映射
// 示例:标准 children 结构
function flattenTree($tree, $result = []) {
foreach ($tree as $node) {
$result[] = array_filter($node, function($k) { return $k !== 'children'; }, ARRAY_FILTER_USE_KEY);
if (!empty($node['children']) && is_array($node['children'])) {
$result = array_merge($result, flattenTree($node['children']));
}
}
return $result;
}
用 Generator 避免大数组内存爆炸
当树很深或节点数上万,一次性生成完整数组会吃光内存。yield 是更轻量的选择——它不累积结果,而是边遍历边产出,调用方按需消费。
适用场景:导出 CSV、流式 API 响应、配合 foreach 迭代但不全量加载。
- Generator 函数内部仍用递归,只是把
$result[] = ...换成yield ... - 不能直接用
array_merge合并 Generator,要用iterator_to_array()才能转成数组(此时又回内存问题) - 调用时必须用
foreach,不能用count()或array_map()直接操作
function flattenTreeGen($tree) {
foreach ($tree as $node) {
yield array_filter($node, fn($k) => $k !== 'children', ARRAY_FILTER_USE_KEY);
if (!empty($node['children']) && is_array($node['children'])) {
yield from flattenTreeGen($node['children']);
}
}
}
// 使用
foreach (flattenTreeGen($data) as $item) {
echo $item['id'] . ': ' . $item['name'] . "\n";
}
用 array_walk_recursive 会丢结构信息
array_walk_recursive() 看起来省事,但它会穿透所有嵌套,把所有叶子值扁平输出,完全丢失层级关系和同层字段关联。
典型错误现象:array_walk_recursive($tree, function($v, $k) { echo "$k=>$v\n"; }); 输出一堆 id=>1、name=>A、id=>2…根本分不清哪个 id 对应哪个 name,更没法保留原始键名或位置。
- 仅适用于「只要所有叶子值,不要上下文」的极简需求(比如提取全部文本内容)
- 一旦需要保留节点完整结构(如
['id'=>5, 'name'=>'X', 'level'=>2]),这条路直接废掉 - 它跳过
children数组本身,也跳过任何非标量值,遇到 Closure 或 object 就静默终止
注意 children 键名和空值的健壮性处理
真实业务数据里,children 可能不存在、为 null、为 false、为空数组 [],甚至为字符串 "[]"。硬写 if ($node['children']) 会误判。
常见错误:用 isset($node['children']) 但没检查是否为数组;或用 !empty() 却让 [0 => []] 这种“非空但无有效子项”的数组意外进入递归。
- 推荐判断组合:
array_key_exists('children', $node) && is_array($node['children']) && !empty($node['children']) - 如果后端返回的是 JSON,且
children字段可能为null,反序列化后要提前过滤掉 null 值 - 若需补全层级信息(如
depth或path),把当前深度作为参数传入递归函数,别依赖键名或计数器
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











