Pour preuve, sans les fonctions anonymes et les fermetures lexicales, le système de bouchonnage (aka mock) de atoum, mon framework de tests unitaires pour PHP 5.3+, n'aurait certainement pas été aussi puissant ni aussi simple à utiliser.

De même, Pimple, un injecteur de dépendance qui est à la base du micro-framework Silex, n'aurait jamais pu être développé avec une version de PHP inférieure à 5.3.

Pour autant, comme souvent avec les nouvelles fonctionnalités ajoutées au langage, le support des fonctions anonymes de PHP 5.3 s'est révélé à l'usage incomplet, ou du moins imparfait et frustrant.

En effet, il ne permet pas, entre autre chose, d'utiliser $this au sein d'une fonction anonyme déclarée dans une méthode de classe, et il était donc nécessaire de recourir à une astuce de ce genre pour pouvoir le faire :

<?php

class tag
{
   protected $tag = '';

   public function __construct($tag)
   {
      $this->tag = $tag;
   }

   public function getTag()
   {
      return $this->tag;
   }

   public function getDecorator()
   {
      $that = $this;

      return function($string) use ($that) {
         return '<' . $that->getTag() . '>' . $string . '</' . $that->getTag() . '>';
      };
   }
}

$div = new tag('div');
$divDecorator = $div->getDecorator();
echo $divDecorator('ceci est un div');

?>

En soit, cela n'a rien de bien dérangeant, si ce n'est que ce n'est pas très esthétique, et que la fermeture lexicale retournée par la méthode est intimement liée à l'instance de la classe foo.

De plus, cette astuce ne permet pas d'accéder à des propriétés ou des méthodes privées ou protégées de la classe considérée.

Or, depuis PHP 5.4, il est désormais possible d'utiliser directement $this dans une fonction anonyme définie dans une méthode de classe, donc sans avoir besoin d'en faire une copie et de l'injecter via le mot-clef use :

<?php

class tag
{
   ...
   public function getDecorator()
   {
      return function($string) {
         return '<' . $this->tag . '>' . $string . '</' . $this->tag . '>'; };
      }
   }
}

?>

Et comme les plus attentifs l'auront remarqué, il est également devenu possible d'accéder à des membres de classe privés ou protégés au sein de la fonction anonyme.

Plus fort encore, il est même devenu possible de rattacher la fonction anonyme ainsi obtenue à un autre objet, et cela même s'il n'est pas de la même classe, grâce à la méthode \closure::bindTo().

Cette méthode retourne en effet un fonction anonyme identique à celle sur laquelle elle est appelée, mais dont $this correspond à l'objet qui lui a été passé en premier argument :

<?php

class otherTag
{
   protected $tag = '';

   public function __construct($tag)
   {
      $this->tag = $tag;
   }
}

$blockquote = new otherTag('blockquote');
$blockquoteDecorator = $divDecorator->bindTo($blockquote, $blockquote);
echo $blockquoteDecorator('ceci est un blockquote');

?>

Grâce à ce code, $this est égal à $blockquote dans la fonction anonyme $blockquoteDecorator, et non plus à $div.

Pour autant, $this pointe toujours sur $div dans la fonction anonyme $divDecorator.

Encore une fois, les plus attentifs auront remarqué que la variable $blockquote est utilisée à la fois comme premier et comme second argument de la méthode \closure::bindTo().

Cela permet de rattacher la portée de la fonction anonyme à $blockquote et donc autorise la variable $this de la fonction anonyme à accéder aux membres privées et protégés de $blockquote, soit en l'occurrence $this->tag.

Si la variable $blockquote n'avait pas été passée également en second argument, l'erreur suivante aurait été générée :

PHP Fatal error:  Cannot access protected property otherTag::$tag in path/to/file.php on line 666

L'utilisation du second argument aurait cependant été inutile si $blockquote avait été une instance de la classe tag.

Il aurait également été possible de s'en passer si la méthode getTag() avait été utilisé dans la fonction anonyme de la classe tag et que cette méthode avait également été définie dans la classe otherTag.

Le support des fonctions anonymes a donc été grandement amélioré par PHP 5.4, et outre le fait que cela rend le code plus lisible, les possibilités fonctionnelles sont également plus importantes qu'avec les versions antérieures du langage, d'autant que je ne présente dans ce billet que quelques-une des possibilités et que j'ai omis tout ce qui concerne les fonctions anonymes statiques.

Je vous invite donc à consulter les tests unitaires de PHP 5.4 (qui correspondent aux fichiers allant de closure_036.phpt à closure_046.phpt) concernant les fonctions anonymes et les fermetures lexicales pour plus de détails.