PHP闭包中的其他闭包:关于"use"作用域的问题

4

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

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

如您所见,我在一个闭包中嵌套了另一个闭包。闭包B从事件中接收$response,因此没有问题。但是它还使用了来自闭包A的$request。
这里,我对'use'变量的作用域有疑问,我看到两种可能性:
1. 任何响应都将具有其自己的请求对象,因为每个传递给$res->on的新侦听器都会重新创建闭包B。因此,有许多闭包B,它们从闭包A的used变量继承了自己的范围。
2. 或者:任何新请求都将替换闭包A中的$req和$res(正常行为……),但也将替换先前创建的闭包B使用的$req。如果请求#1在请求#2到达之前未得到回答(这是基于事件循环的异步代码),那将是有问题的。
我希望我表述清楚了。我之所以这样问,是因为例如在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保持了其原始作用域。此外,我注意到对闭包对象进行var_dump的输出显示它所带来的作用域:
object(Closure)#3 (1) {
  ["static"]=>
  array(1) {
    ["res"]=>
    object(stdClass)#2 (1) {
      ["val"]=>
      int(1)
    }
  }
}

技术解释?我认为这是因为PHP中的闭包是常规的PHP对象,其中use是一种类似于构造函数的东西。
上面的示例中,闭包的作用如下:http://pastebin.com/qkQ5GDFw 但不像这个例子,我强制PHP保持相同的引用,这是使用闭包的“构造函数”不可能实现的:http://pastebin.com/ixfVh2Uf 我说得对吗?有没有PHP专家?
1个回答

5
在PHP中,每个Closure对象都包含一个哈希表。该表存储使用use关键字复制到闭包作用域中的值。在PHP中,数组也是使用哈希表实现的。
当您在闭包中使用一组变量时,就好像创建了一个包含每个正在使用的变量的数组。每个闭包都包含自己独有的“数组”值,并在创建时初始化。与普通数组不同,闭包中使用的变量表无法修改。
$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

创建上面的闭包时,引用 $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

简述:创建闭包时,变量的值被复制到闭包中。若要在闭包外部修改该值,则必须使用引用(例如:function () use (&$value) { ... })。


我不知道为什么我从来没有接受过那个答案,但当时它帮了我很多,让我理解了PHP的内部机制。谢谢=) - Morgan Touverey Quilling

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接