Introducción a Dependency Injection en PHP
El desarrollo moderno en PHP es impensable sin los principios de diseño orientado a objetos. Uno de los patrones clave que permite construir aplicaciones flexibles, testeables y mantenibles es Dependency Injection (DI) o inyección de dependencias.
La esencia del patrón es simple: en lugar de crear objetos dependientes dentro de la clase, los pasamos (inyectamos) desde el exterior. Esto cambia radicalmente la arquitectura de la aplicación, haciendo que las clases sean independientes de implementaciones concretas y simplificando su prueba.
En este artículo analizaremos qué es Dependency Injection en PHP, qué formas de implementación existen y cómo usar contenedores DI para automatizar el proceso.
El problema: ¿por qué es malo sin DI?
Consideremos un ejemplo típico. La clase UserController se encarga de procesar solicitudes relacionadas con usuarios. Utiliza la base de datos a través de la clase MysqlDatabase:
<?php
class MysqlDatabase { public function query(string $sql): array { // ... conexión a MySQL y ejecución de la consulta return []; }}
class UserController { private MysqlDatabase $database;
public function __construct() { // Acoplamiento rígido a una implementación concreta $this->database = new MysqlDatabase(); }
public function listUsers(): array { return $this->database->query('SELECT * FROM users'); }}
$controller = new UserController();¿Cuáles son los problemas de este código?
- Alto acoplamiento:
UserControllerestá fuertemente vinculado aMysqlDatabase. Si queremos migrar a PostgreSQL o usar almacenamiento en archivos, tendremos que modificar el código del controlador. - Dificultad para pruebas: No se puede reemplazar la base de datos por un objeto mock en pruebas unitarias. Cada prueba accederá a la base de datos real, lo cual es lento e incómodo.
- Violación del principio de responsabilidad única: El controlador no debería saber cómo crear una conexión a la base de datos. Su tarea es procesar solicitudes.
La solución a este problema es aplicar el principio de Inversión de Control (IoC) y usar Dependency Injection.
Formas de inyectar dependencias en PHP
Existen tres formas principales de inyectar una dependencia en una clase:
1. Inyección por constructor (Constructor Injection)
La forma más común y recomendada. La dependencia se pasa a través del parámetro del constructor y generalmente se guarda como una propiedad privada de la clase.
<?php
interface DatabaseInterface { public function query(string $sql): array;}
class MysqlDatabase implements DatabaseInterface { public function query(string $sql): array { // Implementación para MySQL return []; }}
class PostgresDatabase implements DatabaseInterface { public function query(string $sql): array { // Implementación para PostgreSQL return []; }}
class UserController { private DatabaseInterface $database;
// La dependencia se inyecta a través del constructor public function __construct(DatabaseInterface $database) { $this->database = $database; }
public function listUsers(): array { return $this->database->query('SELECT * FROM users'); }}
// Uso$mysqlDb = new MysqlDatabase();$controller = new UserController($mysqlDb);
// Fácilmente cambiamos a otra BD$postgresDb = new PostgresDatabase();$controller2 = new UserController($postgresDb);Ventajas: La dependencia es explícitamente visible, el objeto se crea en un estado válido (la dependencia es obligatoria).
2. Inyección por setters (Setter Injection)
Adecuada para dependencias opcionales o cuando las depend