В PHP, что такое закрытие и почему он использует идентификатор "use"?
Я просматриваю некоторые функции 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);
}
как один из примеров анонимных функций.
Кто-нибудь знает об этом? Любая документация? И это выглядит злым, если его когда-нибудь использовать?
Ответы
Ответ 1
Так PHP выражает closure. Это вовсе не зло, а на самом деле оно достаточно мощное и полезное.
В основном это означает, что вы разрешаете анонимной функции "захватывать" локальные переменные (в данном случае, $tax
и ссылку на $total
) за пределами области видимости и сохранять их значения (или в случае of $total
ссылка на $total
) как состояние внутри самой анонимной функции.
Ответ 2
Простой ответ.
function ($quantity) use ($tax, &$total) { .. };
- Закрытие - это функция, назначенная переменной, поэтому вы можете передать ее
- Закрытие представляет собой отдельное пространство имен, обычно вы не можете обращаться к переменным, определенным за пределами этого пространства имен. Приходит ключевое слово use:
- использовать позволяет вам использовать (использовать) следующие переменные внутри закрытия.
- использовать - раннее связывание. Это означает, что значения переменных COPIED устанавливаются при определении закрытия. Поэтому модификация
$tax
внутри замыкания не имеет внешнего эффекта, если только это не указатель, как объект.
- Вы можете передавать переменные как указатели, например, в случае
&$total
. Таким образом, изменение значения $total
имеет внешний эффект, изменяется исходное значение переменной.
- Переменные, определенные внутри закрытия, также недоступны из-за закрытия.
- Замыкания и функции имеют одинаковую скорость. Да, вы можете использовать их во всех своих сценариях.
Как отметил @Mytskine , лучшим лучшим объяснением является RFC для закрытий. (Поднимите его для этого.)
Ответ 3
закрытия прекрасны! они решают множество проблем, связанных с анонимными функциями, и делают действительно элегантный код возможным (по крайней мере, пока мы говорим о php).
javascript-программисты все время используют замыкания, иногда даже не зная об этом, потому что связанные переменные явно не определены - то, что используется в php.
есть лучшие примеры реального мира, чем приведенные выше. скажем, вам нужно отсортировать многомерный массив по суб-значению, но ключ изменяется.
<?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. Одной из проблем является область охвата и присущая браузеру асинхронность. особенно, если речь идет о 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 возвращает функцию с определенным предопределенным параметром!
Честно говоря, мне нравится php намного больше с 5.3 и анонимных функций/закрытий. пространства имен могут быть более важными, но они намного менее сексуальны.
Ответ 4
function () use () {}
- это как закрытие для PHP.
Без use
функция не может получить доступ к родительской переменной области видимости
$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
переменная по ссылке use
с &
$s = "hello";
$f = function () use (&$s) {
echo $s;
};
$s = "how are you?";
$f(); // how are you?
Ответ 5
Zupa проделал отличную работу, объясняя закрытие с помощью "use" и разницу между 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
*/
?>
Пример с ссылкой на переменную (обратите внимание на символ "&" перед переменной);
<?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
*/
?>
Ответ 6
До самых последних лет PHP определял свой AST, а интерпретатор PHP изолировал анализатор от части оценки. В то время, когда вводится замыкание, синтаксический анализатор PHP тесно связан с оценкой.
Поэтому, когда замыкание было впервые введено в PHP, у интерпретатора нет способа узнать, какие переменные будут использоваться в замыкании, потому что оно еще не проанализировано. Таким образом, пользователь должен порадовать движок Zend явным импортом, выполняя домашнюю работу, которую должен сделать Zend.
Это так называемый простой способ в PHP.
Ответ 7
Объем $tax
и $total
находится внутри функции getTotal(). Вы вызываете функцию обратного вызова внутри него. Поэтому нет необходимости называть use
.