递归函数漏写return导致调用链无法终止而死循环。例如阶乘函数中if判断后未return,后续代码仍执行,使递归不停展开。

递归函数没写 return 导致调用链不断展开
PHP 递归函数死循环最常见原因不是忘了写终止条件,而是写了 if 判断却漏了 return。比如计算阶乘时写成:
function factorial($n) {
if ($n <= 1) {
1; // ❌ 这行没有 return,函数继续往下执行
}
return $n * factorial($n - 1); // 永远走到这儿,$n 不会变小
}PHP 函数默认返回 null,而 null 在数值上下文中是 0,但这里的问题是逻辑没中断——if 块执行完直接进递归调用,根本没退出。
- 所有分支路径都必须有明确的
return(包括终止分支) - 不要依赖“隐式返回”,尤其在递归中
- 用
var_dump($n)或error_log($n)打印每次入参,确认是否真进入了终止分支
递归参数没变化 或变化方向错误
即使写了 if ($n ,如果递归调用传的参数和上一层一样,或者反而变大,照样死循环。典型例子:
function countdown($n) {
if ($n == 0) return;
echo "$n\n";
countdown($n); // ❌ 传的还是 $n,没减 1
}更隐蔽的是浮点数或字符串场景:
立即学习“PHP免费学习笔记(深入)”;
- 用
$n /= 2但起始值是奇数,可能陷入1 → 0.5 → 0.25 → ...无法触达== 0 - 处理数组时用
array_shift()却忘了检查empty($arr),空数组后继续调用会出错或无限递归 - 字符串截取用
substr($s, 1)是安全的,但用mb_substr($s, 0, -1)时若长度为 0 可能不缩容,得加mb_strlen($s) <= 1判断
PHP 默认 max_execution_time 和 xdebug.max_nesting_level 会掩盖问题
死循环不会立刻报错,而是等到超时或嵌套过深才中断,容易误判为“程序卡住”而非逻辑错误。
-
xdebug.max_nesting_level默认常是 256,递归到第 257 层才抛Fatal error: Maximum function nesting level of '256' reached -
max_execution_time超时显示PHP Fatal error: Maximum execution time of X seconds exceeded,但这只是结果,不是根因 - 开发时建议临时调高
xdebug.max_nesting_level(如设为 500),方便观察更多层调用栈;上线前务必恢复并确保逻辑收敛
用 static 变量或引用参数做状态控制要格外小心
有人试图用静态变量记录递归深度来强制退出,比如:
function risky($n) {
static $depth = 0;
$depth++;
if ($depth > 10) return; // ⚠️ 错!$depth 不会在每次调用时重置,且多线程/并发下不可靠
// ...
risky($n - 1);
}这种写法在 CLI 下看似有效,但在 Web SAPI(如 FPM)中 static 变量会跨请求残留,下次请求可能直接触发退出。更糟的是它掩盖了真正的终止条件缺失。
- 递归的状态必须由参数显式传递,而不是靠
static、全局变量或闭包引用 - 如果真需要限制层数,应在参数中带入:
risky($n - 1, $depth + 1),并在入口校验 - 引用参数(
&$data)修改原值没问题,但别指望靠它“跳出”递归——终止仍得靠return
递归真正难的不是写 if,而是确保每次调用都在向那个 if 靠近,而且靠近的方式在所有边界条件下都成立。调试时先盯死参数值的变化轨迹,比看逻辑更管用。



















