其他闭包中的 PHP 闭包:“使用”的范围

Mor*_*ing 4 php closures

我有一个看起来像这样的代码:

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

如您所见,我在闭包中有一个闭包。Closure B 正在接收$res来自事件的 ponse,所以它没有问题。但它也use荷兰国际集团$req从封闭A. uest在这里,我有一个关于的作用域怀疑use“d变量,我看到两种可能性:

  • 任何响应都将拥有自己的请求对象,因为封闭B是重新创建的每一个新的听众传递给$res->on。所以有很多闭包 B 都有自己的作用域,它们从闭包 A 的 used 变量中继承了一次。
  • 或者:任何新请求都将替换$req$res在闭包 A 中(正常行为......)但也将替换$req先前创建的闭包 B 使用的。如果在请求 #2 到达之前没有响应请求 #1,这将是有问题的(这是基于事件循环的异步代码)。

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


编辑:我尝试了一个理论上做同样事情的代码,但它更容易测试:

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

$fn1 = $a((object) ['val' => 1]);
$fn2 = $a((object) ['val' => 2]);
$fn2();
$fn1();
Run Code Online (Sandbox Code Playgroud)

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

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

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

我对吗?任何PHP专家?

Tro*_*ski 5

在内部,ClosurePHP 中的每个对象都包含一个哈希表。该表存储了使用use关键字复制到闭包作用域中的值。PHP 中的数组也是使用哈希表实现的。

当您use在闭包中设置一组变量时,就好像您已经创建了一个包含正在使用的每个变量的数组。每个闭包都包含它自己唯一的值“数组”,这些值在创建时被初始化。与普通数组不同,闭包中使用的变量表不能修改。

$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
Run Code Online (Sandbox Code Playgroud)

改变的值$var1不会影响在价值$array[0],也不会改变的值$var1范围内$closure

当您在闭包中使用一个对象时,该对象可能会在闭包之外更改,并且这些更改将反映在闭包中。对象在闭包中使用时不会被克隆。但是,因为您不能修改变量本身,所以不能将变量更改为指向不同的对象。

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

$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
Run Code Online (Sandbox Code Playgroud)

当上面的闭包被创建时,$var1在闭包的值表中创建了一个对的引用,但只有 的值$var2被复制到表中。当的值$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
Run Code Online (Sandbox Code Playgroud)

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