Interfaces de PHP 7, sugerencias de tipo de retorno y auto

7 minutos de lectura

avatar de usuario
GordonM

ACTUALIZAR: PHP 7.4 ahora admite covarianza y contravarianza que aborda el problema principal planteado en esta pregunta.


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

La siguiente es una demostración simple del problema con el 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 era:

Fred Wilma Barney Betty

Lo que realmente obtengo es:

Error fatal de PHP: 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 selfpero tengo entendido que semánticamente self significa “devolver la instancia de la clase en la que acaba de llamar al método”. 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 que la instancia invocada sea lo que se devolverá.

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

  • Creo que es un error en la sugerencia de tipo de devolución de PHP, tal vez debería plantearlo como un insecto; pero es poco probable que alguna solución llegue a PHP 7.1 en esta etapa tardía

    – Marcos Baker

    21 de agosto de 2016 a las 21:33


  • Dado que la última versión beta de 7.1 se puso en línea hace unos días, es muy poco probable que alguna solución llegue a 7.1.

    – Carlota Dunois

    21 de agosto de 2016 a las 21:38

  • Por interés, ¿dónde estás leyendo tu interpretación de cómo el self tipo de retorno se supone que funciona?

    – Adán Cameron

    21/08/2016 a las 21:45


  • @Adam: Parece que es lógico para self para significar “Devolver la instancia a la que llamaste, y no alguna otra instancia que implemente la misma interfaz”. Me parece recordar que Java tenía un tipo de retorno similar (aunque ha pasado un tiempo desde que hice programación Java)

    – GordonM

    21 de agosto de 2016 a las 21:53

  • Hola gordon. Bueno, a menos que esté documentado que funcione en alguna parte, no contaría con que lo que podría ser lógico sea la realidad. TBH con la situación que describiría, sería lo más declarativo posible y usaría iFoo como el tipo de retorno. ¿Hay alguna situación en la que eso realmente no funcione? (Me doy cuenta de que esto es un “consejo” / opinión en lugar de “una respuesta”, dicho eso.

    – Adán Cameron

    21 de agosto de 2016 a las 23:25

avatar de usuario
usuario3942918

nota editorial: la respuesta a continuación está desactualizada. como php PHP7.4.0, lo siguiente es perfectamente legal:

<?php
Interface I{
    public static function init(?string $url): self;
}
class C implements I{
    public static function init(?string $url): self{
        return new self();
    }
}
$o = C::init("foo");
var_dump($o);

respuesta original:

self no se refiere a la instancia, se refiere a la clase actual. No hay forma de que una interfaz especifique que el mismo instancia debe ser devuelto – usando self en la forma en que lo intenta, solo haría cumplir que la instancia devuelta sea de la misma clase.

Dicho esto, las declaraciones de tipo de devolución en PHP deben ser invariantes, mientras que lo que está intentando es covariante.

tu 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.


los Declaraciones de tipos de devolución RFC posee esto para decir:

La aplicación del tipo de retorno declarado durante la herencia es invariable; esto significa que cuando un subtipo anula un método principal, el tipo de retorno del elemento secundario debe coincidir exactamente con el principal y no se puede omitir. Si el padre no declara un tipo de devolución, el hijo puede declarar uno.

Este RFC originalmente proponía tipos de devolución covariantes, pero se cambió a invariable debido a algunos problemas. Es posible agregar tipos de devolución 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  {...}
}

  • Dicho esto, habría esperado una sugerencia de tipo de retorno de static para trabajar, pero ni siquiera se reconoce

    – Marcos Baker

    22 de agosto de 2016 a las 7:20

  • Paul, eliminar los comentarios que eliminó aquí es realmente dañino porque (A) pierde información importante y (B) interrumpe el flujo de discusión en relación con los otros comentarios. No veo ninguna razón por la que sus comentarios que se basan en Mark y Gordon deban eliminarse. De hecho, estás haciendo esto por todas partes y tiene que parar. No hay absolutamente ninguna buena razón para volver a una pregunta de hace un año y eliminar todos sus comentarios, destruyendo por completo el flujo de la discusión. De hecho, es perjudicial y disruptivo.

    – Cody gris

    22 ago 2017 a las 14:00


  • Hay un prefacio importante a una parte de su cita que se ve aquí: “Los tipos de devolución covariantes se consideran sonido de tipo y se utilizan en muchos otros idiomas. (C++ y Java pero no C#, creo). Este RFC originalmente propuso tipos de devolución covariantes, pero se cambió a invariable debido a algunos problemas”. Tengo curiosidad por saber qué problemas tuvo PHP. Su elección de diseño causa varias limitaciones que también causan problemas extraños con los rasgos, haciéndolos algo inútiles en muchos casos. a menos que afloje las restricciones de tipo de devolución (como se ve en algunas respuestas a continuación).Muy frustrante.

    – John Pancoast

    3 de enero de 2020 a las 0:06


  • @MarkBaker el tipo de retorno static se está agregando a PHP 8.

    – Ricardo Jefe

    5 de agosto de 2020 a las 19:33

  • Quizás para ser la “mejor” opción también se lo merece /** @return Foo */ (si no puede actualizar su versión de PHP). Al menos, de esa manera el IDE estaría sugiriendo el tipo de letra correcto. Probado con Netbeans.

    – volpavl

    11 de enero de 2021 a las 21:56


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

interface iFoo
{
    public function bar (string $baz);
}

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

  • O en lugar de Foo Solo usa self.

    – en cambio

    25 dic 2018 a las 20:20

PHP 8 agregará “tipo de retorno estático” que resolverá su problema.

Echa un vistazo a este RFC: https://wiki.php.net/rfc/static_return_type

Esto parece el comportamiento esperado para mí.

Solo cambia tu Foo::bar método para regresar iFoo en vez de self y terminar con eso.

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 los mismos.

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

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad