PHP 闭包与 use 关键字:变量作用域的 4 个易错点

阿乐
阿乐 星耀SVIP管理员 黑卡会员
发布于 2026-09-14 16:07 ·1 浏览 ·0 回复

结论先说:PHP 闭包对外层变量默认是「按值复制」,`use` 决定的只是捕获谁、按值还是按引用,而不是"自动共享外层作用域"。下面 4 个坑全都围绕这一句话。

一、use 默认按值捕获,闭包内改它不影响外部

结论:`function () use ($n)` 里的 `$n` 是闭包创建那一刻的一份副本,闭包内 `$n++` 改的是副本,外层看不到。

$count = 0;
$add = function () use ($count) { $count++; };
$add(); $add();
echo $count; // 0

需要把结果写回外层,必须显式加 `&`:

$count = 0;
$add = function () use (&$count) { $count++; };
$add(); $add();
echo $count; // 2

数组同理:`use ($arr)` 里 `$arr[] = 1` 不会影响外层,`use (&$arr)` 才会。这也是累加器、状态收集器最常见的翻车点——以为闭包"天然能改外面的值"。

二、循环里捕获变量:当心拿到"最后一个值"

结论:在循环中生成闭包数组时,按值捕获是"定格当前值",按引用捕获是"所有闭包共享同一个变量",两者结果完全不同。

$fns = [];
for ($i = 0; $i < 3; $i++) {
    $fns[] = function () use ($i) { return $i; };
}
echo $fns[0](); // 0(定义时的快照)

改成引用捕获,三个闭包指向同一个 `$i`:

$fns = [];
for ($i = 0; $i < 3; $i++) {
    $fns[] = function () use (&$i) { return $i; };
}
echo $fns[0](); // 3 —— 循环结束后 $i 已经是 3

`foreach` 更隐蔽:`foreach ($items as &$v)` 是引用遍历,循环后 `$v` 仍指向最后一个元素,忘记 `unset($v)` 再用 `$v` 做别的事就会串味。要"冻结当前值"就按值捕获,要共享累加状态才用 `&`,并且心里清楚它共享的是同一个变量。

三、use 是定义时的快照,而且只能捕获变量

结论:`use` 列表在闭包创建的那一刻求值,之后再改外层变量不会同步进来;同时 `use` 只能写变量名,不能写表达式,也不建议用来捕获超全局。

$prefix = 'v1';
$make = function () use ($prefix) { return $prefix; };
$prefix = 'v2';
echo $make(); // v1,不是 v2

想读"最新值",要么用 `use (&$prefix)`,要么把值当参数传进去——后者可读性更好:

$make = function ($prefix) { return $prefix; };
echo $make($prefix);

另外两点:`use ($_GET['x'])` 是语法错误,`use ($_GET)` 虽然合法但没必要,超全局在函数体内直接可读;`use ($undefinedVar)` 会触发未定义变量告警并捕获到 `null`,用引用捕获则会凭空造出一个变量,属于典型的隐蔽 bug。

四、箭头函数和 $this:不用 use,但也换不来引用

结论:PHP 7.4 的箭头函数 `fn() => ...` 自动按值捕获外层变量,不能写 `use`,也无法回写外部变量;类方法内的普通闭包自动绑定 `$this`,`static` 闭包不绑定。

$factor = 3;
$mul = fn($x) => $x * $factor; // 自动捕获,无需 use

箭头函数只能写单个表达式,一旦需要多语句或需要修改外部变量,就得退回 `function () use (&$x) {}`。

关于 `$this`:在类里定义的闭包自动带着 `$this`,既不用也不能 `use ($this)`;`static function () {}` 不绑定 `$this`,更安全也更省一点开销。需要换绑上下文时用 `Closure::bind()`、`bindTo()` 或 `call()`。像 Clara BBS 这类无框架、PHP 7.4-8.5 环境、靠运行时插件钩子回调的 PHP 系统,钩子回调几乎都是闭包,上面这几点直接决定计数、累加、批量处理的结果对不对。

一句话收束:`use` 是按值复制、定义时快照、只认变量名,要共享和回写必须显式 `&`;循环里优先按值捕获或传参,类里记得区分普通闭包与 `static` 闭包的 `$this` 差异,踩坑基本就绕开了。

本文转载自 Clara轻量论坛系统,原文地址:https://www.leleweb.cn/thread-341.html
转载请注明出处,版权归原作者所有。

全部回复 0

还没有回复,来抢沙发~