Einführung in Dependency Injection in PHP
Moderne PHP-Entwicklung ist ohne die Prinzipien des objektorientierten Designs nicht denkbar. Eines der Schlüsselmuster, das es ermöglicht, flexible, testbare und wartbare Anwendungen zu erstellen, ist Dependency Injection (DI) oder Abhängigkeitsinjektion.
Das Wesen des Musters ist einfach: Anstatt abhängige Objekte innerhalb einer Klasse zu erstellen, werden sie von außen übergeben (injiziert). Dies verändert die Architektur der Anwendung grundlegend, macht Klassen unabhängig von konkreten Implementierungen und vereinfacht deren Testbarkeit.
In diesem Artikel werden wir untersuchen, was Dependency Injection in PHP ist, welche Möglichkeiten der Implementierung es gibt und wie man DI-Container zur Automatisierung des Prozesses einsetzt.
Das Problem: Warum ist es ohne DI schlecht?
Betrachten wir ein typisches Beispiel. Die Klasse UserController ist für die Verarbeitung von Anfragen im Zusammenhang mit Benutzern zuständig. Sie verwendet eine Datenbank über die Klasse MysqlDatabase:
<?php
class MysqlDatabase { public function query(string $sql): array { // ... Verbindung zu MySQL und Ausführung der Abfrage return []; }}
class UserController { private MysqlDatabase $database;
public function __construct() { // Feste Bindung an eine konkrete Implementierung $this->database = new MysqlDatabase(); }
public function listUsers(): array { return $this->database->query('SELECT * FROM users'); }}
$controller = new UserController();Was sind die Probleme dieses Codes?
- Starke Kopplung:
UserControllerist fest anMysqlDatabasegebunden. Wenn wir auf PostgreSQL umsteigen oder einen Dateispeicher verwenden möchten, muss der Code des Controllers geändert werden. - Schwierigkeit beim Testen: Sie können die Datenbank in Unit-Tests nicht durch ein Mock-Objekt ersetzen. Jeder Test würde auf die reale Datenbank zugreifen, was langsam und unpraktisch ist.
- Verletzung des Prinzips der einzigen Verantwortung: Der Controller sollte nicht wissen, wie man eine Datenbankverbindung herstellt. Seine Aufgabe ist es, Anfragen zu verarbeiten.
Die Lösung dieses Problems ist die Anwendung des Prinzips der Inversion of Control (IoC) und die Verwendung von Dependency Injection.
Arten der Abhängigkeitsinjektion in PHP
Es gibt drei Hauptwege, eine Abhängigkeit in eine Klasse zu injizieren:
1. Injektion über den Konstruktor (Constructor Injection)
Die gebräuchlichste und empfohlene Methode. Die Abhängigkeit wird über einen Konstruktorparameter übergeben und normalerweise als private Eigenschaft der Klasse gespeichert.
<?php
interface DatabaseInterface { public function query(string $sql): array;}
class MysqlDatabase implements DatabaseInterface { public function query(string $sql): array { // Implementierung für MySQL return []; }}
class PostgresDatabase implements DatabaseInterface { public function query(string $sql): array { // Implementierung für PostgreSQL return []; }}
class UserController { private DatabaseInterface $database;
// Die Abhängigkeit wird über den Konstruktor injiziert public function __construct(DatabaseInterface $database) { $this->database = $database; }
public function listUsers(): array { return $this->database->query('SELECT * FROM users'); }}
// Verwendung$mysqlDb = new MysqlDatabase();$controller = new UserController($mysqlDb);
// Einfacher Wechsel zu einer anderen Datenbank$postgresDb = new PostgresDatabase();$controller2 = new UserController($postgresDb);Vorteile: Die Abhängigkeit ist explizit sichtbar, das Objekt wird in einem gültigen Zustand erstellt (Abhängigkeit ist obligatorisch).
2. Injektion über Setter (Setter Injection)
Geeignet für optionale Abhängigkeiten oder wenn die Abhängigkeit