## Interfaces

Las interfaces de objetos permiten crear código que especifica qué métodos y propiedades una clase debe implementar, sin tener que definir cómo se implementan estos métodos o propiedades. Las interfaces comparten un espacio de nombres con las clases, traits y enumeraciones, de modo que no pueden utilizar el mismo nombre.

Las interfaces se definen de la misma manera que una clase, pero utilizando la palabra clave `interface` en lugar de `class`, y sin que ninguno de los métodos tenga su contenido especificado.

Por la naturaleza misma de una interfaz, todos los métodos declarados en una interfaz deben ser públicos.

En la práctica, las interfaces sirven dos roles complementarios:

- Permitir a los desarrolladores crear objetos de clases diferentes que pueden ser utilizados de manera intercambiable, ya que implementan la o las mismas interfaces. Un ejemplo común son varios servicios de acceso a bases de datos, varios gestores de pago o diferentes estrategias de caché. Diferentes implementaciones pueden ser intercambiadas sin necesitar cambios en el código que las utiliza.
- Para permitir que una función o método acepte y opere sobre un argumento que se ajuste a una interfaz, sin preocuparse de qué más puede hacer el objeto o cómo está implementado. Estas interfaces suelen llamarse `Iterable`, `Cacheable`, `Renderable`, etc. para describir el significado de su comportamiento.

Las interfaces pueden definir métodos mágicos para obligar a las clases que las implementan a implementar estos métodos.

> Nota:

> Aunque esto es soportado, incluir los constructores en las interfaces está fuertemente desaconsejado. Hacerlo reduce radicalmente la flexibilidad de los objetos que implementan la interfaz. Además, los constructores no están sujetos a las reglas de herencia, lo que puede causar incoherencias y comportamientos inesperados.

### `implements`

Para implementar una interfaz, se utiliza el operador `implements`. Todos los métodos de la interfaz deben ser implementados en una clase; si no es así, se emitirá un error fatal. Las clases pueden implementar más de una interfaz, separando cada interfaz por una coma.

Advertencia

Una clase que implementa una interfaz puede utilizar nombres diferentes para sus argumentos que la interfaz. Sin embargo, a partir de PHP 8.0, el lenguaje soporta los argumentos nombrados, lo que significa que el llamador puede depender del nombre del argumento en la interfaz. Por esta razón, se recomienda encarecidamente que los desarrolladores utilicen el mismo nombre de argumento que en la interfaz que se implementa.

> Nota:

> Las interfaces pueden ser extendidas como clases, utilizando el operador extends

> Nota:

> La clase que implementa la interfaz debe declarar todos los métodos en la interfaz con una firma compatible. Una clase puede implementar dos interfaces que definan un método con el mismo nombre. En este caso, la implementación debe seguir las reglas de compatibilidad de firmas para todas las interfaces. Así, la covarianza y la contravarianza pueden ser aplicadas.

### Las constantes

Las interfaces pueden contener constantes. Las constantes de interfaces funcionan exactamente como las constantes de clase. Anterior a PHP 8.1.0, no pueden ser redefinidas por una clase/interfaz que las hereda.

### Propiedades

A partir de PHP 8.4.0, las interfaces también pueden declarar propiedades. Si lo hacen, la declaración debe especificar si la propiedad es legible, modificable, o ambas. La declaración de la interfaz se aplica únicamente al acceso en lectura y escritura públicos.

Una clase puede satisfacer una propiedad de interfaz de varias maneras. Puede definir una propiedad pública. Puede definir una propiedad pública virtual que implemente únicamente el crochet correspondiente. O una propiedad de lectura puede ser satisfecha por una propiedad `readonly`. Sin embargo, una propiedad de interfaz que es modificable no puede ser `readonly`.

Ejemplo #1 Ejemplo de propiedades de interfaz

    <?php
    interface I
    {
        // Una clase que implementa esta interfaz DEBE tener una propiedad públicamente legible,
        // pero que esta sea o no modificable públicamente no está restringido.
        public string $readable { get; }

        // Una clase que implementa esta interfaz DEBE tener una propiedad públicamente modificable,
        // pero que esta sea o no legible públicamente no está restringido.
        public string $writeable { set; }

        // Una clase que implementa esta interfaz DEBE tener una propiedad que sea a la vez públicamente
        // legible y públicamente modificable.
        public string $both { get; set; }
    }

    // Esta clase implementa las tres propiedades como propiedades tradicionales, sin crochets.
    // Esto es completamente válido.
    class C1 implements I
    {
        public string $readable;

        public string $writeable;

        public string $both;
    }

    // Esta clase implementa las tres propiedades utilizando únicamente los crochets
    // solicitados. Esto también es completamente válido.
    class C2 implements I
    {
        private string $written = '';
        private string $all = '';

        // Utiliza únicamente un crochet get para crear una propiedad virtual.
        // Esto satisface el requisito "get public".
        // No es modificable, pero esto no es requerido por la interfaz.
        public string $readable { get => strtoupper($this->writeable); }

        // La interfaz requiere únicamente que la propiedad sea modificable,
        // pero incluir operaciones get también es completamente válido.
        // Este ejemplo crea una propiedad virtual, lo cual es aceptable.
        public string $writeable {
            get => $this->written;
            set {
                $this->written = $value;
            }
        }

        // Esta propiedad requiere que la lectura y la escritura sean posibles,
        // por lo que debemos implementar ambas o permitir el comportamiento por defecto.
        public string $both {
            get => $this->all;
            set {
                $this->all = strtoupper($value);
            }
        }
    }
    ?>

