递归中用static变量易出问题,因其在多次调用间共享状态,导致数据残留、深度失控、并发错乱及继承冲突;正确做法是用参数传递状态或手动模拟入栈/出栈。

为什么递归里用 static 变量容易出问题
因为 static 变量在函数多次调用间保持值,而递归本质是同一函数反复进入——每次“进栈”都复用同一个 static 变量。这不是作用域错误,是逻辑冲突:你本想为本次递归维护一个局部状态,结果它被下一次调用(甚至同一次递归的兄弟分支)悄悄改掉了。
典型表现包括:
- 第二次调用函数时,返回结果包含第一次的数据残留
- 深度优先遍历中,某分支提前退出后,
static计数器没回退,导致后续层级判断失准 - 多线程/并发请求下(如 FPM 多 worker),不同请求共用同一
static变量,数据彻底错乱
static $depth 用于限深时的正确写法
静态变量可用于控制递归深度,但必须模拟“入栈+出栈”行为,否则 $depth 只增不减,后续调用全失效。
- 进入函数时
$depth++,检查是否超限,超限则return - 所有递归调用结束后、函数返回前,必须
$depth-- - 不能依赖
return自动清理——PHP 不会自动回滚static值 - 若函数有多个
return分支(如条件提前退出),每个分支前都要确保$depth--已执行,否则泄漏
示例:
function traverse($node, $maxDepth = 10) {
static $depth = 0;
$depth++;
if ($depth > $maxDepth) {
$depth--; // 必须先减再 return
return null;
}
// ...处理逻辑
$result = traverse($node->left, $maxDepth);
$result = traverse($node->right, $maxDepth);
$depth--; // 所有递归完成后减回
return $result;
}
替代 static 的更安全方案
绝大多数场景下,把状态作为参数传递,比依赖 static 更清晰、可测、无副作用。
- 用普通参数传深度:
traverse($node, $depth = 0, $maxDepth = 10),无需static,天然隔离 - 需要累积结果?不要用
static $list = [],改为return array_merge(...)或由调用方传入引用数组(&$acc) - 要防环状引用?把已访问 ID 列表作为参数传入,而不是
static $seen = []—— 否则两次调用会互相污染 - PHP 8.1+ 支持命名参数,可让深层调用语义更明确:
traverse($node, depth: $depth + 1, seen: [...$seen, $node->id])
特别注意继承场景下的 static 共享
如果父类方法用了 static 变量,子类未重写该方法,直接调用 B::counter() 会和 A::counter() 共享同一个计数器——这不是 bug,是 PHP 的明确行为。
- 这种共享对递归尤其危险:A 类递归刚跑一半,B 类调用同一方法,
static状态已被破坏 - 解决办法只有两个:子类重写方法并声明自己的
static;或彻底弃用static,改用实例属性或参数传递 - 别指望
unset(static $x)—— PHP 不允许 unset static 变量
真正难缠的从来不是递归本身,而是你以为“只在这次调用里有效”的 static 变量,其实在函数签名之外悄悄串了门。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











