
通过传入引用参数或返回拼接结果的方式,可完全避免使用类成员变量实现递归路径收集,使函数更纯粹、可测试且线程安全。
在处理嵌套层级结构(如树形菜单、分类路径、祖先链)时,常需将深层嵌套数据“摊平”为有序数组。原始实现借助类属性 $breadcrumb 作为全局累加器,虽简洁但破坏了函数的纯度:它依赖外部状态、不可重入、难以并发调用,且单元测试需额外清理状态。
推荐采用无副作用的递归设计,有两种主流方式:
✅ 方式一:引用参数传递(推荐用于深度可控场景)
private function generateBreadcrumb($structure, &$output = []): array
{
if (!empty($structure)) {
$output[] = [
'id' => $structure['id'],
'name' => $structure['name'],
];
$this->generateBreadcrumb($structure['all_parents'] ?? null, $output);
}
return array_reverse($output);
}⚠️ 注意:array_reverse() 仅在最外层调用后执行一次,内部递归不重复反转,性能合理。&$output 确保所有递归层级共享同一数组引用,避免频繁拷贝。
✅ 方式二:函数式返回拼接(更纯粹,推荐用于高可靠性场景)
private function generateBreadcrumb($structure): array
{
if (empty($structure)) {
return [];
}
// 先递归获取父级路径,再追加当前项 → 自然正序,无需 reverse
return array_merge(
$this->generateBreadcrumb($structure['all_parents'] ?? null),
[['id' => $structure['id'], 'name' => $structure['name']]]
);
}此写法完全无副作用:不修改任何外部变量,输入决定输出,天然支持并发与缓存;但因每次递归都创建新数组,对极深嵌套(>1000 层)可能带来内存开销。
? 使用示例
$data = [
'id' => 5,
'name' => 'Item 5',
'all_parents' => [
'id' => 4,
'name' => 'Item 4',
'all_parents' => [
'id' => 3,
'name' => 'Item 3',
'all_parents' => [
'id' => 2,
'name' => 'Item 2',
'all_parents' => [
'id' => 1,
'name' => 'Item 1',
'all_parents' => null
]
]
]
]
];
$result = $this->generateBreadcrumb($data);
// 输出:[["id"=>1,"name"=>"Item 1"], ..., ["id"=>5,"name"=>"Item 5"]]✅ 总结
- 杜绝类属性依赖是提升代码可维护性与可测试性的关键一步;
- 引用传参适合性能敏感、层级适中的场景;
- 返回拼接方式更符合函数式编程思想,语义清晰,推荐新项目优先采用;
- 两种方案均确保:单次调用、无状态残留、结果确定、易于单元测试。









