Service Container¶
Service-Container ist eine Methode, Klassen-Objekte dynamisch und zentral zu erstellen.
Gemini:¶
Der Service-Container ist keine Funktionalität von PHP selbst. PHP bringt von Haus aus keinen solchen Mechanismus mit.
Es handelt sich um ein rein architektonisches Software-Konzept (ein Entwurfsmuster namens Dependency Injection Container), das von Frameworks in PHP-Code umgesetzt wird. In Concrete CMS stammt diese Funktionalität aus dem Fundament des Laravel-Frameworks (der Illuminate\Container), das Concrete unter der Haube nutzt.
Die großartige Nachricht für deine eigenen, CMS-unabhängigen PHP-Projekte lautet: Nein, du musst dir das absolut nicht selber basteln! Die PHP-Community ist durch den Paketmanager Composer so modular aufgebaut, dass du dir die besten Service-Container der Welt mit nur einem einzigen Befehl in jedes beliebige, eigene PHP-Projekt holen kannst.
Wie du einen Service-Container in deinen eigenen Projekten nutzt¶
Es gibt in der PHP-Welt zwei extrem populäre, schlanke und professionelle Container, die du kostenlos in deinen Projekten installieren kannst.
Option A: PHP-DI (Sehr empfehlenswert für eigene Projekte)¶
PHP-DI ist fantastisch, weil es extrem mächtig ist und über sogenannte Autowiring verfügt (es liest die Konstruktoren deiner Klassen vollautomatisch via Reflection aus, ohne dass du etwas konfigurieren musst).
1. Installation per Terminal in deinem Projekt:
composer require php-di/php-di
2. Nutzung in deinem eigenen PHP-Code:
<?php
require 'vendor/autoload.php';
// 1. Erstelle den Container
$containerBuilder = new \DI\ContainerBuilder();
$container = $containerBuilder->build();
// 2. Fordere einfach deine Klasse an.
// PHP-DI erkennt automatisch, was der Konstruktor dieser Klasse braucht,
// sucht die Abhängigkeiten, baut sie zusammen und liefert das fertige Objekt!
$mailService = $container->get(ImportMailService::class);
$mailService->sendImportReport();
Option B: Die Symfony DependencyInjection Komponente¶
Wenn du es noch etwas "schwergewichtiger" und enterprise-tauglicher magst (viele große CMS und Frameworks wie Drupal oder Typo3 setzen darauf), kannst du die Standalone-Komponente von Symfony nutzen.
Installation:
composer require symfony/dependency-injection
Warum du das für deine eigenen Projekte tun solltest¶
Wenn du anfängst, für deine eigenen PHP-Projekte (außerhalb von Concrete CMS) einen Container wie PHP-DI zu nutzen, erreichst du sofort dasselbe professionelle Level:
- Deine Klassen werden sauberer, weil sie keine
new-Aufrufe von anderen Services mehr im Rumpf haben. - Du kannst deine Projekte exzellent testen (Unit Testing), weil du dem Container beim Testen einfach "Mock-Objekte" (Fake-Datenbanken) übergeben kannst.
- Dein Code wird extrem flexibel und modular.
Zusammenfassend: PHP stellt dir die Sprachelemente (Klassen, Typen, Reflection) zur Verfügung. Frameworks wie Laravel/Concrete CMS oder Composer-Pakete wie PHP-DI nutzen diese Elemente, um dir das fertige Werkzeug "Service-Container" betriebsbereit zu schenken.
Möchtest du ein konkretes Beispiel sehen, wie man so einen Container in einem simplen, eigenen PHP-Skript von Grund auf mit Composer aufsetzt?
Beispiel-Implemenbtierung (Gemini)¶
Perfekt! Dann setzen wir das jetzt einmal ganz pragmatisch auf. Du wirst sehen, dass es in weniger als fünf Minuten eingerichtet ist und das Prinzip danach sofort in Fleisch und Blut übergeht.
Wir bauen ein kleines, eigenständiges Skript für eine Wüsten-Tourismus-App, bei der ein TourManager einen DesertLogger benötigt.
Schritt 1: Das Projekt vorbereiten (Composer)¶
Erstelle einen neuen, leeren Ordner auf deinem Computer (z. B. mein-container-projekt). Öffne dein Terminal (Eingabeaufforderung), navigiere in diesen Ordner und installiere PHP-DI mit diesem Befehl:
composer require php-di/php-di
Composer legt dir jetzt den Ordner vendor/ an, in dem die gesamte Container-Logik und das automatische Laden der Klassen bereitliegen.
Schritt 2: Die Klassen schreiben (Ohne new-Magie)¶
Wir erstellen nun zwei ganz normale PHP-Klassen. Beachte, dass keine dieser Klassen weiß, woher ihre Abhängigkeiten kommen – sie fordern sie einfach nur im Konstruktor an.
Erstelle eine Datei namens DesertLogger.php:
<?php
class DesertLogger
{
public function logMessage(string $message): void
{
// Ein simpler Log in die Konsole
echo "[Wüsten-Log " . date('H:i:s') . "]: " . $message . "\n";
}
}
Erstelle eine Datei namens TourManager.php:
<?php
class TourManager
{
private DesertLogger $logger;
// Hier fordern wir den Logger an. PHP-DI sieht diesen Typ-Hinweis (Typehint)!
public function __construct(DesertLogger $logger)
{
$this->logger = $logger;
}
public function starteKamelTour(): void
{
$this->logger->logMessage("Die Kamel-Tour durch die Sahara wurde gestartet.");
}
}
Schritt 3: Der Container erwacht zum Leben (index.php)¶
Jetzt führen wir alles in einer zentralen Startdatei zusammen. Erstelle eine index.php. Das ist der Ort, an dem der Container die Arbeit für dich übernimmt.
<?php
require 'vendor/autoload.php';
require 'DesertLogger.php';
require 'TourManager.php';
use DI\ContainerBuilder;
// 1. Den Container initialisieren
$builder = new ContainerBuilder();
$container = $builder->build();
// 2. DAS WUNDER: Wir fragen NUR nach dem TourManager.
// Wir sagen NICHT, dass er einen DesertLogger braucht. Das merkt der Container selbst!
/** @var TourManager $tourManager */
$tourManager = $container->get(TourManager.phpClass ?? 'TourManager');
// 3. Ausführen
$tourManager->starteKamelTour();
Wenn du diese index.php jetzt im Terminal via php index.php ausführst, erhältst du sofort die korrekte Log-Ausgabe.
Was ist hier gerade Faszinierendes passiert?¶
- Du hast an keiner Stelle
new TourManager(new DesertLogger())geschrieben. - Der Container hat die
TourManager-Klasse analysiert, gesehen: "Ah, da wird im Konstruktor einDesertLoggerverlangt", hat geschaut, ob er die KlasseDesertLoggerkennt, hat erst den Logger gebaut und diesen dann automatisch in denTourManagerhineingesteckt (Autowiring). - Wenn du morgen den
TourManageranpasst und sagst, er braucht jetzt auch noch eine KlasseKamelRepository, musst du dieindex.phpnicht anfassen. Du erstellst einfach die neue Klasse, fügst sie dem Konstruktor hinzu, und der Container löst die Kette im Hintergrund wieder vollautomatisch auf.
Genau diese Funktionalität nutzt Concrete CMS im Hintergrund mit $this->app->make(). Nur dass das CMS den Container bereits beim Systemstart für dich aufbaut, damit du dich im Package sofort der Logik widmen kannst!