Depuis PHP 5.3, les fonctions anonymes (instances de la classe Closure) sont l'un des éléments moteurs du langage.
On peut les utiliser comme fonction de callback, pour faire du lazy load, des "vraies" closures ou plus simplement pour exécuter un algorithme dont l'utilisation ne nécessite pas la création d'une fonction nommée dans le scope global.
Puisque cette fonction n'a pas de nom, comment faire pour l'appeler de façon récursive ?
Fonction anonyme récursive : ce qui ne fonctionne pas
$f = function($i = 3){ /* Attention aux boucles infinies */ if ($i == 0) { return; } echo $i; $f(--$i); }; $f(); // Notice: Undefined variable: f, Fatal error: Function name must be a string
Et oui ! Impossible pour la fonction de s'utiliser elle-même par le nom de la variable qui la référence car elle n'existe pas encore pendant sa propre définition.
Pour palier à ce problème nous allons utiliser le mot clé "use".
Le mot clé "use" lié à une fonction anonyme (à ne pas confondre avec celui utilisé pour les namespaces) permet d'accéder à des variables définies dans le scope qui contient la définition de cette fonction.
Fonction anonyme : le mot clé use
$a = 42; $o = new stdClass(); $o->foo = 'bar'; $f = function() use ($a, $o, &$b){ echo 'a = '.$a.' b = '.$b.' o = '.$o->foo.'
'; }; $b = 1337; $f(); // affichage : a = 42 b = 1337 o = bar $o->foo = 'baz'; $a++; $b++; $f(); // affichage : a = 42 b = 1338 o = baz
Cet exemple nous apprend trois choses très importantes :
- Les objets ($o) sont passés par référence alors que les scalaires ($a) le sont par copie. Dans les deux cas il faut que les variables soient déclarées avant la fonction anonyme sous peine d'erreurs.
- En déclarant explicitement un passage par référence (&$b), la variable est utilisable même si elle n'existe pas encore pendant la déclaration de la fonction anonyme. Ceci est valable aussi bien avec les objets qu'avec les scalaires.
Avec ceci nous avons maintenant la solution pour faire de la récursivité avec une fonction anonyme : il suffit d'indiquer à notre fonction d'utiliser une référence vers elle-même. Ainsi, peut importe que la variable $f ne soit pas encore déclarée, elle sera quand même résolue.
Fonction anonyme récursive : la solution
$f = function($i = 3) use (&$f){ /* Attention aux boucles infinies */ if ($i == 0) { return; } echo $i; $f(--$i); }; $f(); // affichage : 321
A bientôt !
Commentaire(s)