MISE À JOUR : PHP 7.4 prend désormais en charge la covariance et la contravariance, ce qui résout le problème majeur soulevé dans cette question.
J'ai rencontré un problème avec l'utilisation de l'indication de type de retour dans PHP 7. Je crois comprendre que l'indication : self
signifie que vous avez l'intention qu'une classe d'implémentation se renvoie elle-même. Par conséquent, j'ai utilisé : self
dans mes interfaces pour indiquer cela, mais lorsque j'ai essayé d'implémenter réellement l'interface, j'ai eu des erreurs de compatibilité.
Ce qui suit est une simple démonstration du problème que j'ai rencontré:
interface iFoo
{
public function bar (string $baz) : self;
}
class Foo implements iFoo
{
public function bar (string $baz) : self
{
echo $baz . PHP_EOL;
return $this;
}
}
(new Foo ()) -> bar ("Fred")
-> bar ("Wilma")
-> bar ("Barney")
-> bar ("Betty");
Le résultat attendu était:
Fred Wilma Barney Betty
Ce que j'obtiens en fait, c'est:
Erreur fatale PHP: Déclaration de Foo :: bar (int $ baz): Foo doit être compatible avec iFoo :: bar (int $ baz): iFoo dans test.php à la ligne 7
Le truc est que Foo est une implémentation d'iFoo, donc pour autant que je sache, l'implémentation doit être parfaitement compatible avec l'interface donnée. Je pourrais probablement résoudre ce problème en modifiant soit l'interface, soit la classe d'implémentation (ou les deux) pour renvoyer une indication de l'interface par son nom au lieu de l'utiliser self
, mais je crois comprendre que cela self
signifie sémantiquement "renvoyer l'instance de la classe sur laquelle vous venez d'appeler la méthode ". Par conséquent, le changer pour l'interface signifierait en théorie que je pourrais retourner n'importe quelle instance de quelque chose qui implémente l'interface lorsque mon intention est que l'instance invoquée est ce qui sera retourné.
Est-ce un oubli de PHP ou est-ce une décision délibérée de conception? Si c'est le premier, y a-t-il une chance de le voir corrigé en PHP 7.1? Si ce n'est pas le cas, quelle est la manière correcte de renvoyer en indiquant que votre interface s'attend à ce que vous retourniez l'instance sur laquelle vous venez d'appeler la méthode pour le chaînage?
la source
self
type de retour est censé fonctionner?self
signifier "Renvoyez l'instance sur laquelle vous avez appelé ceci, et non une autre instance qui implémente la même interface". Je semble me souvenir que Java avait un type de retour similaire (même si cela fait un moment que je n'ai pas fait de programmation Java)Réponses:
self
ne fait pas référence à l'instance, il fait référence à la classe courante. Il n'y a aucun moyen pour une interface de spécifier que la même instance doit être retournée - l'utilisationself
de la manière que vous essayez ne ferait que forcer l'instance renvoyée à être de la même classe.Cela dit, les déclarations de type de retour en PHP doivent être invariantes alors que ce que vous essayez est covariant.
Votre utilisation de
self
équivaut à:interface iFoo { public function bar (string $baz) : iFoo; } class Foo implements iFoo { public function bar (string $baz) : Foo {...} }
ce qui n'est pas autorisé.
La RFC des déclarations de type de retour a ceci à dire :
Pour le moment, au moins, le mieux que vous puissiez faire est:
interface iFoo { public function bar (string $baz) : iFoo; } class Foo implements iFoo { public function bar (string $baz) : iFoo {...} }
la source
static
au travail, mais ce n'est même pas reconnustatic
est ajouté à PHP 8.Cela peut également être une solution, que vous ne définissiez pas explicitement le type de retour dans l'interface, uniquement dans le PHPDoc et que vous puissiez ensuite définir le type de retour dans les implémentations:
interface iFoo { public function bar (string $baz); } class Foo implements iFoo { public function bar (string $baz) : Foo {...} }
la source
Foo
simplement utiliserself
.Dans le cas où vous souhaitez forcer depuis l'interface, cette méthode retournera un objet, mais le type d'objet ne sera pas le type d'interface, mais la classe elle-même, alors vous pouvez l'écrire de cette façon:
interface iFoo { public function bar (string $baz) : object; } class Foo implements iFoo { public function bar (string $baz) : self {...} }
Cela fonctionne depuis PHP 7.4.
la source
PHP 8 ajoutera un "type de retour statique" qui résoudra votre problème.
Consultez cette RFC: https://wiki.php.net/rfc/static_return_type
la source
Cela ressemble au comportement attendu pour moi.
Changez simplement votre
Foo::bar
méthode pour reveniriFoo
au lieu deself
et en finir avec elle.Explication:
self
tel qu'utilisé dans l'interface signifie «un objet de typeiFoo
».self
tel qu'utilisé dans l'implémentation signifie «un objet de typeFoo
».Par conséquent, les types de retour dans l'interface et l'implémentation ne sont clairement pas les mêmes.
L'un des commentaires mentionne Java et indique si vous rencontrez ce problème. La réponse est oui, vous auriez le même problème si Java vous permettait d'écrire du code comme celui-là - ce qui n'est pas le cas. Puisque Java vous oblige à utiliser le nom du type au lieu du
self
raccourci PHP , vous ne verrez jamais cela. (Voir ici pour une discussion d'un problème similaire en Java.)la source
self
similaire à déclarerMyClass::class
?