【问题标题】:PHP closure in other closure : scope of "use"其他闭包中的 PHP 闭包:“使用”范围
【发布时间】:2015-09-27 18:19:11
【问题描述】:

我的代码如下所示:

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

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

  • 任何响应都会有自己的请求对象,因为闭包 B 会为每个传递给$res->on 的新侦听器重新创建。因此,有许多闭包 B 的作用域从闭包 A 的已用变量继承一次。
  • 或者:任何新请求都将替换闭包 A 中的 $req$res(正常行为...)但也将替换先前创建的闭包 B 使用的 $req。如果在请求 #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();

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

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

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

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

【问题讨论】:

    标签: php closures


    【解决方案1】:

    在内部,PHP 中的每个Closure 对象都包含一个哈希表。此表存储使用 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
    

    更改$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
    

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

    【讨论】:

    • 我不知道为什么我从来没有接受过这个答案,但它在当时对理解 PHP 的内部原理很有帮助。谢谢=)
    猜你喜欢
    • 2011-09-26
    • 2011-10-07
    • 2014-11-11
    • 1970-01-01
    • 1970-01-01
    • 2016-03-07
    • 1970-01-01
    • 2014-11-15
    • 2012-11-03
    相关资源
    最近更新 更多