PHP 8.3.21 Released!

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.

add a note

User Contributed Notes 4 notes

up
29
thanhn2001 at gmail dot com
14 years ago
PHP prevents interface a contant to be overridden by a class/interface that DIRECTLY inherits it. However, further inheritance allows it. That means that interface constants are not final as mentioned in a previous comment. Is this a bug or a feature?

<?php

interface a
{
const
b = 'Interface constant';
}

// Prints: Interface constant
echo a::b;

class
b implements a
{
}

// This works!!!
class c extends b
{
const
b = 'Class constant';
}

echo
c::b;
?>
up
20
vcnbianchi
3 years ago
Just as all interface methods are public, all interface methods are abstract as well.
up
6
williebegoode at att dot net
10 years ago
In their book on Design Patterns, Erich Gamma and his associates (AKA: "The Gang of Four") use the term "interface" and "abstract class" interchangeably. In working with PHP and design patterns, the interface, while clearly a "contract" of what to include in an implementation is also a helpful guide for both re-use and making changes. As long as the implemented changes follow the interface (whether it is an interface or abstract class with abstract methods), large complex programs can be safely updated without having to re-code an entire program or module.

In PHP coding with object interfaces (as a keyword) and "interfaces" in the more general context of use that includes both object interfaces and abstract classes, the purpose of "loose binding" (loosely bound objects) for ease of change and re-use is a helpful way to think about both uses of the term "interface." The focus shifts from "contractual" to "loose binding" for the purpose of cooperative development and re-use.
up
-1
xedin dot unknown at gmail dot com
4 years ago
This page says that if extending multiple interfaces with the same methods, the signature must be compatible. But this is not all there is to it: the order of `extends` matters. This is a known issue, and while it is disputable whether or not it is a bug, one should be aware of it, and code interfaces with this in mind.

https://bugs.php.net/bug.php?id=67270
https://bugs.php.net/bug.php?id=76361
https://bugs.php.net/bug.php?id=80785
To Top