Einführung in Dependency Injection (DI) in PHP
Moderne PHP-Entwicklung ist ohne das Verständnis der Prinzipien loser Kopplung von Komponenten nicht möglich. Dependency Injection (DI) ist eines der wichtigsten Entwurfsmuster, das es ermöglicht, flexible, testbare und leicht wartbare Anwendungen zu erstellen. Anstatt dass eine Klasse ihre Abhängigkeiten selbst erstellt (z. B. ein Datenbankobjekt oder einen Logger), erhält sie diese von außen. Dies verändert die Code-Architektur grundlegend.
In diesem Artikel werden wir im Detail besprechen, was Dependency Injection in PHP ist, welche Injektionsmethoden es gibt, wie ein DI-Container funktioniert und warum dieses Muster zum Standard für moderne Frameworks wie Laravel, Symfony und Yii2 geworden ist.
1. Das Problem: Feste Kopplung (Tight Coupling)
Bevor wir über die Lösung sprechen, betrachten wir das Problem. Ein klassisches Beispiel ist die Klasse UserController, die direkt ein Database-Objekt in sich selbst erstellt:
<?phpclass Database { public function query(string $sql): array { // Verbindung zur DB und Ausführung der Abfrage return []; }}
class UserController { private Database $db;
public function __construct() { // Feste Abhängigkeit – Objekt wird im Konstruktor erstellt $this->db = new Database(); }
public function getUsers(): array { return $this->db->query('SELECT * FROM users'); }}?>Nachteile dieses Ansatzes:
- Schwierigkeit beim Testen: Sie können
Databasein Unit-Tests nicht durch ein Mock-Objekt ersetzen. - Verletzung des Single-Responsibility-Prinzips (SRP): Die Klasse ist nicht nur für die Geschäftslogik, sondern auch für die Erstellung von Abhängigkeiten verantwortlich.
- Schwierigkeit bei Änderungen: Wenn Sie MySQL durch PostgreSQL ersetzen möchten, müssen Sie den Code in allen Klassen ändern, in denen
Databaseerstellt wird.
2. Die Lösung: Dependency Injection
Dependency Injection schlägt vor, Abhängigkeiten von außen zu übergeben. Es gibt drei Hauptmethoden der Injektion: über den Konstruktor, über Setter und über eine Schnittstelle (Methode). Betrachten wir jede.
2.1. Konstruktor-Injektion (Constructor Injection)
Die gebräuchlichste und empfohlene Methode. Abhängigkeiten werden einmalig bei der Objekterstellung übergeben und können später nicht geändert werden.
<?phpclass UserController { private Database $db;
// Abhängigkeit wird von außen übergeben public function __construct(Database $db) { $this->db = $db; }
public function getUsers(): array { return $this->db->query('SELECT * FROM users'); }}
// Verwendung:$db = new Database();$controller = new UserController($db);?>Vorteile: Stellt sicher, dass die Abhängigkeit immer verfügbar ist; ideal für obligatorische Abhängigkeiten. Nachteile: Wenn eine Klasse viele Abhängigkeiten hat, wird der Konstruktor umfangreich.
2.2. Setter-Injektion (Setter Injection)
Geeignet für optionale Abhängigkeiten, die nach der Objekterstellung gesetzt werden können.
<?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('Fetching users...'); } // Logik zum Abrufen von Benutzern return []; }}
// Verwendung:$controller = new UserController();$controller->setLogger(new Logger());?>Vorteile: