Ainsi, l’exemple suivant ne fonctionne pas avec PHP 5.3, et ce n’est pas parce qu’il est complètement stupide :

<?php
class foo
{
   public $bar = 'A bar';

   public function getBarDumper()
   {
      return function() { var_dump($this->bar); };
   }
}

$foo = new foo();
$bar = $foo->getBarDumper();
$bar();
// Fatal error: Using $this when not in object context in /path/to/stupid/script.php on line X
?>

Pour accéder à l’instance de la classe dans laquelle était déclarée la fonction anonyme avec PHP 5.3, il était donc nécessaire de recourir à une variable temporaire et de l’utiliser dans une fermeture lexicale.

<?php
class foo
{
   public $bar = 'A bar';

   public function getBarDumper()
   {
      $that = $this;
      return function() use ($that) { var_dump($that->bar); };
   }
}

$foo = new foo();
$bar = $foo->getBarDumper();
$bar();
// string(5) "A bar"
?>

Reste que cette solution a un inconvénient majeur, car vu que la fonction anonyme n’a pas la même portée que la méthode, $that ne peut accéder aux propriétés et méthodes protégées ou privées de la classe.

<?php
class foo
{
   protected $bar = 'A bar';

   public function getBarDumper()
   {
      $that = $this;
      return function() use ($that) { var_dump($that->bar); };
   }
}

$foo = new foo();
$bar = $foo->getBarDumper();
$bar();
// Fatal error: Cannot access protected property foo::$bar in /path/to/stupid/script.php on line X
?>

Pour remédier à cela, l’implémentation des fonctions anonymes a été revue pour PHP 5.4.

Depuis cette version, si une fonction anonyme est déclarée dans une méthode, elle a la portée de la classe et $this représente dans cette fonction l’instance courante de la classe et le code suivant est donc pleinement fonctionnel avec PHP 5.4 :

<?php
class foo
{
   protected $bar = 'A bar';

   public function getBarDumper()
   {
      return function() { var_dump($this->bar); };
   }
}

$foo = new foo();
$bar = $foo->getBarDumper();
$bar();
//string(5) "A bar"
?>

Corollaire de ce qui précède, en PHP 5.4, il peut y avoir des fonctions anonymes statiques.

Je vous rassure immédiatement, si vous n’avez pas compris la phrase précédente, c’est parfaitement normal.

Voici un exemple pour vous aider :

<?php
class foo
{
   protected $bar = 'A bar';

   public function getBarDumper()
   {
      return function() { var_dump($this->bar); };
   }

   public static function getFactory()
   {
      // la fonction anonyme suivante est statique
      // car elle est déclarée dans une méthode statique.
      return function() { return new static(); };
   }
}

$factory = foo::getFactory();
$foo = $factory();
$bar = $foo->getBarDumper();
$bar();
//string(5) "A bar"
?>

En clair, une fonction anonyme déclarée dans une méthode statique est une fonction anonyme statique, et en conséquence et fort logiquement, il n’est pas possible d’accéder à $this dans ce type de fonction anonyme.

Pour preuve, le code suivant génère une erreur :

<?php
class foo
{
   protected $bar = 'A bar';

   public function getBarDumper()
   {
      return function() { var_dump($this->bar); };
   }

   public static function getFactory()
   {
      // la fonction anonyme suivante est statique
      // car elle est déclarée dans une méthode statique.
      return function() { var_dump($this); return new static(); };
   }
}

$factory = foo::getFactory();
$foo = $factory();
$bar = $foo->getBarDumper();
$bar();
// Notice: Undefined variable: this in /path/to/stupid/script.php on line X
// NULL
// string(5) "A bar"
?>

Tout cela n’aurait guère d’intérêt si PHP 5.4 ne permettait pas de modifier l’objet vers lequel pointe $this dans une fonction anonyme ainsi que la portée de la classe à l’aide des méthodes \closure::bind() et \closure::bindTo() :

<?php
class foo
{
   protected $bar = 'A bar';

   public function getBarDumper()
   {
      return function() { var_dump($this->bar); };
   }
}

class bar
{
   protected $bar = 'A fucking bar!';
}

$foo = new foo();
$bar = new bar();
$barDumper = $foo->getBarDumper();
$barDumper = $barDumper->bindTo($bar, $bar);
$barDumper();
// string(14) "A fucking bar!"
?>

Si vous utilisez \closure::bind() et \closure::bindTo() sur des fonctions anonymes non statiques, vous n’aurez aucun problème.

Par contre, si vous le faites sur des méthodes statiques, PHP va vous insulter :

<?php

class foo
{
   protected $bar = 'A bar';

   public function getBarDumper()
   {
      return function() { var_dump($this->bar); };
   }

   public static function getFactory()
   {
      // la fonction anonyme suivante est statique
      // car elle est déclarée dans une méthode statique.
      return function() { var_dump($this); return new static(); };
   }
}

class bar {}

$factory = foo::getFactory();
$bar = new bar();
$factory = $factory->bindTo($bar, $bar);
Warning: Cannot bind an instance to a static closure in /path/to/stupid/script.php on line X
?>
Or, PHP ne semble à première vu ne pas permettre de discriminer entre une fonction anonyme statique et une fonction anonyme non statique, ce qui peut poser un problème dans certains cas, notamment lorsque l’on manipule des fonctions anonymes définies par un tiers.

Heureusement, comme souvent avec PHP, il y a une solution, même si, toujours comme souvent avec PHP, elle est immonde.

Une fonction anonyme, avant d’être une instance de la classe \closure, est une fonction.

La classe \reflectionFunction permet donc de l’analyser pour en extraire des informations, et il se trouve que cette classe dispose d’une méthode très intéressante dans le cas qui nous occupe, à savoir \reflectionFunction::getClosureThis().

Comme son nom peut vous le laisser supposer, cette méthode permet de récupérer la valeur de $this dans le cadre de la fonction anonyme.

Pour savoir s'il est possible d'utiliser \closure::bindTo() sur une fonction anonyme, il suffit donc de faire appel à cette méthode et de comparer sa valeur de retour.

Si elle est égale à NULL, la fonction anonyme est statique et en conséquence, l'utilisation de \closure::bindTo() sur cette fonction anonyme générera une erreur!

<?php
class foo
{
   protected $bar = 'A bar';

   public function getBarDumper()
   {
      return function() { var_dump($this->bar); };
   }

   public static function getFactory()
   {
      // la fonction anonyme suivante est statique
      // car elle est déclarée dans une méthode statique.
      return function() { return new static(); };
   }
}

$factory = foo::getFactory();
$reflectedFactory = new \reflectionFunction($factory);
if ($reflectedFactory->getClosureThis() === null) echo "La fonction anonyme est statique" . PHP_EOL;
$bar = $factory()->getBarDumper();
$reflectedBar = new \reflectionFunction($bar);
if ($reflectedBar->getClosureThis() !== null) echo "La fonction anonyme n’est pas statique" . PHP_EOL;
// La fonction anonyme est statique
// La fonction anonyme n’est pas statique
?>