No PHP, o que é um fechamento e por que ele usa o identificador “use”?

Estou verificando alguns resources do PHP 5.3.0 e encontrei um código no site que parece bastante engraçado:

 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); } 

como um dos exemplos em funções anônimas .

Alguém sabe sobre isso? Alguma documentação? E parece mal, deveria alguma vez ser usado?

É assim que o PHP expressa um fechamento . Isso não é mal e, de fato, é bastante poderoso e útil.

Basicamente, isso significa que você está permitindo que a function anônima “capture” variables ​​locais (nesse caso, $tax e uma referência a $total ) fora de seu escopo e preserve seus valores (ou, no caso de $total a referência para $total si) como estado dentro da própria function anônima.

Uma resposta mais simples.

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

  1. O fechamento é uma function atribuída a uma variável, então você pode passá-la
  2. Um encerramento é um espaço de nomes separado, normalmente, você não pode acessar variables ​​definidas fora desse namespace. Lá vem a palavra-chave use :
  3. Use permite que você acesse (use) as variables ​​subsequentes dentro do fechamento.
  4. o uso é binding antecipada. Isso significa que os valores das variables ​​são COPIADOS ao DEFINIR o fechamento. Portanto, modificar $tax dentro do fechamento não tem efeito externo, a menos que seja um ponteiro, como um object é.
  5. Você pode passar variables ​​como pointers como no caso de &$total . Dessa forma, modificar o valor de $total NÃO tem um efeito externo, o valor da variável original é alterado.
  6. Variáveis ​​definidas dentro do fechamento também não são acessíveis de fora do fechamento.
  7. Fechamentos e funções têm a mesma velocidade. Sim, você pode usá-los em todos os seus scripts.

Como @Mytskine apontou, provavelmente, a melhor explicação detalhada é o RFC para fechamentos . (Vota-o por isso.)

encerramentos são lindos! eles resolvem muitos problemas que vêm com funções anônimas, e tornam o código realmente elegante possível (pelo menos enquanto falamos sobre o php).

programadores de javascript usam closures o tempo todo, as vezes até mesmo sem saber, porque variables ​​limitadas não são explicitamente definidas – é para isso que o “uso” é feito em php.

há melhores exemplos do mundo real do que o acima. digamos que você tenha que ordenar uma multidimensional array por um subvalor, mas a chave muda.

 < ?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); ?> 

aviso: código não testado (não tenho o php5.3 installed atm), mas deve parecer algo assim.

há uma desvantagem: muitos desenvolvedores de php podem ficar um pouco impotentes se você os confrontar com encerramentos.

Para entender melhor o encerramento, vou dar outro exemplo – desta vez em javascript. Um dos problemas é o escopo e a assincronia inerente ao navegador. especialmente, se se trata de window.setTimeout(); (ou -interval). Então, você passa uma function para setTimeout, mas você não pode realmente dar nenhum parâmetro, porque fornecer parâmetros executa o código!

 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 retorna uma function com um tipo de parâmetro predefinido!

Para ser honesto, eu gosto muito mais php desde 5.3 e funções anônimas / encerramentos. namespaces podem ser mais importantes, mas são muito menos sexy .

A function () use () {} é o encerramento para o PHP, você deve use para include a variável da function pai.

 < ?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"); 

Zupa fez um ótimo trabalho explicando closures com ‘use’ e a diferença entre EarlyBinding e Referencing as variables ​​que são ‘usadas’.

Então eu fiz um exemplo de código com binding antecipada de uma variável (= cópia):

 < ?php $a = 1; $b = 2; $closureExampleEarlyBinding = function() use ($a, $b){ $a++; $b++; echo "Inside \$closureExampleEarlyBinding() \$a = ".$a."
"; echo "Inside \$closureExampleEarlyBinding() \$b = ".$b."
"; }; echo "Before executing \$closureExampleEarlyBinding() \$a = ".$a."
"; echo "Before executing \$closureExampleEarlyBinding() \$b = ".$b."
"; $closureExampleEarlyBinding(); echo "After executing \$closureExampleEarlyBinding() \$a = ".$a."
"; echo "After executing \$closureExampleEarlyBinding() \$b = ".$b."
"; /* 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 */ ?>

Exemplo com referência a uma variável (observe o caractere ‘&’ antes da variável);

 < ?php $a = 1; $b = 2; $closureExampleReferencing = function() use (&$a, &$b){ $a++; $b++; echo "Inside \$closureExampleReferencing() \$a = ".$a."
"; echo "Inside \$closureExampleReferencing() \$b = ".$b."
"; }; echo "Before executing \$closureExampleReferencing() \$a = ".$a."
"; echo "Before executing \$closureExampleReferencing() \$b = ".$b."
"; $closureExampleReferencing(); echo "After executing \$closureExampleReferencing() \$a = ".$a."
"; echo "After executing \$closureExampleReferencing() \$b = ".$b."
"; /* 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 */ ?>