Declaração de métodos deve ser compatível com métodos pais em PHP

107
Padrões estritos: a declaração de childClass :: customMethod () deve ser compatível com a de parentClass :: customMethod ()

Quais são as possíveis causas desse erro no PHP? Onde posso encontrar informações sobre o que significa ser compatível ?

waiwai933
fonte
notJim acertou exatamente. @ waiwai933, se você pudesse postar os cabeçalhos (apenas a primeira linha function customMethod( ... ):) para cada função, poderíamos contar a você o problema específico
nickf
Mais detalhes sobre a mensagem de erro e implicações no tempo de compilação do PHP: bugs.php.net/bug.php?id=46851
hakre
1
Meu problema era que um argumento tinha uma use Closure;dica de tipo, mas eu não tinha adicionado ao topo da minha classe (já que a dica de tipo era Closure). Então ... certifique-se de verificar se está faltando dependências como essa.
Ryan

Respostas:

126

childClass::customMethod()tem argumentos diferentes ou um nível de acesso diferente (público / privado / protegido) do que parentClass::customMethod().

davidtbernal
fonte
1
provavelmente é porque a visibilidade , assinatura de métodos não é um problema no PHP
Gabriel Sosa
43
Ter os mesmos valores padrão exatos do argumento também é importante. Por exemplo, parentClass::customMethod($thing = false)e childClass::customMethod($thing)acionaria o erro, porque o método da criança não definiu um valor padrão para o primeiro argumento.
Charles
1
Acredito que visibilidade é, na verdade, um erro diferente. A propósito, na minha loja não usamos o modo estrito, por isso (usamos E_ALL, IIRC).
davidtbernal
12
Isso mudou no PHP 5.4, btw: * E_ALL agora inclui erros de nível E_STRICT na diretiva de configuração error_reporting. Veja aqui: php.net/manual/en/migration54.other.php
Duncan Lock
1
A falta de um "e" comercial ( &) nos argumentos também pode desencadear esse erro.
IvanRF de
36

Esta mensagem significa que existem certas chamadas de método possíveis que podem falhar em tempo de execução. Suponha que você tenha

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

O compilador apenas verifica a chamada $ a-> foo () em relação aos requisitos de A :: foo () que não requer parâmetros. $ a pode, entretanto, ser um objeto da classe B que requer um parâmetro e, portanto, a chamada falhará em tempo de execução.

No entanto, isso nunca pode falhar e não aciona o erro

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

Portanto, nenhum método pode ter mais parâmetros necessários do que seu método pai.

A mesma mensagem também é gerada quando as dicas de tipo não correspondem, mas neste caso o PHP é ainda mais restritivo. Isso dá um erro:

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

assim como isso:

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

Isso parece mais restritivo do que precisa ser e suponho que seja devido aos internos.

As diferenças de visibilidade causam um erro diferente, mas pelo mesmo motivo básico. Nenhum método pode ser menos visível do que seu método pai.

Idrut
fonte
2
em seu último exemplo - não deve haver um erro aqui porque é legítimo, stdClass $ a é mais restritivo do que $ a misto. Existe alguma forma de contornar isto? quero dizer, neste caso, o PHP deve permitir isso, mas ainda dá um erro ...
galchen
2
Seu último exemplo é seguro para tipos, então certamente é "mais restritivo do que precisa ser". Este pode ser um caso de programação de culto à carga, uma vez que entra em conflito com o polimorfismo em C ++ e Java en.wikipedia.org/wiki/…
Warbo
obrigado pela explicação, no meu caso o primeiro exemplo que você deu foi exatamente o que estava desencadeando meu erro.
billynoah
Obrigado por isso, senhor.
Eldoïr
22

se quiser manter o formulário OOP sem desativar nenhum erro, você também pode:

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
fonte
Eu adoraria usar este hack para contornar esses erros. Eu me preocupo que possa haver uma penalidade de desempenho para esta abordagem. Vou pesquisar isso, mas se você tiver recursos para ajudar a responder a essa pergunta, seria ótimo.
Adam Friedman
Depende da situação, eu acho. Ainda sim, talvez um pouco hacky, mas é php? já, às vezes isso pode ser uma boa solução, obrigado! <@
Mestre James
você salvou meu dia! esta era a única opção para lançar o projeto php5 legado no servidor com php7 sem dor
vladkras
Para este caso você pode usar valores padrão em vez de func_get_args(), ou seja, em B, public function foo($a = null, $b = null, $c = null)como isso não quebrar o contrato prometido por A.
Jake
1

Apenas para expandir esse erro no contexto de uma interface, se você digitar hinting seus parâmetros de função assim:

interface A

use Bar;

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

Classe B

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

Se você esqueceu de incluir a useinstrução em sua classe de implementação (Classe B), você também receberá esse erro, embora os parâmetros do método sejam idênticos.

Spholt
fonte
0

Enfrentei esse problema ao tentar estender uma classe existente do GitHub. Vou tentar me explicar, primeiro escrevendo a aula como pensei que deveria ser e depois a aula como está agora.

O que eu pensei

namespace mycompany\CutreApi;

use mycompany\CutreApi\ClassOfVendor;

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

O que eu finalmente fiz

namespace mycompany\CutreApi;

use \vendor\AwesomeApi\ClassOfVendor;

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

Portanto, parece que esse erro também surge quando você está usando um método que retorna uma classe com namespace e tenta retornar a mesma classe, mas com outro namespace. Felizmente eu encontrei essa solução, mas não entendo totalmente o benefício desse recurso no php 7.2, para mim é normal reescrever métodos de classe existentes conforme necessário, incluindo a redefinição de parâmetros de entrada e / ou até mesmo o comportamento do método.

Uma desvantagem da abordagem anterior é que os IDEs não podiam reconhecer os novos métodos implementados em \ mycompany \ CutreApi \ ClassOfVendor (). Então, por enquanto, irei com essa implementação.

Atualmente concluído

namespace mycompany\CutreApi;

use mycompany\CutreApi\ClassOfVendor;

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

Então, em vez de tentar usar o método "qualquer coisa", escrevi um novo chamado "getWhatever". Na verdade, os dois estão fazendo o mesmo, apenas retornando uma classe, mas com namespaces diferentes, conforme descrevi antes.

Espero que isso possa ajudar alguém.

Ferran
fonte