在 PHP 中,什么是闭包,为什么它使用“use”标识符?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1065188/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
In PHP, what is a closure and why does it use the "use" identifier?
提问by SeanDowney
I'm checking out some PHP 5.3.0features and ran across some code on the site that looks quite funny:
我正在检查一些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);
}
as one of the examples on anonymous functions.
作为匿名函数的例子之一。
Does anybody know about this? Any documentation? And it looks evil, should it ever be used?
有人知道这件事吗?有什么文件吗?它看起来很邪恶,应该使用它吗?
采纳答案by Andrew Hare
This is how PHP expresses a closure. This is not evil at all and in fact it is quite powerful and useful.
这就是 PHP 表达闭包的方式。这根本不是邪恶的,事实上它非常强大和有用。
Basically what this means is that you are allowing the anonymous function to "capture" local variables (in this case, $taxand a reference to $total) outside of it scope and preserve their values (or in the case of $totalthe reference to $totalitself) as state within the anonymous function itself.
基本上这意味着您允许匿名函数在其范围之外“捕获”局部变量(在这种情况下,$tax以及对 的引用$total)并将它们的值(或在$total引用$total自身的情况下)作为状态匿名函数本身。
回答by zupa
A simpler answer.
一个更简单的答案。
function ($quantity) use ($tax, &$total) { .. };
function ($quantity) use ($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 usekeyword:
- useallows you to access (use) the succeeding variables inside the closure.
- useis early binding. That means the variable values are COPIED upon DEFINING the closure. So modifying
$taxinside 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$totalDOES 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.
- 闭包是分配给变量的函数,因此您可以传递它
- 一个闭包是一个独立的命名空间,通常,你不能访问在这个命名空间之外定义的变量。来了use关键字:
- use允许您访问(使用)闭包内的后续变量。
- 使用是早期绑定。这意味着在定义闭包时复制变量值。所以
$tax在闭包内部修改没有外部影响,除非它是一个指针,就像一个对象一样。 - 您可以像在
&$total. 这样,修改$totalDOES的值会产生外部影响,原始变量的值会发生变化。 - 闭包内部定义的变量也不能从闭包外部访问。
- 闭包和函数具有相同的速度。是的,您可以在所有脚本中使用它们。
As @Mytskine pointed outprobably the best in-depth explanation is the RFC for closures. (Upvote him for this.)
回答by Steely Wing
The function () use () {}is like closure for PHP.
这function () use () {}就像 PHP 的闭包。
Without use, function cannot access parent scope variable
没有use,函数无法访问父作用域变量
$s = "hello";
$f = function () {
echo $s;
};
$f(); // Notice: Undefined variable: s
$s = "hello";
$f = function () use ($s) {
echo $s;
};
$f(); // hello
The usevariable's value is from when the function is defined, not when called
该use变量的值是在函数定义的时候,不叫的时候
$s = "hello";
$f = function () use ($s) {
echo $s;
};
$s = "how are you?";
$f(); // hello
usevariable by-reference with &
use变量引用 &
$s = "hello";
$f = function () use (&$s) {
echo $s;
};
$s = "how are you?";
$f(); // how are you?
回答by stefs
closures are beautiful! they solve a lot of problems that come with anonymous functions, and make really elegant code possible (at least as long as we talk about php).
闭包很漂亮!它们解决了匿名函数带来的许多问题,并使真正优雅的代码成为可能(至少在我们谈论 php 的时候)。
javascript programmers use closures all the time, sometimes even without knowing it, because bound variables aren't explicitly defined - that's what "use" is for in php.
JavaScript 程序员一直在使用闭包,有时甚至不知道它,因为绑定变量没有明确定义 - 这就是 php 中的“use”。
there are better real-world examples than the above one. lets say you have to sort an multidimensional array by a sub-value, but the key changes.
有比上面的例子更好的现实世界的例子。假设您必须按子值对多维数组进行排序,但键会发生变化。
<?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);
?>
warning: untested code (i don't have php5.3 installed atm), but it should look like something like that.
警告:未经测试的代码(我没有安装 php5.3 atm),但它应该看起来像那样。
there's one downside: a lot of php developers may be a bit helpless if you confront them with closures.
有一个缺点:如果你面对闭包,很多 php 开发人员可能会有点无助。
to understand the nice-ty of closures more, i'll give you another example - this time in javascript. one of the problems is the scoping and the browser inherent asynchronity. especially, if it comes to window.setTimeout();(or -interval). so, you pass a function to setTimeout, but you can't really give any parameters, because providing parameters executes the code!
为了更多地理解闭包的好处,我会给你另一个例子——这次是在 javascript 中。问题之一是范围和浏览器固有的异步性。特别是,如果涉及到window.setTimeout();(或 -interval)。所以,你传递了一个函数给 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);
myFunction returns a function with a kind-of predefined parameter!
myFunction 返回一个带有某种预定义参数的函数!
to be honest, i like php a lot more since 5.3 and anonymous functions/closures. namespaces may be more important, but they're a lot less sexy.
老实说,自 5.3 和匿名函数/闭包以来,我更喜欢 php。命名空间可能更重要,但它们不那么性感。
回答by joronimo
Zupa did a great job explaining closures with 'use' and the difference between EarlyBinding and Referencing the variables that are 'used'.
Zupa 很好地解释了“使用”的闭包以及 EarlyBinding 和引用“使用”的变量之间的区别。
So I made a code example with early binding of a variable (= copying):
所以我做了一个早期绑定变量(=复制)的代码示例:
<?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
*/
?>
Example with referencing a variable (notice the '&' character before variable);
引用变量的示例(注意变量前的“&”字符);
<?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
*/
?>
回答by Zhu Jinxuan
Until very recent years, PHP has defined its AST and PHP interpreter has isolated the parser from the evaluation part. During the time when the closure is introduced, PHP's parser is highly coupled with the evaluation.
直到最近几年,PHP 才定义了它的 AST,而 PHP 解释器将解析器与评估部分隔离开来。在引入闭包期间,PHP 的解析器与求值高度耦合。
Therefore when the closure was firstly introduced to PHP, the interpreter has no method to know which which variables will be used in the closure, because it is not parsed yet. So user has to pleased the zend engine by explicit import, doing the homework that zend should do.
因此,当闭包第一次被引入 PHP 时,解释器没有办法知道哪些变量将在闭包中使用,因为它还没有被解析。所以用户必须通过显式导入来取悦 zend 引擎,做 zend 应该做的功课。
This is the so-called simple way in PHP.
这就是PHP中所谓的简单方式。

