Deklaracja metod powinna być zgodna z metodami nadrzędnymi w PHP

107
Ścisłe standardy: deklaracja childClass :: customMethod () powinna być zgodna z deklaracją parentClass :: customMethod ()

Jakie są możliwe przyczyny tego błędu w PHP? Gdzie mogę znaleźć informacje o tym, co to znaczy być kompatybilnym ?

waiwai933
źródło
notJim ma rację. @ waiwai933, gdybyś mógł opublikować nagłówki (tylko pierwszą linię function customMethod( ... )
:)
Więcej szczegółów na temat komunikatu o błędzie i implikacji dotyczących czasu kompilacji PHP: bugs.php.net/bug.php?id=46851
hakre
1
Mój problem polegał na tym, że argument zawierał podpowiedź do typu, ale potem nie dodałem use Closure;go do początku mojej klasy (ponieważ podpowiedź do typu była Closure). Więc ... pamiętaj, aby sprawdzić, czy nie brakuje takich zależności.
Ryan

Odpowiedzi:

126

childClass::customMethod()ma inne argumenty lub inny poziom dostępu (publiczny / prywatny / chroniony) niż parentClass::customMethod().

davidtbernal
źródło
1
prawdopodobnie dlatego, że widoczność , podpis metod nie jest problemem w PHP
Gabriel Sosa,
43
Istotne jest również posiadanie tych samych dokładnych wartości domyślnych argumentów. Na przykład parentClass::customMethod($thing = false)i childClass::customMethod($thing)spowoduje błąd, ponieważ metoda dziecka nie zdefiniowała wartości domyślnej dla pierwszego argumentu.
Charles
1
Uważam, że widoczność jest w rzeczywistości innym błędem. Nawiasem mówiąc, w moim sklepie nie używamy trybu ścisłego z tego powodu (używamy E_ALL, IIRC).
davidtbernal
12
Zmieniło się to w PHP 5.4, btw: * E_ALL zawiera teraz błędy poziomu E_STRICT w dyrektywie konfiguracyjnej error_reporting. Zobacz tutaj: php.net/manual/en/migration54.other.php
Duncan Lock
1
Brak znaku ampersand ( &) w argumentach może również wywołać ten błąd.
IvanRF
36

Ten komunikat oznacza, że ​​istnieją pewne możliwe wywołania metod, które mogą zakończyć się niepowodzeniem w czasie wykonywania. Załóżmy, że tak

class A { public function foo($a = 1) {;}}
class B extends A { public function foo($a) {;}}
function bar(A $a) {$a->foo();}

Kompilator sprawdza tylko wywołanie $ a-> foo () pod kątem wymagań A :: foo (), która nie wymaga żadnych parametrów. $ a może jednak być obiektem klasy B, który wymaga parametru, więc wywołanie zakończy się niepowodzeniem w czasie wykonywania.

To jednak nigdy nie może zawieść i nie powoduje błędu

class A { public function foo($a) {;}}
class B extends A { public function foo($a = 1) {;}}
function bar(A $a) {$a->foo();}

Zatem żadna metoda nie może mieć więcej wymaganych parametrów niż jej metoda nadrzędna.

Ta sama wiadomość jest również generowana, gdy wskazówki dotyczące typów nie pasują, ale w tym przypadku PHP jest jeszcze bardziej restrykcyjne. To daje błąd:

class A { public function foo(StdClass $a) {;}}
class B extends A { public function foo($a) {;}}

tak jak to:

class A { public function foo($a) {;}}
class B extends A { public function foo(StdClass $a) {;}}

Wydaje się, że jest to bardziej restrykcyjne niż powinno być i zakładam, że wynika to z przyczyn wewnętrznych.

Różnice w widoczności powodują inny błąd, ale z tego samego podstawowego powodu. Żadna metoda nie może być mniej widoczna niż jej metoda nadrzędna.

ldrut
źródło
2
w twoim ostatnim przykładzie - nie powinno tu być błędu, ponieważ jest prawidłowy, stdClass $ a jest bardziej restrykcyjny niż mieszany $ a. czy jest sposób na obejście tego? mam na myśli to, że w tym przypadku PHP powinno na to pozwolić, ale nadal wyświetla błąd ...
galchen
2
Twój ostatni przykład jest bezpieczny dla typów, więc z pewnością jest „bardziej restrykcyjny, niż powinien”. Może to być przypadek programowania kultowego typu cargo, ponieważ koliduje z polimorfizmem w C ++ i Javie en.wikipedia.org/wiki/…
Warbo
dzięki za wyjaśnienie, w moim przypadku pierwszy przykład, który podałeś, był dokładnie tym, co spowodowało mój błąd.
billynoah
Dziękuję za to, sir.
Eldoïr
22