### Ejemplos

Ejemplo #2 Ejemplo de interfaz

    <?php

    // Declaración de la interfaz 'Template'
    interface Template
    {
        public function setVariable($name, $var);
        public function getHtml($template);
    }

    // Implementación de la interfaz
    // Esto funcionará
    class WorkingTemplate implements Template
    {
        private $vars = [];

        public function setVariable($name, $var)
        {
            $this->vars[$name] = $var;
        }

        public function getHtml($template)
        {
            foreach($this->vars as $name => $value) {
                $template = str_replace('{' . $name . '}', $value, $template);
            }

            return $template;
        }
    }

    // Esto no funcionará
    // Fatal error: Class BadTemplate contains 1 abstract methods
    // and must therefore be declared abstract (Template::getHtml)
    class BadTemplate implements Template
    {
        private $vars = [];

        public function setVariable($name, $var)
        {
            $this->vars[$name] = $var;
        }
    }
    ?>

Ejemplo #3 Las interfaces extensibles

    <?php
    interface A
    {
        public function foo();
    }

    interface B extends A
    {
        public function baz(Baz $baz);
    }

    // Esto funcionará
    class C implements B
    {
        public function foo()
        {
        }

        public function baz(Baz $baz)
        {
        }
    }

    // Esto no funcionará y resultará en un error fatal
    class D implements B
    {
        public function foo()
        {
        }

        public function baz(Foo $foo)
        {
        }
    }
    ?>

Ejemplo #4 Compatibilidad de la varianza con múltiples interfaces

    <?php
    class Foo {}
    class Bar extends Foo {}

    interface A {
        public function myfunc(Foo $arg): Foo;
    }

    interface B {
        public function myfunc(Bar $arg): Bar;
    }

    class MyClass implements A, B
    {
        public function myfunc(Foo $arg): Bar
        {
            return new Bar();
        }
    }
    ?>

Ejemplo #5 Herencia de múltiples interfaces

    <?php
    interface A
    {
        public function foo();
    }

    interface B
    {
        public function bar();
    }

    interface C extends A, B
    {
        public function baz();
    }

    class D implements C
    {
        public function foo()
        {
        }

        public function bar()
        {
        }

        public function baz()
        {
        }
    }
    ?>

Ejemplo #6 Interfaces con constantes

    <?php
    interface A
    {
        const B = 'Constante de la interfaz';
    }

    // Muestra: Constante de la interfaz
    echo A::B;

    // Sin embargo, esto no funcionará, ya que no está permitido
    // sobrescribir constantes.
    class B implements A
    {
        const B = 'Constante de clase';
    }

    // Muestra: Constante de clase
    // Anterior a PHP 8.1.0, esto no funcionaría, ya que no estaba permitido
    // redefinir constantes.
    echo B::B;
    ?>

Ejemplo #7 Las interfaces con las clases abstractas

    <?php
    interface A
    {
        public function foo(string $s): string;

        public function bar(int $i): int;
    }

    // Una clase abstracta puede implementar solo una parte de una interfaz.
    // Las clases que extienden la clase abstracta deben implementar el resto.
    abstract class B implements A
    {
        public function foo(string $s): string
        {
            return $s . PHP_EOL;
        }
    }

    class C extends B
    {
        public function bar(int $i): int
        {
            return $i * 2;
        }
    }
    ?>

Ejemplo #8 Extendiendo e implementando simultáneamente

    <?php

    class One
    {
        /* ... */
    }

    interface Usable
    {
        /* ... */
    }

    interface Updatable
    {
        /* ... */
    }

    // El orden de las palabras clave aquí es importante. 'extends' debe ir primero.
    class Two extends One implements Usable, Updatable
    {
        /* ... */
    }
    ?>

Una interfaz, con las declaraciones de tipos, proporciona una buena manera de asegurarse de que un objeto particular contiene métodos particulares. Ver el operador instanceof y las declaraciones de tipo.

`↑` and `↓` to navigate • `Enter` to select • `Esc` to close • `/` to open