2009-06-30 9 views
317

Estoy comprobando algunas características PHP 5.3.0 y corrió a través de un código en el sitio que se ve bastante divertido:En PHP 5.3.0, ¿cuál es el identificador de función "usar"?

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 uno de los ejemplos en anonymous functions.

¿Alguien sabe de esto? ¿Alguna documentación? Y parece malvado, ¿debería usarse alguna vez?

Respuesta

282

Así es como PHP expresa un closure. Esto no es malo en absoluto y, de hecho, es bastante poderoso y útil.

Básicamente lo que esto significa es que usted está permitiendo que la función anónima para "capturar" las variables locales (en este caso, $tax y una referencia a $total) fuera de ella alcance y preservar sus valores (o en el caso de $total la referencia al $total) como estado dentro de la función anónima.

+1

¿SOLAMENTE se usa para cierres? Gracias por su explicación, no sabía la diferencia entre la función anónima y un cierre – SeanDowney

+108

La palabra clave 'use' también se utiliza para [espacios de nombres de alias] (http://php.net/manual/en/language.namespaces.importing .php). Es sorprendente que, más de 3 años después del lanzamiento de PHP 5.3.0, la sintaxis 'function ... use' aún esté oficialmente indocumentada, lo que hace que los cierres sean una característica no documentada. El documento incluso [confunde las funciones anónimas y los cierres] (http://php.net/manual/en/functions.anonymous.php). La única documentación (beta y no oficial) sobre 'use()' que pude encontrar en php.net fue [RFC for closures] (https://wiki.php.net/rfc/closures). Así –

+1

[Cuando cierres de uso de función se implementó en PHP?] (Http://stackoverflow.com/questions/20411631/when-was-function-use-closures-implemented-in-php) supongo que entonces estaba en PHP 5.3 ? ¿Está documentado en el manual de PHP ahora de alguna manera? – rubo77

47

¡Los cierres son hermosos! resuelven muchos problemas que vienen con funciones anónimas y hacen posible un código realmente elegante (al menos mientras hablemos de php).

Los programadores de Javascript usan cierres todo el tiempo, a veces incluso sin saberlo, porque las variables vinculadas no están explícitamente definidas, eso es lo que "usa" es para php.

hay mejores ejemplos del mundo real que el anterior. digamos que tiene que ordenar una matriz multidimensional por un subvalor, pero la clave cambia.

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

advertencia: código no probado (no tengo PHP5.3 atm instalado), pero debe parecerse a algo así.

hay una desventaja: muchos desarrolladores de PHP pueden estar un poco indefensos si los enfrenta con cierres.

para entender la calidad de los cierres más, le daré otro ejemplo - esta vez en javascript. uno de los problemas es el alcance y la asincronía inherente del navegador. especialmente, si se trata de window.setTimeout(); (o -intervalo). por lo tanto, pasas una función a setTimeout, pero realmente no puedes dar ningún parámetro, ¡porque proporcionando parámetros ejecuta el 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 devuelve una función con un tipo de parámetro predefinido!

para ser honesto, me gusta mucho más php desde 5.3 y funciones anónimas/cierres. los espacios de nombres pueden ser más importantes, pero son mucho menos sexys.

+4

ohhhhhhhh, por lo que los usos se utiliza para pasar de variables extra * *, pensé que era una tarea divertida. ¡Gracias! – SeanDowney

+29

tenga cuidado. los parámetros se usan para pasar valores cuando la función es LLAMADA. los cierres se usan para "pasar" valores cuando la función está DEFINIDA. – stefs

+0

En Javascript, se puede usar [bind()] (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind) para especificar argumentos iniciales para las funciones - ver [ Funciones parcialmente aplicadas] (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind#Partially_applied_functions). –

374

Una respuesta más simple.

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

  1. El cierre es una función asignada a una variable, por lo que puede pasar alrededor
  2. Un cierre es un espacio de nombres separado, normalmente, no se puede acceder a las variables definidas fuera de este espacio de nombres.Llega la palabra clave uso:
  3. uso que permite el acceso (uso) las variables que le siguen en el interior del cierre.
  4. uso es vinculante. Eso significa que los valores de las variables se COPIARÁN al DEFINIR el cierre. Por lo tanto, modificar $tax dentro del cierre no tiene ningún efecto externo, a menos que sea un puntero, como lo es un objeto.
  5. Puede pasar en las variables como punteros, como en el caso de &$total. De esta manera, al modificar el valor de $total TIENE UN EFECTO EXTERNO, el valor de la variable original cambia.
  6. Las variables definidas en el interior del cierre no son accesibles desde fuera del cierre tampoco.
  7. Los cierres y las funciones tienen la misma velocidad. Sí, puedes usarlos en todos tus scripts.

Como @Mytskine pointed out probablemente la mejor explicación en profundidad es la RFC for closures. (Él Upvote para esto.)

+14

Gracias, la mejor respuesta es la tuya! – PHPst

+17

bueno que mencionó vinculación temprana – usoban

+3

La palabra clave as en la instrucción de uso me está dando un error de sintaxis en php 5.5: '$ closure = function ($ value) use ($ localVar as $ alias) {// stuff};' El error dado es: 'Parse: error de sintaxis, inesperado 'como' (T_AS), esperando ',' o ')' ' –

11

Zupa hizo un gran trabajo explicando cierres con 'uso' y la diferencia entre EarlyBinding y hacer referencia a las variables que se 'utilizan'.

Hice, pues, un ejemplo de código con el enlace anticipado de una variable (= copia):

<?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 
*/ 

?> 

ejemplo con referencia a una variable (nótese la '&' carácter antes 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 
*/ 

?> 
30

El function() use() {} es el cierre para PHP, debe utilizar use para incluir la variable de matriz 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"); 
+1

Gracias por la explicación mucho más simple. –

-1

El alcance de $tax y $total es dentro de la función getTotal(). Está llamando a la función de devolución de llamada dentro de ella. Por lo tanto, no es necesario llamar al use.

+2

En realidad, el alcance es diferente en la devolución de llamada: vea una demostración [aquí] (https://www.tehplayground.com/KyxlnmC2C8BCuva4). Tal vez esté pensando en un lenguaje como JavaScript, donde las funciones anónimas pueden acceder a las variables en el ámbito principal –

+0

BTW - Argumenté que esto no es una respuesta porque realmente no responde la pregunta (s) "_ ¿Alguien sabe de esto? ¿Alguna documentación? Y parece malvada, ¿alguna vez debería usarse? _ " –

Cuestiones relacionadas