在PHP中,什么是闭包,为什么它使用"use"标识符?

Sea*_*ney 387 php closures

我正在检查一些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);
}
Run Code Online (Sandbox Code Playgroud)

作为匿名函数的一个例子.

有人知道吗?有文件吗?如果它被使用它看起来很邪恶?

zup*_*upa 456

一个更简单的答案.

function ($quantity) use ($tax, &$total) { .. };

  1. 闭包是一个赋给变量的函数,所以你可以传递它
  2. 闭包是一个单独的命名空间,通常,您无法访问在此命名空间之外定义的变量.有自带的使用关键字:
  3. use允许您访问(使用)闭包内的后续变量.
  4. 使用是早期绑定.这意味着在定义闭包时变量值是COPIED.因此$tax,在闭包内部进行修改没有外部效果,除非它是一个指针,就像对象一样.
  5. 您可以将变量作为指针传递给像&$total.这样,修改$totalDOES 的值有外部效果,原始变量的值会发生变化.
  6. 封闭件内部定义的变量也无法从封闭件外部进行访问.
  7. 闭包和功能具有相同的速度.是的,您可以在脚本上使用它们.

正如@Mytskine 指出的那样,关于闭包RFC可能是最好的深入解释.(为此赞成他.)

  • use语句中的as关键字在php 5.5中给出了一个语法错误:`$ closure = function($ value)use($ localVar as $ alias){// stuff};`给出的错误是:`Parse:syntax error ,意外'为'(T_AS),期待','或')'` (4认同)
  • 我会以这种方式添加到第5点,修改像`&$ total`这样的指针的值也有内部效果.换句话说,如果在定义*之后更改闭包**之外的`$ total`*的值,则只有新指针是指针才会传入. (4认同)
  • @ AndyD273的目的确实非常相似,只是`global`仅允许访问全局名称空间,而`use`允许访问父名称空间中的变量。全局变量通常被认为是邪恶的。访问父作用域通常是创建闭包的目的。它不是“邪恶的”,因为它的范围非常有限。诸如JS之类的其他语言隐式_use_父作用域的变量(作为指针,而不是复制的值)。 (2认同)
  • 这条线停止了我两个小时的徒劳搜索`你可以像在 &$total 的情况下一样将变量作为指针传递。这样,修改 $total 的值确实有外部影响,原始变量的值发生了变化。` (2认同)

And*_*are 346

这就是PHP表达闭包的方式.这根本不是邪恶的,事实上它非常强大和有用.

基本上这意味着您允许匿名函数在其范围之外"捕获"局部变量(在本例中$tax为引用$total)并保留它们的值(或者在$total引用$total自身的情况下)作为状态匿名函数本身.

  • `use`关键字也用于[别名命名空间](http://php.net/manual/en/language.namespaces.importing.php).令人惊讶的是,在PHP 5.3.0发布3年多之后,语法`function ... use`仍然是官方未记录的,这使得闭包成为一种无证的功能.doc甚至[混淆匿名函数和闭包](http://php.net/manual/en/functions.anonymous.php).关于`use()`的唯一(beta和非官方)文档我可以在php.net上找到[RFC for closure](https://wiki.php.net/rfc/closures). (129认同)
  • 那么[函数是否在PHP中实现了闭包?](http://stackoverflow.com/questions/20411631/when-was-function-use-closures-implemented-in-php)我猜那时它是在PHP 5.3中?现在以某种方式记录在PHP手册中吗? (2认同)

Ste*_*ing 52

function () use () {}就是关闭了PHP,你必须使用use包括父母的变量use.

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

$f(); // Notice: Undefined variable: s
Run Code Online (Sandbox Code Playgroud)

  • 感谢您的简单得多的解释。 (6认同)
  • 认真地。我什至不认为php手册可以解释这一点。 (2认同)
  • 读完这篇文章后,我不后悔滚动了一下,但是猜测第三行中的错别字需要稍作修改。应该有$ s而不是$ obj。 (2认同)

ste*_*efs 51

封闭是美丽的!他们解决了许多匿名函数带来的问题,并且可以使代码变得非常优雅(至少只要我们谈论php).

javascript程序员一直使用闭包,有时甚至不知道它,因为绑定变量没有明确定义 - 这就是php中的"use".

有比上面更好的现实世界的例子.假设您必须按子值对多维数组进行排序,但键会发生变化.

<?php
    function generateComparisonFunctionForKey($key) {
        return function ($left, $right) use ($key) {
            if ($left[$key] == $right[$key])
                return 0;
            else
                return ($left[$key] < $right[$key]) ? -1 : 1;
        };
    }

    $myArray = array(
        array('name' => 'Alex', 'age' => 70),
        array('name' => 'Enrico', 'age' => 25)
    );

    $sortByName = generateComparisonFunctionForKey('name');
    $sortByAge  = generateComparisonFunctionForKey('age');

    usort($myArray, $sortByName);

    usort($myArray, $sortByAge);
?>
Run Code Online (Sandbox Code Playgroud)

警告:未经测试的代码(我没有安装php5.3 atm),但它看起来应该是这样的.

有一个缺点:如果你用闭包来对付它们,很多php开发人员可能会有点无助.

为了更好地理解闭包的好处,我会给你另一个例子 - 这次是在javascript中.其中一个问题是范围和浏览器固有的异步性.特别是,如果涉及window.setTimeout();(或 - 间隔).所以,你将一个函数传递给setTimeout,但是你不能真正给出任何参数,因为提供参数会执行代码!

function getFunctionTextInASecond(value) {
    return function () {
        document.getElementsByName('body')[0].innerHTML = value; // "value" is the bound variable!
    }
}

var textToDisplay = prompt('text to show in a second', 'foo bar');

// this returns a function that sets the bodys innerHTML to the prompted value
var myFunction = getFunctionTextInASecond(textToDisplay);

window.setTimeout(myFunction, 1000);
Run Code Online (Sandbox Code Playgroud)

myFunction返回一个带有一种预定义参数的函数!

说实话,自从5.3和匿名函数/闭包以来我更喜欢php.命名空间可能更重要,但它们的性感要差得多.

  • 小心.函数CALLED时,参数用于传递值.闭包用于在函数DEFINED时"传递"值. (37认同)
  • ohhhhhhhh,所以Uses用于传入*extra*变量,我认为这是一些有趣的任务.谢谢! (4认同)

jor*_*imo 16

Zupa很好地解释了使用"使用"的闭包以及EarlyBinding和引用"使用"变量之间的区别.

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

<?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
*/

?>
Run Code Online (Sandbox Code Playgroud)

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

<?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
*/

?>
Run Code Online (Sandbox Code Playgroud)