Interfaces de PHP 7, sugerencias y tipo de retorno

Me he encontrado con un problema con el uso de sugerencias de tipo de retorno en PHP 7. Tengo entendido que al hacer alusión : self significa que tiene la intención de que una clase implementadora se devuelva a sí misma. Por lo tanto, utilicé : self en mis interfaces para indicar eso, pero cuando traté de implementar realmente la interfaz, obtuve errores de compatibilidad.

La siguiente es una demostración simple del problema que me he encontrado:

 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"); 

El resultado esperado fue:

Fred Wilma Barney Betty

Lo que realmente obtengo es:

PHP Fatal error: Declaración de Foo :: bar (int $ baz): Foo debe ser compatible con iFoo :: bar (int $ baz): iFoo en test.php en la línea 7

La cuestión es que Foo es una implementación de iFoo, por lo que puedo decir que la implementación debería ser perfectamente compatible con la interfaz dada. Presumiblemente podría solucionar este problema cambiando la interfaz o la clase de implementación (o ambas) para devolver la sugerencia de la interfaz por nombre en lugar de usar self , pero mi entendimiento es que semánticamente self significa “devolver la instancia de la clase que acaba de llamar el método en “. Por lo tanto, cambiarlo a la interfaz significaría en teoría que podría devolver cualquier instancia de algo que implemente la interfaz cuando mi intención es para la instancia invocada es lo que se devolverá.

¿Esto es un descuido en PHP o es una decisión de diseño deliberada? Si es el primero, ¿hay alguna posibilidad de verlo arreglado en PHP 7.1? Si no es así, ¿cuál es la forma correcta de retorno que insinúa que su interfaz espera que devuelva la instancia para la que acaba de llamar el método de encadenamiento?

self no se refiere a la instancia, se refiere a la clase actual. No hay forma de que una interfaz especifique que debe devolverse la misma instancia : usar self de la manera en que lo intentará solo exigiría que la instancia devuelta sea de la misma clase.

Dicho esto, las declaraciones de tipo de retorno en PHP deben ser invariables, mientras que lo que estás intentando es covariante.

Su uso de self es equivalente a:

 interface iFoo { public function bar (string $baz) : iFoo; } class Foo implements iFoo { public function bar (string $baz) : Foo {...} } 

que no está permitido


Declaraciones de tipo de devolución RFC tiene esto que decir :

La aplicación del tipo de devolución declarada durante la herencia es invariante; esto significa que cuando un subtipo anula un método principal, el tipo de devolución del hijo debe coincidir exactamente con el padre y no puede omitirse. Si el padre no declara un tipo de devolución, entonces el niño puede declarar uno.

Este RFC originalmente propuso tipos de retorno covariantes pero se cambió a invariante debido a algunos problemas. Es posible agregar tipos de retorno covariantes en algún momento en el futuro.


Por el momento, al menos, lo mejor que puedes hacer es:

 interface iFoo { public function bar (string $baz) : iFoo; } class Foo implements iFoo { public function bar (string $baz) : iFoo {...} } 

También puede ser una solución, que no se define explícitamente el tipo de retorno en la interfaz, solo en el PHPDoc y luego se puede definir el tipo de retorno en las implementaciones:

 interface iFoo { public function bar (string $baz); } class Foo implements iFoo { public function bar (string $baz) : Foo {...} } 

Esto se ve como el comportamiento esperado para mí.

Simplemente cambie su método Foo::bar para devolver iFoo lugar de self y iFoo con ello.

Explicación:

self como se usa en la interfaz significa “un objeto de tipo iFoo “.
self como se usa en la implementación significa “un objeto de tipo Foo “.

Por lo tanto, los tipos de devolución en la interfaz y la implementación claramente no son lo mismo.

Uno de los comentarios menciona Java y si tendrías este problema. La respuesta es sí, tendría el mismo problema si Java le permitiera escribir un código como ese, lo que no ocurre. Como Java requiere que uses el nombre del tipo en lugar del atajo self de PHP, nunca vas a ver esto realmente. (Consulte aquí para una discusión sobre un problema similar en Java).

    Intereting Posts