2010-12-30 10 views
13

Creo que será mucho más fácil ver el problema en un ejemplo de código que escribir la pregunta en primer lugar. Aquí está mi código php:¿Insinúa el tipo de php no llevarse bien con las interfaces y las clases abstractas?

<?php 

interface AnInterface 
{ 
     public function method(); 
}  

class AClass implements AnInterface 
{ 
     public function method() 
     { 
       echo __METHOD__; 
     } 
}  

abstract class AnAbstractClass 
{ 
     abstract public function method(AnInterface $Object); 
} 

class ConcreteClass extends AnAbstractClass 
{ 
     public function method(AClass $Object) 
     { 
       $Object->method(); 
     } 
} 

$Object1 = new ConcreteClass(); 
$Object2 = new AClass(); 

$Object1->method($Object2); 

El código anterior hace que el siguiente error:

Fatal error: Declaration of ConcreteClass::method() must be compatible with that of AnAbstractClass::method()

El problema es que no parece php estar reconociendo las firmas de AnAbstractClass :: método y ConcreteClass: : método como compatible. ¿Estoy haciendo algo mal? ¡Gracias!

+4

Por favor, * por favor * adquiera el hábito de publicar el mensaje de error que su código está generando. Publicar código sin publicar la salida (o error) es inútil. – meagar

Respuesta

24

php doesn't seem to be recognizing the signatures of AnAbstractClass::method and ConcreteClass::method as compatible.

PHP es correcto, son no compatible con. Al permitir que solo las instancias de AClass (o sus hijos) pasen a ConcreteClass::method, está incumpliendo el contrato que AnAbstractClass proporciona: Cualquiera de sus subclases debe aceptar AnInterface como argumento para su method().

Si su ejemplo práctico, y no tenía otra clase BClass implementar AnInterface, tendríamos una situación donde según AnAbstractClass, method() debe aceptar casos de BClass, mientras que según ConcreteClass, no debería.

Cambie su firma para ConcreteClass::method para que coincida con la de AnAbstractClass::method.

+3

Después de pensarlo un poco, llegué a la conclusión de que tienes razón. Creo que estaba tratando de mal usar un patrón. Muchas gracias. – Muc

+0

¿Puedo anularlo en phpdoc para autocompletar en mi IDE (phpStorm)? Porque ahora no me da esa habilidad – Crusader

2

No se calcula. Tuvimos la misma discusión ayer:
Can parameter types be specialized in PHP

Todas las clases derivadas deben implementar las firmas de método de forma idéntica.

Esto es algo que idealmente se verificaría en el tiempo de ejecución. Pero en PHP el analizador sí. (. Para compensar, PHP no comprueba el acceso atributo privado/protegido en el momento de análisis, pero dejó que una vez volar en tiempo de ejecución)

Si desea aplicar un tipo más estrictas, aconsejaría:

assert(is_a($Object, "AClass")); 
+5

Dado que es PHP 5, esa afirmación debería decir 'assert ($ Object instanceof AClass);' – BoltClock

+0

Sí, eso podría funcionar, pero en realidad no es la forma en que se supone que es, creo que tuve una idea errónea en primer lugar , vea la respuesta de @meagar – Muc

+0

Los casos como heredar de ArrayObject serían buenos candidatos para hacer una afirmación de tipo o lanzar si se recibiera el tipo incorrecto. Pero no puede cambiar la interfaz básica (por ejemplo, ArrayAccess :: offsetSet siempre tomará un parámetro mixto para el valor). –

2

He aquí un ejemplo que muestra, ¿por qué esto no está permitido:

<?php 
class BClass implements AnInterface { } 

function moo(AnAbstractClass $abstract) 
{ 
    $b = new BClass(); 
    $abstract->method($b); 
} 

Este sería un código válido, pero sería un error, si pasa un ConcreteClass a MOO, debido a su método ConcreteClass::method no permite BClass.

Es complicado, pero es más fácil de entender, si ve un ejemplo.

Cuestiones relacionadas