Programación Orientada a Objetos con Interfaces PHP
Las interfaces son parte fundamental de la POO en PHP: definen contratos de métodos, permiten múltiples implementaciones y favorecen el desacoplamiento.
Una interfaz es un contrato que lista los métodos que una clase debe proporcionar, sin indicar cómo funcionan esos métodos. Permite que clases no relacionadas acuerden un conjunto compartido de comportamientos, de modo que el resto del código pueda depender del contrato en lugar de una clase concreta. Este capítulo explica cómo declarar interfaces, implementarlas, usar constantes, extender y combinar interfaces, y los errores más comunes que hay que evitar.
Si eres nuevo en el trabajo con objetos, lee primero Clases y Objetos PHP y ¿Qué es la POO en PHP?.
¿Qué es una Interfaz en PHP?
Una interfaz define un conjunto de firmas de métodos públicos — nombres, parámetros y (opcionalmente) tipos de retorno — pero sin cuerpo de método. Cualquier clase que implemente la interfaz se compromete a proporcionar un cuerpo concreto para cada uno de esos métodos. Si no lo hace, PHP lanza un error fatal.
Dado que la interfaz solo indica qué puede hacer una clase y nunca cómo, puedes intercambiar una implementación por otra sin modificar el código que las usa. Ese desacoplamiento es el propósito principal.
<?php
interface Logger {
public function log(string $message): void;
}Reglas clave para los miembros de una interfaz:
- Todos los métodos son implícitamente public y abstract — no se les puede dar cuerpo, y no se pueden marcar como
privateniprotected. - Una interfaz puede declarar constantes, pero no propiedades.
- Una clase indica que cumple el contrato con la palabra clave
implements.
¿Por qué Usar Interfaces?
- Contratos. Garantizan que cualquier clase que implemente la interfaz expone una API acordada, de modo que los llamadores saben exactamente qué métodos están disponibles.
- Bajo acoplamiento. El código puede escribirse contra la interfaz (
Logger) en lugar de una clase concreta (FileLogger), lo que permite cambiar implementaciones libremente — un logger de archivo real en producción, uno ficticio en las pruebas. - Múltiples tipos. A diferencia de la herencia de clases, una clase puede implementar muchas interfaces, lo que le permite desempeñar varios roles a la vez.
- Polimorfismo. Diferentes objetos que comparten una interfaz pueden usarse indistintamente en cualquier lugar donde se espere esa interfaz.
Implementar una Interfaz
Usa la palabra clave implements seguida del nombre de la interfaz, y proporciona un cuerpo para cada método que la interfaz declare.
<?php
interface Logger {
public function log(string $message): void;
}
class ConsoleLogger implements Logger {
public function log(string $message): void {
echo "[LOG] {$message}\n";
}
}
$logger = new ConsoleLogger();
$logger->log('App started');
// Output: [LOG] App startedConsoleLogger cumple el contrato de Logger, así que en cualquier lugar donde tu código espere un Logger puedes pasar un ConsoleLogger.
Type Hinting con una Interfaz
El mayor beneficio llega cuando se usa el type-hint de la interfaz en lugar de una clase concreta. La siguiente función funciona con cualquier Logger, por lo que puedes cambiar implementaciones sin reescribirla.
<?php
interface Logger {
public function log(string $message): void;
}
class ConsoleLogger implements Logger {
public function log(string $message): void {
echo "[LOG] {$message}\n";
}
}
class SilentLogger implements Logger {
public function log(string $message): void {
// intentionally does nothing
}
}
function runJob(Logger $logger): void {
$logger->log('Job finished');
}
runJob(new ConsoleLogger()); // Output: [LOG] Job finished
runJob(new SilentLogger()); // Output: (nothing)Implementar Múltiples Interfaces
Una clase puede implementar varias interfaces a la vez separando sus nombres con comas. Así es como PHP suple la falta de herencia múltiple de clases.
<?php
interface Drawable {
public function draw(): string;
}
interface Serializable {
public function toArray(): array;
}
class Circle implements Drawable, Serializable {
public function __construct(private float $radius) {}
public function draw(): string {
return "Circle(r={$this->radius})";
}
public function toArray(): array {
return ['type' => 'circle', 'radius' => $this->radius];
}
}
$c = new Circle(2.5);
echo $c->draw() . "\n"; // Output: Circle(r=2.5)
echo json_encode($c->toArray()); // Output: {"type":"circle","radius":2.5}Constantes de Interfaz
Las interfaces pueden contener constantes que hereda cada clase que las implementa. Son útiles para valores fijos y compartidos que pertenecen al contrato.
<?php
interface HttpStatus {
const OK = 200;
const NOT_FOUND = 404;
}
class Response implements HttpStatus {
public function notFound(): int {
return self::NOT_FOUND;
}
}
$r = new Response();
echo $r->notFound(); // Output: 404
echo HttpStatus::OK; // Output: 200Extender Interfaces
Una interfaz puede construirse sobre otra con extends y, a diferencia de las clases, una interfaz puede extender múltiples interfaces a la vez. Una clase que implemente la interfaz hija debe satisfacer todos los métodos heredados.
<?php
interface Readable {
public function read(): string;
}
interface Writable {
public function write(string $data): void;
}
interface ReadWrite extends Readable, Writable {}
class Memory implements ReadWrite {
private string $buffer = '';
public function read(): string {
return $this->buffer;
}
public function write(string $data): void {
$this->buffer .= $data;
}
}
$m = new Memory();
$m->write('hello ');
$m->write('world');
echo $m->read(); // Output: hello worldComprobar una Interfaz
Usa el operador instanceof para confirmar que un objeto cumple un contrato dado antes de llamar a sus métodos.
<?php
interface Logger {
public function log(string $message): void;
}
class ConsoleLogger implements Logger {
public function log(string $message): void {
echo $message . "\n";
}
}
$obj = new ConsoleLogger();
var_dump($obj instanceof Logger); // Output: bool(true)Interfaz vs. Clase Abstracta
Se solapan, pero resuelven problemas distintos:
| Interfaz | Clase abstracta | |
|---|---|---|
| Cuerpos de métodos | No (solo firmas) | Sí (puede mezclar concretos + abstractos) |
| Propiedades | No (solo constantes) | Sí |
| Cuántas por clase | Muchas (implements A, B) | Una (extends A) |
| Úsala cuando | Clases no relacionadas comparten una capacidad | Clases relacionadas comparten código y estado |
Recurre a una interfaz para describir una capacidad; recurre a una clase abstracta para compartir implementación parcial entre clases relacionadas. Si necesitas compartir cuerpos de métodos entre clases no relacionadas, consulta PHP Traits.
Errores Comunes
- Olvidar un método. No implementar aunque sea un solo método de la interfaz genera un error fatal al declarar la clase.
- Reducir la visibilidad. Los métodos de la interfaz son públicos; no se pueden implementar como
protectedniprivate. - Cambiar la firma. Los parámetros y el tipo de retorno del método que implementa deben ser compatibles con la interfaz, o PHP lanzará un error.
- Esperar propiedades. Las interfaces declaran comportamiento, no datos — no pueden contener propiedades, solo constantes.
Conclusión
Las interfaces te permiten programar contra un contrato en lugar de una clase concreta: definen lo que una clase debe hacer, admiten múltiples implementaciones y facilitan enormemente el polimorfismo, las pruebas y la refactorización. Combínalas con la herencia y los traits para diseñar aplicaciones PHP flexibles y mantenibles.