Pour rappel, __invoke() permet d'utiliser une instance d'objet de la même manière qu'un appel de fonction, de la manière suivante :

<?php

class foo
{
public function __invoke()
{
echo __CLASS__;
}
}

$foo = new foo();

$foo(); // provoque l'éxécution de foo::__invoke() qui affiche 'foo'

?>

Imaginons maintenant que vous souhaitez utiliser cette possibilité dans le cadre d'une autre classe, de cette façon :


<?php

class foo
{
public function __invoke()
{
echo __CLASS__;
}
}

class bar
{
protected $foo = null;

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

public function writeFoo()
{
$this->foo();
return $this;
}
}

$foo = new foo();
$bar = new bar($foo);
$bar->writeFoo();

?>

Je pense que vous serez d'accord avec moi sur le fait que ce code n'a absolument rien d'extraordinaire, même s'il est évidement possible d'ergoter pendant longtemps sur la pertinence d'utiliser une méthode magique.

Pourtant, ce code ne fonctionne tout simplement pas et son exécution provoque le message d'erreur suivant :

fch@witchblade:/usr/home/fch/tmp
3> php test.php
PHP Fatal error: Call to undefined method bar::foo() in /usr/home/fch/download/test.php on line 23

PHP se révèle en effet totalement incapable de résoudre l'appel à foo::__invoke() si une instance de la classe foo est utilisé comme propriété d'objet, et cela quelque soit la version du langage, y compris celle de développement.

Il semble en effet que les développeurs de PHP n'aient pas eu conscience de l'ensemble des possibilités d'utilisation de la méthode __invoke() et qu'en conséquence, il ne soit pas possible de l'utiliser dans tous les cas de figure existant.

SI cela n'est pas trop pénalisant, puisqu'il y a dans le cas présent une solution, qui consiste à faire un appel explicite à __invoke() et donc supprimer toute magie, je trouve dommage que l'implémentation d'une nouvelle fonctionnalité dans le langage ne soit pas suffisamment étudiée en amont et nécessite, pour pouvoir être mise en œuvre, le recours à des solutions plus ou moins propres.

Pour autant, tout espoir n'est pas perdu, puisque les développeurs de PHP ont conscience du problème, du moins dans le cas des fonctions anonymes et des fermetures, aka closure, puisqu'il existe une RFC sur le sujet.

Le problème sera donc potentiellement résolu un jour, lorsque d'épineuses questions, telle que la priorité de ce genre de manipulation par rapport à la méthode magique __call(), auront une réponse.

Dans l'intervalle, il faudra donc faire des appels explicite à __invoke(), et donc renoncer à toute magie dans votre code, tout comme dans le cas des fonctions anonymes et des fermetures.

À contrario, le cas des incohérences que j'ai évoqué en introduction de ce billet, notamment celles liées au manque de cohérence des noms de fonction et à l'ordre variable des arguments, est une cause perdue.

En effet, pour des raisons de compatibilité avec l'existant, la communauté des développeurs de PHP se refuse à envisager le moindre changement à ce niveau.