100 votes

Interfaces PHP 7, indication de type de retour et self

J'ai couru dans quelque chose d'un problème avec l'utilisation de type de retour allusion en PHP 7. Ma compréhension est que l'allusion : self signifie que vous avez l'intention pour la mise en œuvre de la classe de retourner lui-même. J'ai donc utilisé : self dans mes interfaces pour indiquer que, mais quand j'ai essayé de mise en œuvre de l'interface j'ai eu des erreurs de compatibilité.

Ce qui suit est une simple démonstration de la question que j'ai pu croiser:

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 est:

Fred Wilma Barney Betty

Ce que j'ai fait:

PHP Fatal error: Déclaration de Foo::bar(int $baz): Foo doit être compatible avec iFoo::bar(int $baz): iFoo dans test.php sur la ligne 7

Le truc, c'est Foo est une implémentation de iFoo, donc autant que je peux dire à la mise en œuvre doit être parfaitement compatible avec l'interface donnée. Je pourrait sans doute résoudre ce problème en changeant l'interface ou la mise en œuvre de la classe (ou les deux) de rendement de l'indice de l'interface par leur nom au lieu d'utiliser self, mais ma compréhension est que de la sémantique self signifie "retour de l'instance de la classe que vous venez de appelé la méthode". Donc de la modifier à l'interface signifie en théorie que je retourne tout cas de quelque chose qui implémente l'interface lors de mon intention est pour le invoquée instance est ce qui va être retourné.

Est-ce un oubli dans PHP ou est-ce une volonté délibérée de décision de conception? Si c'est l'ancien est-il une chance de le voir corrigé en PHP 7.1? Si non alors quelle est la bonne manière de revenir allusion au fait que votre interface vous attend pour retourner l'instance que vous avez appelé la méthode de chaînage?

107voto

Paul Crovella Points 17057

self ne fait pas référence à l'instance, il se réfère à la classe en cours. Il n'existe aucun moyen d'une interface pour spécifier que la même instance doit être retourné - à l'aide d' self de la manière que vous tentez serait de ne l'appliquer que le retour de l'instance de la même classe.

Cela dit, le type de retour des déclarations en PHP doit être invariante alors que ce que vous tentez est covariant.

Votre utilisation de self est équivalent à:

interface iFoo
{
    public function bar (string $baz) : iFoo;
}

class Foo implements iFoo
{

    public function bar (string $baz) : Foo  {...}
}

qui n'est pas autorisé.


Le Type de Retour des Déclarations RFC a ceci à dire:

L'application de la déclaration de type de retour au cours de l'héritage est invariant, ce qui signifie que lorsqu'un sous-type remplace un parent méthode, puis le type de retour de l'enfant doit correspondre exactement à la mère et ne peut pas être omis. Si le parent ne pas déclarer un type de retour alors que l'enfant est autorisé à déclarer.

...

Cette RFC initialement proposé covariante des types de retour, mais a été changé à invariante à cause de quelques problèmes. Il est possible d'ajouter des covariants des types de retour à un certain moment dans l'avenir.


Pour le moment, du moins du mieux que vous pouvez faire est de:

interface iFoo
{
    public function bar (string $baz) : iFoo;
}

class Foo implements iFoo
{

    public function bar (string $baz) : iFoo  {...}
}

14voto

Gabor Points 116

Il peut également s'agir d'une solution, à savoir que vous ne définissiez pas explicitement le type de retour dans l'interface, mais uniquement dans le PHPDoc, puis que vous puissiez 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  {...}
}
 

4voto

instead Points 958

Au cas où, lorsque vous souhaitez forcer l'interface, cette méthode renverra un objet, mais le type d'objet ne sera pas le type d'interface, mais la classe elle-même, 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.2.

0voto

Moshe Katz Points 3466

Cela ressemble au comportement attendu de moi.

Il suffit de changer votre Foo::bar méthode pour renvoyer iFoo au lieu de self et être fait avec elle.

Explication:

self utilisé dans l'interface de "un objet de type iFoo."
self utilisés dans les moyens de mise en œuvre "d'un objet de type Foo."

Par conséquent, les types de retour dans l'interface et la mise en œuvre sont clairement pas les mêmes.

L'un des commentaires mentionne Java et si vous avez ce problème. La réponse est oui, vous auriez le même problème si Java vous a permis de vous écrire du code comme ça, il n'est pas. Depuis Java exige que vous utilisez le nom du type, au lieu de PHP self de raccourci, vous n'allez jamais voir cette. (Voir ici pour une discussion d'une semblable question en Java.)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X