jeśli chcesz zachować formularz OOP bez wyłączania błędów, możesz również:

class A
{
    public function foo() {
        ;
    }
}
class B extends A
{
    /*instead of : 
    public function foo($a, $b, $c) {*/
    public function foo() {
        list($a, $b, $c) = func_get_args();
        // ...

    }
}
Sajjad Shirazy
źródło
Bardzo chciałbym użyć tego hacka, aby obejść te błędy. Martwię się, że takie podejście może wpłynąć na wydajność? Zbadam to, ale jeśli masz jakieś zasoby, aby pomóc odpowiedzieć na to pytanie, byłoby świetnie.
Adam Friedman
Chyba zależy od sytuacji. Wciąż tak, może trochę hacky, ale to php? już, czasami może to być niezła praca, dzięki! <@
Master James,
uratowałeś mi dzień! była to jedyna opcja, aby uruchomić stary projekt php5 na serwerze z php7 bez bólu
vladkras
W tym przypadku można użyć wartości domyślnych zamiast func_get_args(), czyli w B, public function foo($a = null, $b = null, $c = null)jak to nie zerwać kontrakt obiecał A.
Jake
1

Aby rozwinąć ten błąd w kontekście interfejsu, jeśli wpisujesz wskazówki dotyczące parametrów funkcji w następujący sposób:

interfejs A

use Bar;

interface A
{
    public function foo(Bar $b);
}

Klasa B.

class B implements A
{
    public function foo(Bar $b);
}

Jeśli zapomniałeś dołączyć useinstrukcji do swojej klasy implementującej (Klasa B), otrzymasz również ten błąd, mimo że parametry metody są identyczne.

Spholt
źródło
0

Napotkałem ten problem podczas próby rozszerzenia istniejącej klasy z GitHub. Spróbuję się wytłumaczyć, najpierw pisząc zajęcia tak, jak myślałem, że powinny, a potem zajęcia tak, jak jest teraz.

Co myślałem

namespace mycompany\CutreApi;

use mycompany\CutreApi\ClassOfVendor;

class CutreApi extends \vendor\AwesomeApi\AwesomeApi
{
   public function whatever(): ClassOfVendor
   {
        return new ClassOfVendor();
   }
}

Co w końcu zrobiłem

namespace mycompany\CutreApi;

use \vendor\AwesomeApi\ClassOfVendor;

class CutreApi extends \vendor\AwesomeApi\AwesomeApi
{
   public function whatever(): ClassOfVendor
   {
        return new \mycompany\CutreApi\ClassOfVendor();
   }
}

Wygląda więc na to, że ten błąd pojawia się również wtedy, gdy używasz metody zwracającej klasę z przestrzenią nazw i próbujesz zwrócić tę samą klasę, ale z inną przestrzenią nazw. Na szczęście znalazłem to rozwiązanie, ale nie do końca rozumiem korzyści płynące z tej funkcji w php 7.2, dla mnie normalne jest przepisywanie istniejących metod klas tak, jak ich potrzebujesz, w tym przedefiniowanie parametrów wejściowych i / lub nawet zachowanie metoda.

Jedną z wad poprzedniego podejścia jest to, że IDE nie mogły rozpoznać nowych metod zaimplementowanych w \ mycompany \ CutreApi \ ClassOfVendor (). Więc na razie pójdę z tą implementacją.

Obecnie zrobione

namespace mycompany\CutreApi;

use mycompany\CutreApi\ClassOfVendor;

class CutreApi extends \vendor\AwesomeApi\AwesomeApi
{
   public function getWhatever(): ClassOfVendor
   {
        return new ClassOfVendor();
   }
}

Więc zamiast próbować użyć metody „cokolwiek”, napisałem nową o nazwie „getWhthing”. W rzeczywistości obie robią to samo, po prostu zwracają klasę, ale z różnymi przestrzeniami nazw, jak opisałem wcześniej.

Mam nadzieję, że to może komuś pomóc.

Ferran
źródło