Je vérifie quelques-unes des fonctionnalités de PHP 5.3.0
et rencontre du code sur le site qui a l'air assez amusant:
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);
}
comme l'un des exemples sur fonctions anonymes .
Est-ce que quelqu'un sait à ce sujet? Toute documentation? Et ça a l'air mauvais, faut-il jamais l'utiliser?
C’est ainsi que PHP exprime un fermeture . Ce n'est pas mal du tout et en fait c'est assez puissant et utile.
En gros, cela signifie que vous autorisez la fonction anonyme à "capturer" des variables locales (dans ce cas, $tax
et une référence à $total
) en dehors de son étendue et à conserver leurs valeurs (ou cas de $total
la référence à $total
lui-même) en tant qu'état dans la fonction anonyme elle-même.
Une réponse plus simple.
function ($quantity) use ($tax, &$total) { .. };
$tax
à l'intérieur de la fermeture n'a aucun effet externe, à moins qu'il ne s'agisse d'un pointeur, comme le serait un objet.&$total
. Ainsi, si vous modifiez la valeur de $total
_ AVOIS UN effet externe, la valeur de la variable d'origine change.Comme @Mytskine a souligné , la meilleure explication détaillée est probablement le RFC pour les fermetures . (Upvote lui pour cela.)
les fermetures sont belles! ils résolvent beaucoup de problèmes liés aux fonctions anonymes et rendent le code vraiment élégant (du moins tant que nous parlons de php).
les programmeurs javascript utilisent des fermetures tout le temps, parfois même sans le savoir, car les variables liées ne sont pas explicitement définies - c'est à cela que sert "utilisation" en php.
il existe de meilleurs exemples du monde réel que celui ci-dessus. Disons que vous devez trier un tableau multidimensionnel par une sous-valeur, mais la clé change.
<?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);
?>
avertissement: code non testé (je n'ai pas installé atm php5.3), mais il devrait ressembler à quelque chose comme ça.
il y a un inconvénient: beaucoup de développeurs php peuvent être un peu impuissants si vous les confrontez à des fermetures.
pour mieux comprendre la gentillesse des fermetures, je vais vous donner un autre exemple - cette fois en javascript. L'un des problèmes est la portée et l'asynchronité inhérente au navigateur. surtout s'il s'agit de window.setTimeout();
(ou -interval). alors, vous passez une fonction à setTimeout, mais vous ne pouvez pas vraiment donner de paramètres, car fournir des paramètres exécute le code!
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 renvoie une fonction avec un type de paramètre prédéfini!
pour être honnête, j'aime beaucoup php depuis 5.3 et les fonctions/fermetures anonymes. Les espaces de noms peuvent être plus importants, mais ils sont beaucoup moins sexy.
La function () use () {}
est comme une fermeture pour PHP.
Sans use
, la fonction ne peut pas accéder à la variable de portée parent
$s = "hello";
$f = function () {
echo $s;
};
$f(); // Notice: Undefined variable: s
$s = "hello";
$f = function () use ($s) {
echo $s;
};
$f(); // hello
La valeur de la variable use
date de la définition de la fonction et non de son appel.
$s = "hello";
$f = function () use ($s) {
echo $s;
};
$obj = "how are you?";
$f(); // hello
use
variable par référence avec &
$s = "hello";
$f = function () use (&$s) {
echo $s;
};
$s = "how are you?";
$f(); // how are you?
Zupa a fait un excellent travail en expliquant les fermetures avec "utilisation" et la différence entre EarlyBinding et Referencing les variables "utilisées".
J'ai donc créé un exemple de code avec la liaison anticipée d'une variable (= copie):
<?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
*/
?>
Exemple de référencement d'une variable (notez le caractère '&' avant la 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
*/
?>
Jusque très récemment, PHP a défini son interprète AST et PHP a isolé l'analyseur de la partie évaluation. Au moment où la fermeture est introduite, l'analyseur syntaxique de PHP est fortement associé à l'évaluation.
Par conséquent, lorsque la fermeture a été introduite pour la première fois en PHP, l'interpréteur n'a aucune méthode pour savoir quelles variables seront utilisées dans la fermeture, car celle-ci n'est pas encore analysée. Ainsi, l'utilisateur doit satisfaire le moteur zend par une importation explicite, en faisant le devoir que zend devrait faire.
C'est la méthode dite simple en PHP.