In PHP, what is a closure and why does it use the “use” identifier?
我正在检查一些
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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); } |
作为匿名函数的一个例子。
有人知道吗?有文件吗?它看起来很邪恶,应该用过吗?
一个简单的答案。
正如@mytskine指出的,最好的深入解释可能是闭包的RFC。(向他投赞成票。)
这就是PHP表示闭包的方式。这一点也不邪恶,事实上,它是非常强大和有用的。
基本上,这意味着您允许匿名函数"捕获"它范围之外的局部变量(在本例中,是
封口很漂亮!它们解决了匿名函数带来的许多问题,并使真正优雅的代码成为可能(至少在我们讨论PHP的时候)。
JavaScript程序员总是使用闭包,有时甚至不知道闭包,因为绑定变量没有被明确定义——这就是"use"在PHP中的用途。
有比上面的例子更好的真实世界的例子。假设必须按子值对多维数组进行排序,但键会更改。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | <?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); ?> |
警告:未测试的代码(我没有安装php5.3 atm),但它看起来应该是这样的。
有一个缺点:如果你用闭包来对付他们,很多PHP开发人员可能会有点束手无策。
为了更好地理解闭包的好处,我将给出另一个例子——这次是在JavaScript中。其中一个问题是范围界定和浏览器固有的异步性。尤其是当涉及到
1 2 3 4 5 6 7 8 9 10 11 12 | 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返回一个带有预定义参数的函数!
老实说,自从5.3和匿名函数/闭包以来,我更喜欢PHP。名称空间可能更重要,但它们不那么性感。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | <?php $message ="hello "; $example = function () { echo $message; }; // Notice: Undefined variable: message $example(); $example = function () use ($message) { echo $message; }; //"hello" $example(); // Inherited variable's value is from when the function is defined, not when called $message ="world "; //"hello" $example(); // Inherit by-reference $message ="hello "; $example = function () use (&$message) { echo $message; }; //"hello" $example(); // The changed value in the parent scope is reflected inside the function call $message ="world "; //"world" $example(); // Closures can also accept regular arguments $example = function ($arg) use ($message) { echo $arg . ' ' . $message; }; //"hello world" $example("hello"); |
zupa用"use"解释了闭包,以及早期绑定和引用"used"变量之间的区别。
因此,我做了一个代码示例,其中早期绑定了一个变量(=copying):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | <?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 */ ?> |
引用变量的示例(注意变量前的"&;"字符);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | <?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 */ ?> |