Introduction à l'Injection de Dépendances (DI) en PHP
Le développement PHP moderne est impossible sans comprendre les principes de couplage faible entre les composants. Dependency Injection (DI), ou injection de dépendances, est l'un des modèles de conception clés qui permet de créer des applications flexibles, testables et facilement maintenables. Au lieu que la classe crée elle-même ses dépendances (par exemple, un objet base de données ou un logger), elle les reçoit de l'extérieur. Cela change radicalement l'architecture du code.
Dans cet article, nous allons détailler ce qu'est la Dependency Injection en PHP, quelles sont les méthodes d'injection, comment fonctionne un conteneur DI et pourquoi ce modèle est devenu la norme pour les frameworks modernes tels que Laravel, Symfony et Yii2.
1. Le problème : Le couplage fort (Tight Coupling)
Avant de parler de la solution, examinons le problème. Un exemple classique est la classe UserController, qui crée directement un objet Database à l'intérieur d'elle-même :
<?phpclass Database { public function query(string $sql): array { // Connexion à la BDD et exécution de la requête return []; }}
class UserController { private Database $db;
public function __construct() { // Dépendance forte — on crée l'objet dans le constructeur $this->db = new Database(); }
public function getUsers(): array { return $this->db->query('SELECT * FROM users'); }}?>Inconvénients de cette approche :
- Difficulté de test : Vous ne pouvez pas remplacer
Databasepar un objet mock dans les tests unitaires. - Violation du principe de responsabilité unique (SRP) : La classe est responsable non seulement de la logique métier, mais aussi de la création des dépendances.
- Difficulté de modification : Si vous souhaitez remplacer MySQL par PostgreSQL, vous devrez modifier le code dans toutes les classes où
Databaseest créé.
2. La solution : L'injection de dépendances (Dependency Injection)
La Dependency Injection propose de transmettre les dépendances de l'extérieur. Il existe trois méthodes principales d'injection : par le constructeur, par les setters et par l'interface (méthode). Examinons chacune.
2.1. Injection par le constructeur (Constructor Injection)
La méthode la plus courante et recommandée. Les dépendances sont transmises une fois lors de la création de l'objet et ne peuvent pas être modifiées par la suite.
<?phpclass UserController { private Database $db;
// La dépendance est transmise de l'extérieur public function __construct(Database $db) { $this->db = $db; }
public function getUsers(): array { return $this->db->query('SELECT * FROM users'); }}
// Utilisation :$db = new Database();$controller = new UserController($db);?>Avantages : Garantit que la dépendance est toujours disponible ; idéal pour les dépendances obligatoires. Inconvénients : Si la classe a beaucoup de dépendances, le constructeur devient volumineux.
2.2. Injection par les setters (Setter Injection)
Convient pour les dépendances optionnelles qui peuvent être définies après la création de l'objet.
<?phpclass Logger { public function log(string $message): void { echo $message; }}
class UserController { private ?Logger $logger = null;
public function setLogger(Logger $logger): void { $this->logger = $logger; }
public function getUsers(): array { if ($this->logger) { $this->logger->log('Récupération des utilisateurs...'); } // Logique de récupération des utilisateurs return []; }}
// Utilisation :$controller = new UserController();$controller->setLogger(new Logger());?>Avantages :