AKTUALIZACJA : PHP 7.4 obsługuje teraz kowariancję i kontrawariancję, co rozwiązuje główny problem poruszony w tym pytaniu.
Napotkałem problem ze stosowaniem podpowiedzi typu zwracanego w PHP 7. Rozumiem, że podpowiedź : self
oznacza, że klasa implementująca ma zwrócić się sama. Dlatego użyłem : self
w moich interfejsach, aby to wskazać, ale kiedy próbowałem faktycznie zaimplementować interfejs, otrzymałem błędy zgodności.
Poniżej znajduje się prosta demonstracja problemu, z którym się spotkałem:
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");
Oczekiwany wynik to:
Fred Wilma Barney Betty
W rzeczywistości otrzymuję:
Błąd krytyczny PHP: Deklaracja Foo :: bar (int $ baz): Foo musi być kompatybilne z iFoo :: bar (int $ baz): iFoo w test.php w linii 7
Chodzi o to, że Foo jest implementacją iFoo, o ile wiem, implementacja powinna być idealnie kompatybilna z danym interfejsem. Prawdopodobnie mógłbym rozwiązać ten problem, zmieniając interfejs lub klasę implementującą (lub obie) tak, aby zwracały wskazówkę do interfejsu według nazwy zamiast używania self
, ale rozumiem, że semantycznie self
oznacza „zwróć wystąpienie klasy, do której właśnie wywołałeś metodę ”. Dlatego zmiana go na interfejs teoretycznie oznaczałaby, że mógłbym zwrócić każdą instancję czegoś, co implementuje interfejs, gdy moja intencja jest zwrócona w wywołanej instancji.
Czy jest to przeoczenie w PHP, czy jest to celowa decyzja projektowa? Jeśli jest to pierwsze, czy jest szansa, że zostanie to naprawione w PHP 7.1? Jeśli nie, to jaki jest właściwy sposób zwrócenia wskazówki, że Twój interfejs oczekuje od Ciebie zwrócenia instancji, do której właśnie wywołałeś metodę w celu utworzenia łańcucha?
źródło
self
powinien działać typ zwrotu?self
aby oznaczać „Zwróć instancję, do której to wywołałeś , a nie inną instancję, która implementuje ten sam interfejs”. Wydaje mi się, że Java miała podobny typ zwrotu (chociaż minęło trochę czasu, odkąd programowałem w Javie)Odpowiedzi:
self
nie odnosi się do instancji, odnosi się do bieżącej klasy. Nie ma sposobu, aby interfejs określił, że ta sama instancja musi zostać zwrócona - użycieself
w sposób, w jaki próbujesz, wymusiłoby jedynie wymuszenie, aby zwracana instancja była tej samej klasy.To powiedziawszy, deklaracje typu zwracanego w PHP muszą być niezmienne, podczas gdy to, co próbujesz, jest kowariantne.
Twoje użycie
self
jest równoważne z:interface iFoo { public function bar (string $baz) : iFoo; } class Foo implements iFoo { public function bar (string $baz) : Foo {...} }
co jest niedozwolone.
Powrót Rodzaj Deklaracje RFC ma do powiedzenia :
Na razie przynajmniej najlepsze, co możesz zrobić, to:
interface iFoo { public function bar (string $baz) : iFoo; } class Foo implements iFoo { public function bar (string $baz) : iFoo {...} }
źródło
static
zadziała, ale nie jest nawet rozpoznawanastatic
jest dodawany do PHP 8.Może to być również rozwiązanie, w którym nie definiujesz jawnie typu zwracanego w interfejsie, tylko w PHPDoc, a następnie możesz zdefiniować określony typ zwracania w implementacjach:
interface iFoo { public function bar (string $baz); } class Foo implements iFoo { public function bar (string $baz) : Foo {...} }
źródło
Foo
prostu użyćself
.W przypadku, gdy chcesz wymusić z interfejsu, ta metoda zwróci obiekt, ale typ obiektu nie będzie typem interfejsu, a samą klasą, możesz to zapisać w ten sposób:
interface iFoo { public function bar (string $baz) : object; } class Foo implements iFoo { public function bar (string $baz) : self {...} }
Działa od PHP 7.4.
źródło
PHP 8 doda „statyczny typ zwrotu”, który rozwiąże Twój problem.
Sprawdź ten dokument RFC: https://wiki.php.net/rfc/static_return_type
źródło
To wygląda na oczekiwane zachowanie.
Po prostu zmień
Foo::bar
metodę na powrótiFoo
zamiastself
i skończ z tym.Wyjaśnienie:
self
użyte w interfejsie oznacza „obiekt typuiFoo
”.self
użyte w implementacji oznacza „obiekt typuFoo
”.Dlatego typy zwracane w interfejsie i implementacja wyraźnie nie są takie same.
Jeden z komentarzy wspomina o Javie i czy miałbyś ten problem. Odpowiedź brzmi: tak, miałbyś ten sam problem, gdyby Java pozwalała ci pisać w ten sposób kod - co nie. Ponieważ Java wymaga użycia nazwy typu zamiast
self
skrótu PHP , nigdy tego nie zobaczysz. (Zobacz tutaj, aby zapoznać się z omówieniem podobnego problemu w Javie).źródło
self
podobne do deklarowaniaMyClass::class
?