Вопрос по php – Что такое замыкание в PHP и почему он использует идентификатор «использования»?
Я проверяю некоторые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);
}
в качестве одного из примеров наанонимные функции.
Кто-нибудь знает об этом? Любая документация? И это выглядит злым, стоит ли его когда-либо использовать?
Закрытие. Это вовсе не зло, и на самом деле это довольно сильно и полезно.
В основном это означает, что вы разрешаете анонимной функции «захватывать» локальные переменные (в данном случае,$tax
и ссылка на$total
) выходит за его рамки и сохраняет их значения (или в случае$total
ссылка на$total
себя) как состояние внутри самой анонимной функции.
use
ключевое слово также используется для Лизинг пространств имен. Удивительно, что через 3 года после выхода PHP 5.3.0 синтаксисfunction ... use
до сих пор официально недокументирован, что делает замыкания недокументированными. Документ даже вводит в заблуждение анонимные функции и замыкания. Единственная (бета и неофициальная) документация наuse ()
Я мог найти на php.net был RFC для закрытий.
user699082
function ($quantity) use ($tax, &$total) { .. };
$tax
внутри замыкания не имеет внешнего эффекта, если только он не является указателем, как объект. Вы можете передавать переменные в виде указателей, как в случае&$total
. Таким образом, изменив значение$total
ОСТАЕТСЯ ли внешний эффект, значение исходной переменной изменяется. Переменные, определенные внутри замыкания, также недоступны извне замыкания. Закрытия и функции имеют одинаковую скорость. Да, вы можете использовать их во всех сценариях.As @Mytskineуказа, вероятно, лучшее подробное объяснение - это RFC для закрытий. (Проголосуй за это.)
$closure = function ($value) use ($localVar as $alias) { //stuff};
Ошибка дана:Parse: syntax error, unexpected 'as' (T_AS), expecting ',' or ')'
Kal Zekdor
&$total
имеет и внутренний эффект. Другими словами, если вы измените значение$total
за пределам закрытияпосл определено, новое значение передается, только если это указатель.
billynoah
global
разрешает доступ только к глобальному пространству имен, тогда какuse
позволяет получить доступ к переменным в родительском пространстве имен. Глобальные переменные обычно считаются злыми. Доступ к родительской области часто является самой целью создания замыкания. Это не «зло», поскольку его возможности очень ограничены. Другие языки, такие как JS неявно Использование переменные родительской области видимости (как указатель, а не как скопированное значение).
zupa
связанных с анонимными функциями, и делают возможным действительно элегантный код (по крайней мере, пока мы говорим о 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);
?>
warning: непроверенный код (у меня не установлен 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);
myFunction возвращает функцию со своего рода предопределенным параметром!
Если честно, мне больше нравится php с 5.3 и анонимные функции / замыкания. пространства имен могут быть важнее, но они намного менее сексуальны.
Thefunction () use () {}
является закрытием для PHP, вы должны использоватьuse
включить переменную родителяfunction
.
<?php
$message = "hello\n";
$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\n";
// "hello"
$example();
// Inherit by-reference
$message = "hello\n";
$example = function () use (&$message) {
echo $message;
};
// "hello"
$example();
// The changed value in the parent scope is reflected inside the function call
$message = "world\n";
// "world"
$example();
// Closures can also accept regular arguments
$example = function ($arg) use ($message) {
echo $arg . ' ' . $message;
};
// "hello world"
$example("hello");
use' и разницы между EarlyBinding и Referencing для переменных, которые 'используются'.
Так что я сделал пример кода с ранним связыванием переменной (= копирование):
<?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
*/
?>