PHP 其他闭包中的闭包:范围 "use"

PHP closure in other closure : scope of "use"

我的代码如下所示:

$app->add(function($req, $res, $next) {
    # closure A
    $res->on('end', function($res) use ($req) {
        # closure B
    });
    $next();
});

如你所见,我有一个闭包中的一个闭包。闭包 B 正在从事件中接收 $res 响应,因此没有问题。但它也是来自闭包 A 的 useing $request。 在这里,我对 use'd 变量的范围有疑问,我看到两种可能性:

希望我说得够清楚了。我问这个是因为,例如,在 JavaScript 中,我们有时必须使用回调生成器来确保不替换子闭包的范围。


编辑: 我尝试使用理论上做同样事情的代码,但更容易测试:

$a = function($var) {
    return function() use ($var) {
        var_dump($var);
    };
};

$fn1 = $a((object) ['val' => 1]);
$fn2 = $a((object) ['val' => 2]);
$fn2();
$fn1();

输出 (2, 1) 显示第一个函数 $fn1 保持其原始作用域。另外,我注意到 Closure 对象上 var_dump 的输出显示了它带来的范围:

object(Closure)#3 (1) {
  ["static"]=>
  array(1) {
    ["res"]=>
    object(stdClass)#2 (1) {
      ["val"]=>
      int(1)
    }
  }
}

技术说明?我认为这是因为 PHP 中的闭包是常规 PHP 对象,其中 use 是一种构造函数。

我说得对吗?有 PHP 专家吗?

在内部,PHP 中的每个 Closure 对象都包含一个散列 table。 table 存储使用 use 关键字复制到闭包作用域中的值。 PHP 中的数组也使用散列 table.

实现

当您 use 闭包中的一组变量时,就好像您已经创建了一个包含每个正在使用的变量的数组。每个闭包都包含它自己独特的 "array" 值,这些值在创建时被初始化。与普通数组不同,无法修改闭包中使用的变量 table。

$var1 = 1;
$var2 = 2;

$closure = function () use ($var1, $var2) {
    return $var1 . ", " . $var2 . "\n";
};

$array = [$var1, $var2];

$var1 = 3;
$var2 = 4;

echo $closure(); // echoes 1, 2
echo $array[0] . ", " . $array[1] . "\n"; // echoes 1, 2

更改 $var1 的值不会影响 $array[0] 处的值,也不会更改 $closure.

$var1 的值

当您在闭包中使用对象时,该对象可能会在闭包之外发生更改,并且这些更改将反映在闭包中。在闭包中使用时不会克隆对象。但是,由于您无法修改变量本身,因此无法更改变量以指向不同的对象。

变量也可以通过引用在闭包中使用。这允许在闭包之外修改变量值,并且这些更改反映在闭包本身内。

$var1 = 1;
$var2 = 2;

$closure = function () use (&$var1, $var2) {
    return $var1 . ", " . $var2 . "\n";
};

$array = [&$var1, $var2];

$var1 = 3;
$var2 = 4;

echo $closure(); // echoes 3, 2
echo $array[0] . ", " . $array[1] . "\n"; // echoes 3, 2

创建上面的闭包时,会在闭包的 table 值中创建对 $var1 的引用,但只有 $var2 的值被复制到 [=51] =].当 $var1$var2 的值发生变化时,只有 $var1 的值在闭包内发生变化,因为只有那个变量被引用使用。这又类似于创建一个数组,其中 $var1 通过引用添加到数组,但 $var2 的值被复制到数组。

在闭包内创建闭包时,内部闭包会在创建闭包时复制变量的值。它是在另一个闭包中创建的并不重要。

$value = 1;

$closure = function ($arg) use ($value) {
    return function () use ($arg, $value) {
        return $value + $arg;
    };
};

$value = 10;

$callback1 = $closure(1);
$callback2 = $closure(2);

echo $callback1() . "\n"; // Echoes 2
echo $callback2() . "\n"; // Echoes 3

TL;DR: 变量的值在创建闭包时被复制到闭包中。为了能够在闭包之外修改值,该值必须通过引用使用(例如,function () use (&$value) { ... })。