PHP递归函数指的是在函数内部直接或间接调用自身的函数结构,这种写法可以简化很多需要重复处理同类逻辑的场景,比如树形结构遍历、阶乘计算、无限级分类处理等。不过递归函数如果没有正确的终止条件,很容易导致程序陷入死循环,消耗大量服务器资源。

PHP递归函数的基本写法
递归函数的核心结构包含两个部分,一个是递归调用自身的逻辑,另一个是终止递归的边界条件。如果没有边界条件,函数会一直调用自身直到超出PHP的最大调用栈深度,触发致命错误。
一个简单的递归函数基本结构如下:
<?php
function recursion($param) {
// 终止条件判断
if (满足终止条件) {
return 终止返回值;
}
// 递归调用逻辑
$result = 处理当前逻辑;
return $result + recursion(修改后的参数);
}
?>
阶乘计算的递归实现
阶乘是递归最经典的应用场景,n的阶乘等于n乘以n-1的阶乘,直到乘到1为止。对应的PHP递归实现代码如下:
<?php
/**
* 计算n的阶乘
* @param int $n 需要计算阶乘的正整数
* @return int 阶乘结果
*/
function factorial($n) {
// 终止条件:1的阶乘是1
if ($n == 1) {
return 1;
}
// 递归调用:n的阶乘等于n乘以n-1的阶乘
return $n * factorial($n - 1);
}
// 测试计算5的阶乘,结果应该是120
echo factorial(5);
?>
调用factorial(5)时,执行过程会是5 * factorial(4),factorial(4)又会调用4 * factorial(3),直到factorial(1)返回1,再逐层返回计算结果,最终得到120。
无限级分类的递归实现
很多系统需要存储无限级分类数据,比如部门架构、商品分类等,这类数据通常用父id关联上级分类,用递归可以很方便地遍历出完整的分类树。假设分类数据如下:
<?php
// 分类数据,id为分类id,pid为父分类id,0表示顶级分类
$categoryList = [
['id' => 1, 'pid' => 0, 'name' => '数码产品'],
['id' => 2, 'pid' => 1, 'name' => '手机'],
['id' => 3, 'pid' => 1, 'name' => '电脑'],
['id' => 4, 'pid' => 2, 'name' => '智能手机'],
['id' => 5, 'pid' => 0, 'name' => '服装'],
['id' => 6, 'pid' => 5, 'name' => '男装'],
];
/**
* 递归获取子分类
* @param array $allList 所有分类数据
* @param int $pid 父分类id
* @return array 子分类树
*/
function getChildCategory($allList, $pid = 0) {
$tree = [];
foreach ($allList as $item) {
if ($item['pid'] == $pid) {
// 递归获取当前分类的子分类
$item['child'] = getChildCategory($allList, $item['id']);
$tree[] = $item;
}
}
return $tree;
}
// 获取完整的分类树
$categoryTree = getChildCategory($categoryList);
print_r($categoryTree);
?>
这个函数会先找出所有pid等于当前传入pid的分类,然后对每个分类递归查找它的子分类,最终组装成完整的树形结构数组。
PHP递归调用的注意事项
- 必须设置明确的终止条件:每个递归函数都必须有至少一个终止条件,否则会无限调用自身,导致栈溢出错误。
- 递归深度限制:PHP默认的最大递归调用深度是1000左右,超过这个深度会抛出致命错误,如果处理逻辑需要的深度超过这个限制,建议改用循环实现。
- 参数传递正确:递归调用时传递给自身的参数必须朝着终止条件的方向变化,否则永远无法触发终止条件。
- 性能问题:递归函数每次调用都会在调用栈中保存当前函数的上下文,过多的递归调用会消耗更多内存,对于性能要求高的场景要谨慎使用。
递归与循环的对比
很多递归能实现的场景也可以用循环实现,两者的选择可以根据场景判断:
| 对比项 | 递归 | 循环 |
|---|---|---|
| 代码简洁度 | 处理树形、分治类问题时代码更简洁 | 处理线性重复逻辑时更直观 |
| 内存消耗 | 较高,每次调用都要保存上下文 | 较低,只需要在当前作用域处理 |
| 深度限制 | 有最大调用栈深度限制 | 没有固定深度限制,只要逻辑正确可以一直执行 |
| 适用场景 | 树形遍历、阶乘、分治算法等 | 数组遍历、累加、简单重复逻辑等 |
如果递归的层级不深,且逻辑用递归写更清晰,优先选择递归;如果层级较深或者性能要求高,建议改用循环实现。