PHP
Tutorial PHP: Interfaces
PHP: Aprende a usar interfaces para definir métodos que aseguren polimorfismo, flexibilidad y el cumplimiento de contratos en tus aplicaciones mediante abstracción.
Aprende PHP GRATIS y certifícateDeclaración de interfaces
En PHP, una interfaz es una estructura que define un conjunto de métodos sin implementar, es decir, especifica qué métodos deben ser implementados por cualquier clase que la utilice. Las interfaces permiten establecer un contrato que las clases deben cumplir, promoviendo así el polimorfismo y la flexibilidad en el diseño del código.
Para declarar una interfaz, se utiliza la palabra clave interface
seguida del nombre de la interfaz. Por convención, los nombres de las interfaces suelen comenzar con una "I" mayúscula, aunque esto no es obligatorio. Dentro de la definición, se declaran los métodos sin cuerpo (sin llaves ni contenido) y no se especifica visibilidad, ya que por defecto todos los métodos en una interfaz son públicos.
Ejemplo de declaración de una interfaz básica:
<?php
interface IForma
{
public function calcularArea(): float;
public function calcularPerimetro(): float;
}
En este ejemplo, la interfaz IForma
declara dos métodos: calcularArea()
y calcularPerimetro()
. Cualquier clase que implemente esta interfaz deberá proporcionar una implementación concreta para ambos métodos, asegurando así que todas las formas puedan calcular su área y perímetro.
Es posible que una interfaz herede de otra utilizando la palabra clave extends
. Esto permite crear jerarquías de interfaces y extender contratos existentes.
Ejemplo de herencia entre interfaces:
<?php
interface IForma3D extends IForma
{
public function calcularVolumen(): float;
}
Aquí, IForma3D
extiende a IForma
, por lo que cualquier clase que implemente IForma3D
deberá implementar los métodos calcularArea()
, calcularPerimetro()
y calcularVolumen()
.
Las interfaces también pueden contener constantes, las cuales son automáticamente públicas y no pueden ser sobrescritas por las clases que las implementen.
Ejemplo con constantes en una interfaz:
<?php
interface IConstantes
{
const VERSION = '1.0';
const AUTOR = 'Juan Pérez';
public function mostrarInformacion(): void;
}
En este caso, las constantes VERSION
y AUTOR
están disponibles para cualquier clase que implemente IConstantes
y se accede a ellas utilizando el operador de resolución de ámbito ::
:
<?php
echo IConstantes::VERSION . "\n";
echo IConstantes::AUTOR . "\n";
Es importante destacar que en las interfaces no se pueden declarar propiedades ni implementar lógica en los métodos. Las interfaces están diseñadas para definir qué debe hacerse, pero no cómo hacerlo. Esto fomenta la abstracción y permite que diferentes clases proporcionen implementaciones variadas del mismo conjunto de métodos.
Además, una clase puede implementar múltiples interfaces, permitiendo así la composición de comportamientos y garantizando que la clase cumpla con varios contratos simultáneamente.
Ejemplo de una clase implementando varias interfaces:
<?php
interface IAlmacenamiento
{
public function guardar(string $datos): void;
}
interface IRegistro
{
public function registrar(string $mensaje): void;
}
class Sistema implements IAlmacenamiento, IRegistro
{
public function guardar(string $datos): void
{
// Implementación del método guardar
}
public function registrar(string $mensaje): void
{
// Implementación del método registrar
}
}
En este ejemplo, la clase Sistema
implementa tanto IAlmacenamiento
como IRegistro
, y debe proporcionar una implementación para todos los métodos declarados en ambas interfaces.
Finalmente, las interfaces pueden ser utilizadas como tipos en declaraciones de parámetros y retornos de funciones, lo que permite utilizar tipado para asegurar que se reciban objetos que implementen una interfaz específica.
Ejemplo de uso de una interfaz como tipo:
<?php
function procesarForma(IForma $forma): void
{
$area = $forma->calcularArea();
echo "El área es: " . $area . "\n";
}
Aquí, la función procesarForma
espera un objeto que implemente la interfaz IForma
, garantizando que el método calcularArea()
esté disponible.
Implementación de interfaces
Para implementar una interfaz en PHP, una clase utiliza la palabra clave implements
seguida del nombre de la interfaz. Al hacerlo, la clase se compromete a proporcionar una implementación concreta de todos los métodos declarados en la interfaz. Esto garantiza que cualquier objeto de esa clase cumplirá con el contrato establecido por la interfaz.
Ejemplo básico de implementación de una interfaz:
<?php
interface IForma
{
public function calcularArea(): float;
public function calcularPerimetro(): float;
}
class Rectangulo implements IForma
{
private float $ancho;
private float $alto;
public function __construct(float $ancho, float $alto)
{
$this->ancho = $ancho;
$this->alto = $alto;
}
public function calcularArea(): float
{
return $this->ancho * $this->alto;
}
public function calcularPerimetro(): float
{
return 2 * ($this->ancho + $this->alto);
}
}
$rectangulo = new Rectangulo(5.0, 10.0);
echo "Área: " . $rectangulo->calcularArea() . "\n";
echo "Perímetro: " . $rectangulo->calcularPerimetro() . "\n";
En este ejemplo, la clase Rectangulo
implementa la interfaz IForma
. Por lo tanto, está obligada a definir los métodos calcularArea()
y calcularPerimetro()
. Si la clase no implementa alguno de los métodos de la interfaz, PHP lanzará un error fatal, ya que no se cumple el contrato.
Es posible que una clase implemente múltiples interfaces, separándolas por comas en la declaración. Esto permite agregar diversas capacidades a una clase, asegurando que cumpla con varios contratos simultáneamente.
Ejemplo de una clase implementando múltiples interfaces:
<?php
interface IColorable
{
public function setColor(string $color): void;
public function getColor(): string;
}
class Circulo implements IForma, IColorable
{
private float $radio;
private string $color;
public function __construct(float $radio, string $color)
{
$this->radio = $radio;
$this->color = $color;
}
public function calcularArea(): float
{
return pi() * pow($this->radio, 2);
}
public function calcularPerimetro(): float
{
return 2 * pi() * $this->radio;
}
public function setColor(string $color): void
{
$this->color = $color;
}
public function getColor(): string
{
return $this->color;
}
}
$circulo = new Circulo(3.0, 'rojo');
echo "Área: " . $circulo->calcularArea() . "\n";
echo "Color: " . $circulo->getColor() . "\n";
En este caso, la clase Circulo
implementa las interfaces IForma
e IColorable
. Debe definir todos los métodos de ambas interfaces, garantizando así que los objetos Circulo
tengan tanto forma como color.
Al implementar una interfaz, es crucial que la firma de los métodos en la clase coincida exactamente con la definida en la interfaz. Esto incluye el nombre del método, los tipos y cantidad de parámetros, y el tipo de retorno. De lo contrario, se producirá un error durante la ejecución.
Ejemplo incorrecto de implementación:
<?php
class Cuadrado implements IForma
{
private float $lado;
public function __construct(float $lado)
{
$this->lado = $lado;
}
// Faltan los tipos de retorno
public function calcularArea()
{
return $this->lado * $this->lado;
}
public function calcularPerimetro()
{
return 4 * $this->lado;
}
}
$cuadrado = new Cuadrado(4.0);
echo "Área: " . $cuadrado->calcularArea() . "\n";
Este código generará un error porque los métodos calcularArea()
y calcularPerimetro()
no especifican el tipo de retorno float
como se declara en la interfaz IForma
. Para corregirlo, se debe añadir el tipo de retorno en la implementación.
Corrección:
<?php
class Cuadrado implements IForma
{
private float $lado;
public function __construct(float $lado)
{
$this->lado = $lado;
}
public function calcularArea(): float
{
return $this->lado * $this->lado;
}
public function calcularPerimetro(): float
{
return 4 * $this->lado;
}
}
$cuadrado = new Cuadrado(4.0);
echo "Área: " . $cuadrado->calcularArea() . "\n";
Al implementar interfaces, también es posible utilizar herencia de clases. Una clase puede extender otra clase y, al mismo tiempo, implementar una o más interfaces.
Ejemplo de clase heredando y implementando una interfaz:
<?php
class Forma
{
protected string $nombre;
public function __construct(string $nombre)
{
$this->nombre = $nombre;
}
public function getNombre(): string
{
return $this->nombre;
}
}
class Triangulo extends Forma implements IForma
{
private float $base;
private float $altura;
public function __construct(string $nombre, float $base, float $altura)
{
parent::__construct($nombre);
$this->base = $base;
$this->altura = $altura;
}
public function calcularArea(): float
{
return ($this->base * $this->altura) / 2;
}
public function calcularPerimetro(): float
{
// Supongamos un triángulo equilátero
return 3 * $this->base;
}
}
$triangulo = new Triangulo('Triángulo', 5.0, 8.0);
echo $triangulo->getNombre() . "\n";
echo "Área: " . $triangulo->calcularArea() . "\n";
Aquí, Triangulo
extiende la clase Forma
e implementa la interfaz IForma
. De esta manera, Triangulo
hereda propiedades y métodos de Forma
y está obligado a implementar los métodos de IForma
.
Si una clase implementa una interfaz que extiende otra interfaz, debe implementar todos los métodos de ambas. Esto asegura una jerarquía clara de contratos y su cumplimiento.
Ejemplo con interfaces extendidas:
<?php
interface IFigura extends IForma
{
public function dibujar(): void;
}
class Poligono implements IFigura
{
public function calcularArea(): float
{
// Implementación específica
return 0.0;
}
public function calcularPerimetro(): float
{
// Implementación específica
return 0.0;
}
public function dibujar(): void
{
echo "Dibujando polígono\n";
}
}
$poligono = new Poligono();
$poligono->dibujar();
La clase Poligono
implementa IFigura
, que extiende IForma
, por lo que debe implementar todos los métodos de ambas interfaces.
Las interfaces también son útiles para definir contratos para funcionalidades comunes en clases no relacionadas. Por ejemplo, si varias clases deben ser iterables, pueden implementar la interfaz incorporada Iterator
, lo que les permite ser recorridas en bucles foreach
.
Ejemplo con interfaz incorporada:
<?php
class Coleccion implements Iterator
{
private array $elementos = [];
private int $indice = 0;
public function __construct(array $elementos)
{
$this->elementos = $elementos;
}
public function rewind(): void
{
$this->indice = 0;
}
public function current(): mixed
{
return $this->elementos[$this->indice];
}
public function key(): int
{
return $this->indice;
}
public function next(): void
{
$this->indice++;
}
public function valid(): bool
{
return isset($this->elementos[$this->indice]);
}
}
$coleccion = new Coleccion([1, 2, 3, 4, 5]);
foreach ($coleccion as $elemento) {
echo $elemento . "\n";
}
En este ejemplo, la clase Coleccion
implementa la interfaz Iterator
, proporcionando los métodos necesarios para que pueda ser iterada con foreach
.
Implementar interfaces en PHP es fundamental para diseñar aplicaciones escalables y mantenibles. Al definir contratos claros mediante interfaces e implementar esas interfaces en las clases, se promueve la interoperabilidad y se facilita el cambio de implementaciones sin afectar al resto del código.
Interfaces vs clases abstractas
En PHP, tanto las interfaces como las clases abstractas son herramientas fundamentales para definir estructuras y contratos que las clases concretas deben seguir. Aunque ambas permiten establecer métodos a implementar, existen diferencias clave que determinan cuándo es más apropiado utilizar una u otra.
Las clases abstractas son clases que no pueden ser instanciadas directamente y pueden contener tanto métodos implementados como métodos abstractos. Un método abstracto es aquel que se declara sin implementación y obliga a las clases hijas a definirlo. Por otro lado, una clase abstracta puede incluir propiedades y métodos concretos que proporcionan funcionalidad compartida entre las clases derivadas.
Ejemplo de clase abstracta:
<?php
abstract class Vehiculo
{
protected string $marca;
public function __construct(string $marca)
{
$this->marca = $marca;
}
abstract public function conducir(): void;
public function obtenerMarca(): string
{
return $this->marca;
}
}
En este ejemplo, la clase Vehiculo es abstracta y define un método abstracto conducir() que debe ser implementado por las clases hijas. Además, proporciona un método concreto obtenerMarca() y una propiedad $marca.
Las interfaces, en cambio, solo pueden declarar métodos públicos sin implementación y constantes. No pueden contener propiedades ni métodos concretos. Su propósito es definir un conjunto de métodos que las clases que las implementen deben proporcionar, sin dictar cómo deben hacerlo.
Ejemplo de interfaz:
<?php
interface IVehiculo
{
public function conducir(): void;
public function frenar(): void;
}
Aquí, la interfaz IVehiculo declara los métodos conducir() y frenar(), que cualquier clase que implemente esta interfaz debe definir.
La principal diferencia radica en que una clase puede implementar múltiples interfaces pero solo puede heredar de una única clase (abstracta o concreta). Esto permite que las interfaces proporcionen una forma de "herencia múltiple" en términos de contratos, mientras que las clases abstractas proporcionan una herencia de implementación.
Comparación de uso:
- Herencia múltiple de interfaces:
<?php
interface IA
{
public function metodoA(): void;
}
interface IB
{
public function metodoB(): void;
}
class ClaseMulti implements IA, IB
{
public function metodoA(): void
{
echo "Implementación de método A\n";
}
public function metodoB(): void
{
echo "Implementación de método B\n";
}
}
$obj = new ClaseMulti();
$obj->metodoA();
$obj->metodoB();
En este caso, ClaseMulti implementa dos interfaces, IA e IB, y debe proporcionar implementaciones para todos los métodos declarados en ambas.
- Herencia simple con clase abstracta:
<?php
abstract class Animal
{
protected string $nombre;
public function __construct(string $nombre)
{
$this->nombre = $nombre;
}
abstract public function hacerSonido(): void;
}
class Perro extends Animal
{
public function hacerSonido(): void
{
echo "Guau\n";
}
}
$mascota = new Perro("Firulais");
$mascota->hacerSonido();
Aquí, la clase Perro extiende de la clase abstracta Animal y está obligada a implementar el método abstracto hacerSonido(). También hereda la propiedad $nombre y el método __construct().
Cuándo usar una interfaz o una clase abstracta:
Interfaz: Cuando se desea definir un contrato sin proporcionar ninguna implementación. Es útil cuando clases no relacionadas deben implementar métodos comunes. Por ejemplo, si varias clases deben ser serializables, pueden implementar una interfaz Serializable.
Clase abstracta: Cuando se quiere compartir implementación común entre clases relacionadas y, además, forzar la implementación de ciertos métodos. Es apropiado cuando existe una jerarquía clara y las clases derivadas comparten comportamiento.
Limitaciones y consideraciones:
Una clase en PHP solo puede heredar de una única clase (abstracta o no), debido a la ausencia de herencia múltiple. Sin embargo, puede implementar múltiples interfaces, lo que ofrece mayor flexibilidad.
Las clases abstractas pueden tener constructores, propiedades y métodos con cualquier nivel de visibilidad (public, protected, private), mientras que las interfaces no pueden tener propiedades ni métodos no públicos.
A partir de PHP 8, las interfaces pueden contener métodos con implementación utilizando la palabra clave default, sin embargo, esta característica es limitada en comparación con las clases abstractas.
Ejemplo de métodos predeterminados en interfaces (PHP 8):
interface ILogger
{
public function log(string $mensaje): void;
public function logInfo(string $mensaje): void;
}
class Logger implements ILogger
{
public function log(string $mensaje): void
{
echo $mensaje . "\n";
}
public function logInfo(string $mensaje): void
{
$this->log("[INFO]: $mensaje");
}
}
$logger = new Logger();
$logger->logInfo("Esto es un mensaje informativo");
En este ejemplo, la interfaz ILogger.
Nota: Aunque las interfaces puedan tener métodos con implementación predeterminada, su uso es limitado y no reemplaza las capacidades de una clase abstracta para proporcionar una base sólida de funcionalidad.
Similitudes entre interfaces y clases abstractas:
- Ambas no pueden ser instanciadas directamente.
- Ambas pueden declarar métodos que las clases derivadas deben implementar.
- Ambas pueden utilizarse como tipos para garantizar que las clases cumplan con cierto contrato.
Diferencias clave:
- Implementación múltiple: Una clase puede implementar múltiples interfaces pero solo puede extender una clase abstracta.
- Propiedades y métodos concretos: Las clases abstractas pueden tener propiedades y métodos con implementación, mientras que las interfaces no pueden tener propiedades y, hasta PHP 8, no podían tener métodos con implementación.
- Visibilidad de métodos: Las interfaces solo pueden declarar métodos públicos, mientras que las clases abstractas pueden tener métodos con cualquier nivel de visibilidad.
Decisión de diseño:
La elección entre una interfaz y una clase abstracta depende del diseño y necesidades de la aplicación:
- Si se necesita compartir código común y proveer una base de comportamiento, es preferible una clase abstracta.
- Si se desea asegurar que varias clases no relacionadas implementen ciertos métodos, se debe usar una interfaz.
Ejemplo práctico:
Supongamos que estamos desarrollando una aplicación que maneja diferentes tipos de pagos: Tarjeta de crédito, PayPal y Transferencia bancaria.
Utilizando una interfaz:
<?php
interface IPago {
public function procesarPago(float $monto): bool;
}
Cada método de pago implementará la interfaz IPago:
<?php
class PagoTarjeta implements IPago {
public function procesarPago(float $monto): bool {
// Lógica para procesar pago con tarjeta
return true;
}
}
class PagoPayPal implements IPago {
public function procesarPago(float $monto): bool {
// Lógica para procesar pago con PayPal
return true;
}
}
Si todos los métodos de pago comparten código, podríamos utilizar una clase abstracta:
<?php
abstract class Pago
{
protected float $monto;
public function __construct(float $monto)
{
$this->monto = $monto;
}
abstract public function procesar(): bool;
public function registrarTransaccion(): void
{
// Código común para registrar la transacción
}
}
Las clases concretas heredan de Pago y deben implementar procesar():
<?php
class PagoTarjeta extends Pago
{
public function procesar(): bool
{
// Lógica específica para tarjeta
$this->registrarTransaccion();
return true;
}
}
En este caso, la clase abstracta Pago proporciona una propiedad $monto y un método concreto registrarTransaccion() que es común a todos los métodos de pago.
Las interfaces y las clases abstractas son herramientas complementarias en PHP. La elección entre una y otra debe basarse en si se necesita compartir implementación y establecer una base común (clase abstracta) o si se requiere establecer un contrato que puede ser implementado por clases diversas y no necesariamente relacionadas (interfaz). Entender las diferencias y características de cada una permite diseñar aplicaciones más flexibles, mantenibles y coherentes.
Todas las lecciones de PHP
Accede a todas las lecciones de PHP y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.
Introducción A Php
Introducción Y Entorno
Instalación Y Primer Programa De Php
Introducción Y Entorno
Tipos De Datos, Variables Y Constantes
Sintaxis
Operadores Y Expresiones
Sintaxis
Estructuras De Control
Sintaxis
Funciones Y Llamada De Funciones
Sintaxis
Cadenas De Texto Y Manipulación
Sintaxis
Manejo De Números
Sintaxis
Manejo De Fechas Y Tiempo
Sintaxis
Manejo De Arrays
Sintaxis
Introducción A La Poo En Php
Programación Orientada A Objetos
Clases Y Objetos
Programación Orientada A Objetos
Constructores Y Destructores
Programación Orientada A Objetos
Herencia
Programación Orientada A Objetos
Encapsulación
Programación Orientada A Objetos
Polimorfismo
Programación Orientada A Objetos
Interfaces
Programación Orientada A Objetos
Traits
Programación Orientada A Objetos
Namespaces
Programación Orientada A Objetos
Autoloading De Clases
Programación Orientada A Objetos
Manejo De Errores Y Excepciones
Programación Orientada A Objetos
Manejo De Archivos
Programación Orientada A Objetos
Patrones De Diseño
Programación Orientada A Objetos
Introducción A Los Formularios En Php
Formularios
Procesamiento De Datos De Formularios
Formularios
Manejo De Archivos En Formularios
Formularios
Redirecciones Y Retroalimentación Al Usuario
Formularios
Formularios Dinámicos Y Separación De Lógica
Formularios
Introducción A La Persistencia En Php
Persistencia
Conexión A Bases De Datos
Persistencia
Consultas Y Operaciones Crud
Persistencia
Gestión De Transacciones
Persistencia
Manejo De Errores Y Excepciones En Base De Datos
Persistencia
Patrones De Acceso A Datos
Persistencia
Concepto De Sesiones En Php
Sesiones Y Cookies
Configuración De Sesiones
Sesiones Y Cookies
Cookies
Sesiones Y Cookies
Manejo Avanzado De Sesiones Y Cookies
Sesiones Y Cookies
Principales Vulnerabilidades En Php
Seguridad
Seguridad En Formularios Y Entrada De Datos
Seguridad
Protección Frente A Inyección Sql
Seguridad
Gestión De Contraseñas Y Cifrado
Seguridad
Seguridad En Sesiones Y Cookies
Seguridad
Configuraciones De Php Para Seguridad
Seguridad
Introducción Al Testing En Php
Testing
Phpunit
Testing
Cobertura De Código En Testing
Testing
Test Doubles (Mocks, Stubs, Fakes, Spies)
Testing
Pruebas De Integración Y Funcionales
Testing
En esta lección
Objetivos de aprendizaje de esta lección
- Entender el concepto de interfaces en PHP.
- Aprender a declarar interfaces con métodos públicos.
- Conocer las ventajas del polimorfismo a través de contratos.
- Implementar interfaces en clases y entender su obligatoriedad.
- Utilizar interfaces para combinar comportamientos de clases diferentes.
- Extender interfaces para crear jerarquías.
- Diferenciar entre interfaces y clases abstractas.