La declaración de métodos debe ser compatible con los métodos principales en PHP

107
Estándares estrictos: la declaración de childClass :: customMethod () debe ser compatible con la de parentClass :: customMethod ()

¿Cuáles son las posibles causas de este error en PHP? ¿Dónde puedo encontrar información sobre lo que significa ser compatible ?

waiwai933
fuente
notJim tiene exactamente la razón. @ waiwai933, si pudiera publicar los encabezados (solo la primera línea function customMethod( ... )
:)
Más detalles sobre el mensaje de error y las implicaciones del tiempo de compilación de PHP: bugs.php.net/bug.php?id=46851
hakre
1
Mi problema era que un argumento tenía una use Closure;sugerencia de tipo, pero luego no lo había agregado a la parte superior de mi clase (ya que la sugerencia de tipo era Closure). Entonces ... asegúrese de verificar si le faltan dependencias como esa.
Ryan

Respuestas:

126

childClass::customMethod()tiene argumentos diferentes, o un nivel de acceso diferente (público / privado / protegido) que parentClass::customMethod().

davidtbernal
fuente
1
probablemente sea porque la visibilidad , la firma de métodos no es un problema en PHP
Gabriel Sosa
43
También es importante tener exactamente los mismos valores predeterminados de argumento. Por ejemplo, parentClass::customMethod($thing = false)y childClass::customMethod($thing)desencadenaría el error, porque el método del niño no ha definido un valor predeterminado para el primer argumento.
Charles
1
Creo que la visibilidad es en realidad un error diferente. Por cierto, en mi tienda no usamos el modo estricto, por eso (usamos E_ALL, IIRC).
davidtbernal
12
Esto ha cambiado en PHP 5.4, por cierto: * E_ALL ahora incluye errores de nivel E_STRICT en la directiva de configuración error_reporting. Vea aquí: php.net/manual/en/migration54.other.php
Duncan Lock
1
La falta de un ampersand ( &) en los argumentos también puede desencadenar este error.
IvanRF
36

Este mensaje significa que hay ciertas posibles llamadas a métodos que pueden fallar en tiempo de ejecución. Suponga que tiene

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

El compilador solo verifica la llamada $ a-> foo () con los requisitos de A :: foo () que no requiere parámetros. Sin embargo, $ a puede ser un objeto de clase B que requiere un parámetro y, por lo tanto, la llamada fallaría en tiempo de ejecución.

Sin embargo, esto nunca puede fallar y no desencadena el error.

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

Por lo tanto, ningún método puede tener más parámetros necesarios que su método principal.

El mismo mensaje también se genera cuando las sugerencias de tipo no coinciden, pero en este caso PHP es aún más restrictivo. Esto da un error:

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

como hace esto:

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

Eso parece más restrictivo de lo que debería ser y supongo que se debe a factores internos.

Las diferencias de visibilidad provocan un error diferente, pero por la misma razón básica. Ningún método puede ser menos visible que su método principal.

ldrut
fuente
2
en su último ejemplo, no debería haber un error aquí porque es legítimo, stdClass $ a es más restrictivo que mixto $ a. ¿Hay alguna manera de evitar esto? Quiero decir, en este caso PHP debería permitir esto, pero todavía da un error ...
galchen
2
Su último ejemplo es de tipo seguro, por lo que ciertamente es "más restrictivo de lo necesario". Este puede ser un caso de programación cargo-culto, ya que entra en conflicto con el polimorfismo en C ++ y Java en.wikipedia.org/wiki/…
Warbo
gracias por la explicación, en mi caso el primer ejemplo que diste fue exactamente lo que estaba provocando mi error.
billynoah
Gracias por esto, señor.
Eldoïr
22

Si desea mantener el formulario OOP sin desactivar ningún error, también puede:

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
fuente
Me encantaría usar este truco para evitar estos errores. Me preocupa que este enfoque pueda tener una penalización en el rendimiento. Investigaré esto, pero si tiene algún recurso para ayudar a responder esa pregunta, sería genial.
Adam Friedman
Depende de la situación, supongo. Todavía sí, tal vez un poco hacky, pero ¿es php? ya, a veces eso puede ser un buen trabajo, ¡gracias! <@
Master James
¡Salvaste mi día! esta fue la única opción para lanzar un proyecto php5 heredado en el servidor con php7 sin dolor
vladkras
Para este caso, puede utilizar los valores por defecto en lugar de func_get_args(), por ejemplo, en B, public function foo($a = null, $b = null, $c = null), ya que esto no se rompe el contrato prometido por A.
Jake
1

Solo para expandir este error en el contexto de una interfaz, si escribe insinuando los parámetros de su función de esta manera:

interfaz A

use Bar;

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

Clase B

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

Si ha olvidado incluir la usedeclaración en su clase de implementación (Clase B), también obtendrá este error aunque los parámetros del método sean idénticos.

Spholt
fuente
0

Enfrenté este problema al intentar extender una clase existente desde GitHub. Voy a tratar de explicarme, primero escribiendo la clase como pensé que debería ser, y luego la clase como es ahora.

Lo que pensé

namespace mycompany\CutreApi;

use mycompany\CutreApi\ClassOfVendor;

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

Lo que finalmente hice

namespace mycompany\CutreApi;

use \vendor\AwesomeApi\ClassOfVendor;

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

Entonces, parece que este error surge también cuando está utilizando un método que devuelve una clase con espacio de nombres e intenta devolver la misma clase pero con otro espacio de nombres. Afortunadamente he encontrado esta solución, pero no entiendo completamente el beneficio de esta característica en php 7.2, para mí es normal reescribir los métodos de clase existentes a medida que los necesite, incluida la redefinición de los parámetros de entrada y / o incluso el comportamiento del método.

Una desventaja del enfoque anterior es que los IDE no podían reconocer los nuevos métodos implementados en \ mycompany \ CutreApi \ ClassOfVendor (). Entonces, por ahora, iré con esta implementación.

Hecho actualmente

namespace mycompany\CutreApi;

use mycompany\CutreApi\ClassOfVendor;

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

Entonces, en lugar de intentar usar el método "lo que sea", escribí uno nuevo llamado "getWhatever". De hecho, ambos están haciendo lo mismo, solo devuelven una clase, pero con diferentes espacios de nombres como he descrito antes.

Espero que esto pueda ayudar a alguién.

Ferran
fuente