递归中修改$var不影响上层值,因php默认值传递;需显式传参、引用或Static变量实现跨层状态共享,对象属性修改则因引用语义而全局生效。

递归函数里改 $var 不影响上一层的值
PHP 的函数参数默认是值传递,递归调用时每一层都拿到变量的副本,改了当前层的 $var,上一层的同名变量完全不受影响。这不是 bug,是 PHP 作用域和传参机制决定的。
常见错误现象:
写了个计数器 $count,想在递归里累加,结果每次打印都是初始值或只加了一次。
- 如果真要跨层共享状态,得显式传参(比如
function foo($data, $count = 0))或者用引用(&$count),但后者容易失控 - 别依赖全局变量来“偷懒”,它会让递归逻辑变脆弱,尤其在并发或重入场景下
- 注意:对象实例默认是引用语义,改
$obj->prop会影响所有层——但这不是变量本身共享,而是指向同一块内存
static 变量在递归里会持续累积
static 变量只初始化一次,且生命周期贯穿整个请求,递归每深入一层,它都还是那个变量。这常被误当作“递归专属状态”,其实它是函数级的,跟调用栈无关。
使用场景:
做简单缓存(比如计算斐波那契第 n 项时缓存已算过的值),或统计某函数总共被调用了几次。
立即学习“PHP免费学习笔记(深入)”;
- 示例:
static $calls = 0; $calls++;每次进函数都自增,不管是不是递归调用 - 性能影响小,但要注意清空逻辑——比如函数可能被多次调用,
static值不会自动重置 - 兼容性没问题,PHP 5.3+ 都支持,但别在 CLI 脚本里长期运行还依赖它,容易内存泄漏
用引用传参时,&$item 确实能改到上一层
只有明确加 & 声明引用参数,才能让递归中对变量的修改反馈到父层。这是最直接的“共享状态”方式,但也是最容易出错的方式。
常见错误现象:
递归中途修改了 &$list,结果上层遍历时发现数组乱序、键丢失,甚至报 Cannot assign by reference to overloaded Object。
- 只对真正需要修改的参数加引用,别一股脑全引;尤其是数组,PHP 数组写时复制(copy-on-write),加引用反而绕过优化
- 对象不用加引用也能改属性,加了引用还可能导致意外的变量绑定(比如把局部变量和参数绑死)
- 递归深度大时,引用链过长可能让调试器显示混乱,var_dump 有时看不出谁指向谁
闭包递归必须用 use (&$func) 才能调用自己
匿名函数不能直接在函数体里写 $func($arg) 来递归,因为定义还没结束,变量不可见。PHP 不支持匿名函数的“自然递归”,得靠引用捕获自己。
示例:
$factorial = function($n) use (&$factorial) {<br> return $n <= 1 ? 1 : $n * $factorial($n - 1);<br>};
- 漏掉
&就会报undefined variable: factorial—— 这是最常见的坑 - 这种写法在 PHP 7.4+ 的箭头函数里不支持,箭头函数不允许
use引用,所以递归必须用普通匿名函数 - 性能略低于普通函数(多了闭包创建开销),但差别极小,别为了这点性能放弃可读性
递归里变量怎么变,关键看你怎么传、怎么声明。值传参最安全,static 最省事,引用最灵活也最危险,闭包递归则有个绕不过去的引用捕获步骤。漏掉任何一个符号,比如 & 或 use,就直接挂。