Considerar los siguientes PHP interfaces:

interface Item {
    //some methods here
}

interface SuperItem extends Item {
    //some extra methods here, not defined in Item
}

interface Collection {
    public function add(Item $item);
    //more methods here
}

interface SuperCollection extends Collection {
    public function add(SuperItem $item);
    //more methods here that "override" the Collection methods like "add()" does
}

Estoy usando PHPStorm, y cuando hago esto, me da un error en el IDE que, básicamente, establece la definición de add() en SuperCollection no es compatible con la definición de la interfaz se extiende, Collection.

De una manera, yo puedo ver que esto es un problema, como la firma del método no coincide con el que «anula» exactamente. Sin embargo, yo creo que esto sería compatible, como SuperItem se extiende Item, así que me gustaría ver add(SuperItem) la misma como add(Item).

Soy curioso en cuanto a si esto es compatible con PHP (versión 5.4 o superior), y tal vez el IDE tiene un bug que no correctamente atrapar a este.

  • En 5.3, me sale «Fatal error: Declaración de SuperCollection::add() debe ser compatible con el de la Colección::add()». Así que parece que PhpStorm es correcta. ¿Por qué crees que 5.4 se comporte de manera diferente?

7 Comentarios

  1. 14

    No, estoy bastante seguro de que PHP no soporta esto, en cualquier versión, y sería más bien la derrota el punto de una interfaz.

    El punto de que una interfaz que le da un contrato fijo con otro código que hace referencia a la misma interfaz.

    Por ejemplo, considere una función como esta:

    function doSomething(Collection $loopMe) { ..... }

    Esta función espera recibir un objeto que implementa la Collection interfaz.

    Dentro de la función, el programador sería capaz de escribir las llamadas a los métodos que se definen en Collection, sabiendo que sería el objeto de implementar los métodos.

    Si usted tiene una anulado la interfaz como esta, entonces usted tiene un problema con esto, porque una SuperCollection objeto podría ser pasados a la función. Iba a trabajar, ya que también es un Collection objeto debido a la herencia. Pero a continuación el código en la función ya no podía estar seguro de que sabe lo que es la definición de la add() método.

    Una interfaz es, por definición, un contrato fijo. Es inmutable.

    Como una alternativa, usted podría considerar el uso de clases abstractas en lugar de interfaces. Esto le permitiría anular en no-de modo Estricto, aunque usted todavía tiene errores si utiliza el Modo Estricto, por las mismas razones.

    • Gracias por la explicación! Estaba pensando que esto debería funcionar en mi cabeza, pero me ayudó a ver el error en que.
    • No estoy de acuerdo con esta respuesta. Las Interfaces no están preocupados con las definiciones de método o de las relaciones en el mismo (por ejemplo, add()ing un elemento significa que usted puede también delete() es). Su única responsabilidad es la de hacer cumplir compatible con las firmas de método (entradas/salidas). Estás en lo correcto acerca de ellos tienen contratos de duración determinada, pero incorrecta acerca de la finalidad del contrato. Dos clases podría muy fácilmente implementan la misma interfaz y han totalmente diferente semántica (por ejemplo, add() en realidad podría eliminar elementos en una clase, y no importa el tiempo las entradas/salidas siguen siendo del mismo tipo).
    • aunque creo que no invalida la explicación, es algo a tener en cuenta.
    • Es una cuestión de interpretación, pero considero que la responsabilidad de una interfaz para extender la semántica de las funciones, no solo las firmas. Muchos prominentes PHP interfaces vienen con bloques de documentación, por ejemplo, github.com/php-fig/log/blob/master/Psr/Log/LoggerInterface.php o github.com/php-fig/http-message/blob/master/src/… . Los ejecutores necesario leer la documentación para garantizar que no se viole el Principio de Sustitución de Liskov. Eso es una ventaja de PHP tener nominal, más que estructural, escribiendo.
  2. 3

    Como una solución estoy usando PHPDoc bloques en las interfaces.

    interface Collection {
       /**
        * @param Item $item
        */
        public function add($item);
        //more methods here
    }
    
    interface SuperCollection extends Collection {
        /**
        * @param SuperItem $item
        */
        public function add($item);
        //more methods here that "override" the Collection methods like "add()" does
    }

    De esta manera, en caso de que usted está utilizando correctamente las interfaces IDE debe ayudar a detectar algunos errores. Usted puede utilizar técnica similar para anular el valor de retorno de tipos así.

  3. 3

    Que la respuesta va a cambiar a finales de este año (2019), si PHP 7.4 se libera como estaba previsto, con tipo mejorado de la varianza.

    El código de la pregunta seguirá siendo válido:

    interface Item {
        //some methods here
    }
    
    interface SuperItem extends Item {
        //some extra methods here, not defined in Item
    }
    
    interface Collection {
        public function add(Item $item);
        //more methods here
    }
    
    interface SuperCollection extends Collection {
        public function add(SuperItem $item); //This will still be a compile error
        //more methods here that "override" the Collection methods like "add()" does
    }

    Porque el Collection interfaz garantiza que cualquier cosa que implementa la que puede aceptar cualquier objeto de tipo Item como el parámetro de add.

    Sin embargo, el siguiente código será válido en PHP 7.4:

    interface Item {
        //some methods here
    }
    
    interface SuperItem extends Item {
        //some extra methods here, not defined in Item
    }
    
    interface Collection {
        public function add(SuperItem $item);
        //more methods here
    }
    
    interface SuperCollection extends Collection {
        public function add(Item $item); //no problem
        //more methods here that "override" the Collection methods like "add()" does
    }

    En este caso Collection garantías de que puede aceptar cualquier SuperItem. Dado que todos los SuperItems son Items, SuperCollection también hace de esta garantía, asegurando que puede aceptar cualquier otro tipo de Item. Esto se conoce como un Contravariante método de tipo de parámetro.

    Hay una forma limitada de tipo de variación en las actuales versiones de PHP. Suponiendo que otras interfaces son como en la pregunta, el SuperCollection puede ser definida como:

    interface SuperCollection extends Collection {
        public function add($item); //no problem
        //more methods here that "override" the Collection methods like "add()" does
    }

    Esto puede ser interpretado en el sentido de valor alguno puede ser pasado a la add método. Que incluye, por supuesto, todos los Items, por lo que este es todavía el tipo de seguro, o puede ser interpretado en el sentido de que una clase no especificada de valores, generalmente documentado como mixed puede ser pasado y el programador debe utilizar otro conocimiento exacto de lo que puede trabajar con la función.

  4. 1

    La ampliación de la interfaz no está permitido cambiar las definiciones de método. Si su SuperItem está extendiendo Elemento, se debe pasar a través de las clases de la implementación de interfaz para la Recolección sin problemas.

    Pero basado en lo que usted realmente quiere hacer, usted puede probar:

    • Crear la interfaz con los métodos ligeramente diferentes para SuperItem y aplicar que:

      interface SuperCollection extends Collection {
          public function addSuper(SuperItem $superItem);
      }
    • Uso de patrón decorator para crear casi la misma interfaz sin necesidad de extender:

      interface Collection {
          public function add(Item $item);
          //more methods here
      }
      
      interface SuperCollection {
          public function add(SuperItem $item);
          //more methods here that "override" the Collection methods like "add()" does
      }

      A continuación, decorador (abstracta) de la clase, que va a utilizar esta interfaz:

      class BasicCollection implements Collection {
          public function add(Item $item)
          {
          }
      }
      
      class DecoratingBasicCollection implements SuperCollection {
          protected $collection;
      
          public function __construct(Collection $collection)
          {
              $this->collection = $collection;
          }
      
          public function add(SuperItem $item)
          {
              $this->collection->add($item);
          }
      }
  5. 0

    El problema no está en el IDE. En PHP no se puede reemplazar un método. Y la compatibilidad es sólo en la dirección opuesta – puede esperar una instancia de la clase padre y recibir de una subclase. Pero cuando se espera una subclase, usted no puede estar seguro si usted recibe los padres de clase – subclase puede definir los métodos que no existen en el padre. Pero aún así, no se puede invalidar el método

    • Por favor, por favor, trate de usar las palabras correctas. Se PUEDE reemplazar un método en PHP, simplemente no sobrecargar.
  6. 0

    Cuando tengo un método que me necesite sobrecarga (que PHP no soporta), estoy seguro de que uno de los argumentos del método (por lo general el último) es una matriz. De esta manera, me puede pasar lo que necesito. Puedo probar a continuación, dentro de la función para varios elementos de la matriz de decirme lo de rutina en el método que debe llevar a cabo por lo general en un select/caso.

Dejar respuesta

Please enter your comment!
Please enter your name here