我正在检查一些PHP 5.3.0的特性,在网站上看到了一些看起来很有趣的代码:

public function getTotal($tax)
{
    $total = 0.00;

    $callback =
        /* This line here: */
        function ($quantity, $product) use ($tax, &$total)
        {
            $pricePerItem = constant(__CLASS__ . "::PRICE_" .
                strtoupper($product));
            $total += ($pricePerItem * $quantity) * ($tax + 1.0);
        };

    array_walk($this->products, $callback);
    return round($total, 2);
}

作为匿名函数的例子之一。

有人知道吗?文档吗?它看起来很邪恶,应该被使用吗?


当前回答

直到最近几年,PHP已经定义了它的AST, PHP解释器已经将解析器与求值部分隔离开来。在引入闭包期间,PHP解析器与计算高度耦合。

因此,当闭包第一次引入PHP时,解释器没有方法知道闭包中将使用哪些变量,因为它还没有被解析。所以用户必须通过显式导入zend引擎,做zend应该做的功课。

这就是PHP中所谓的简单方法。

其他回答

函数()use(){}类似于PHP的闭包。

如果不使用,函数不能访问父作用域变量

$s = "hello";
$f = function () {
    echo $s;
};

$f(); // Notice: Undefined variable: s
$s = "hello";
$f = function () use ($s) {
    echo $s;
};

$f(); // hello

use变量的值是函数定义时的值,而不是调用时的值

$s = "hello";
$f = function () use ($s) {
    echo $s;
};

$s = "how are you?";
$f(); // hello

使用变量引用&

$s = "hello";
$f = function () use (&$s) {
    echo $s;
};

$s = "how are you?";
$f(); // how are you?

直到最近几年,PHP已经定义了它的AST, PHP解释器已经将解析器与求值部分隔离开来。在引入闭包期间,PHP解析器与计算高度耦合。

因此,当闭包第一次引入PHP时,解释器没有方法知道闭包中将使用哪些变量,因为它还没有被解析。所以用户必须通过显式导入zend引擎,做zend应该做的功课。

这就是PHP中所谓的简单方法。

一个更简单的答案。

功能($quantity)使用($tax, &$total) {..};

The closure is a function assigned to a variable, so you can pass it around A closure is a separate namespace, normally, you can not access variables defined outside of this namespace. There comes the use keyword: use allows you to access (use) the succeeding variables inside the closure. use is early binding. That means the variable values are COPIED upon DEFINING the closure. So modifying $tax inside the closure has no external effect, unless it is a pointer, like an object is. You can pass in variables as pointers like in case of &$total. This way, modifying the value of $total DOES HAVE an external effect, the original variable's value changes. Variables defined inside the closure are not accessible from outside the closure either. Closures and functions have the same speed. Yes, you can use them all over your scripts.

正如@Mytskine指出的,可能最好的深入解释是闭包的RFC。(给他点赞。)

这就是PHP表达闭包的方式。这一点都不邪恶,事实上它是相当强大和有用的。

基本上,这意味着您允许匿名函数在其作用域之外“捕获”局部变量(在本例中为$tax和对$total的引用),并将它们的值(或在$total的情况下为对$total本身的引用)保存为匿名函数本身的状态。

Zupa很好地解释了带“use”的闭包,以及EarlyBinding和引用“used”变量之间的区别。

所以我做了一个早期绑定变量(=复制)的代码示例:

<?php

$a = 1;
$b = 2;

$closureExampleEarlyBinding = function() use ($a, $b){
    $a++;
    $b++;
    echo "Inside \$closureExampleEarlyBinding() \$a = ".$a."<br />";
    echo "Inside \$closureExampleEarlyBinding() \$b = ".$b."<br />";    
};

echo "Before executing \$closureExampleEarlyBinding() \$a = ".$a."<br />";
echo "Before executing \$closureExampleEarlyBinding() \$b = ".$b."<br />";  

$closureExampleEarlyBinding();

echo "After executing \$closureExampleEarlyBinding() \$a = ".$a."<br />";
echo "After executing \$closureExampleEarlyBinding() \$b = ".$b."<br />";

/* this will output:
Before executing $closureExampleEarlyBinding() $a = 1
Before executing $closureExampleEarlyBinding() $b = 2
Inside $closureExampleEarlyBinding() $a = 2
Inside $closureExampleEarlyBinding() $b = 3
After executing $closureExampleEarlyBinding() $a = 1
After executing $closureExampleEarlyBinding() $b = 2
*/

?>

引用变量的示例(注意变量前面的'&'字符);

<?php

$a = 1;
$b = 2;

$closureExampleReferencing = function() use (&$a, &$b){
    $a++;
    $b++;
    echo "Inside \$closureExampleReferencing() \$a = ".$a."<br />";
    echo "Inside \$closureExampleReferencing() \$b = ".$b."<br />"; 
};

echo "Before executing \$closureExampleReferencing() \$a = ".$a."<br />";
echo "Before executing \$closureExampleReferencing() \$b = ".$b."<br />";   

$closureExampleReferencing();

echo "After executing \$closureExampleReferencing() \$a = ".$a."<br />";
echo "After executing \$closureExampleReferencing() \$b = ".$b."<br />";    

/* this will output:
Before executing $closureExampleReferencing() $a = 1
Before executing $closureExampleReferencing() $b = 2
Inside $closureExampleReferencing() $a = 2
Inside $closureExampleReferencing() $b = 3
After executing $closureExampleReferencing() $a = 2
After executing $closureExampleReferencing() $b = 3
*/

?>