Most German docs were written with `ue`/`ae`/`oe`/`ss` ASCII fallbacks (`fuer`, `aenderung`, `koennen`, `gemaess`) — relic from older toolchain constraints. Replaced 237 occurrences across 38 .md files with proper umlauts and ß so the docs read naturally. Substitutions are constrained to plain prose: fenced code blocks (```...```), inline code spans (`...`), markdown link targets `[..](..)`, and HTML comments are preserved verbatim. Path references like `docs/howto-erste-aenderung.md` keep their original (umlaut-replaced) filenames — only the surrounding prose is normalised. Tool: bin/fix-md-umlauts.py (idempotent — no-op on already-clean files). Re-runnable if ASCII fallbacks creep back in via copy-paste. Doc-link-check + doc-drift-check stay green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4.3 KiB
Dependency Injection Container
Letzte Aktualisierung: 2026-03-06
Was ist das und wozu brauchen wir es?
Statt dass jede Action-Datei ihre Services selbst zusammenbaut
(new UserAccountService(new UserRepo(), new AuditRepo(), ...)),
gibt es einen zentralen "Service-Speicher": den AppContainer.
Der Container weiß, wie er jeden Service bauen muss — aber er baut ihn erst, wenn er zum ersten Mal gebraucht wird (lazy). Wird der gleiche Service in derselben Anfrage nochmal angefragt, bekommt man dieselbe Instanz zurück (Singleton pro Request).
Services abrufen — der app() Helper
In jeder Action-Datei (pages/**/*.php) steht der Container über
die globale Hilfsfunktion app() zur Verfügung:
$userService = app(\MintyPHP\Service\User\UserAccountService::class);
$authService = app(\MintyPHP\Service\Access\AuthorizationService::class);
Den vollständigen Klassennamen (FQN) als Schlüssel verwenden — so gibt es keine Namenskollisionen und IDEs können Autocomplete nutzen.
app() wirft eine RuntimeException wenn der Service nicht registriert ist.
Das ist absichtlich laut, damit fehlende Registrierungen sofort auffallen.
Aufbau: Registrars
Beim Start der Anwendung läuft core/App/registerContainer.php.
Es erstellt den Container und ruft 8 Registrars nacheinander auf:
| Registrar | Was er registriert |
|---|---|
RepositoryFactoryRegistrar |
Alle Repository-Factories (SQL-Schicht) |
ServiceFactoryRegistrar |
Alle Service-Factories (Business-Logik, mit Abhängigkeiten) |
AccessRegistrar |
RBAC-spezifische Services (Roles, Permissions, Gateways) |
AuthRegistrar |
Auth-spezifische Services (Login, SSO, API-Tokens) |
DirectoryRegistrar |
LDAP/Entra-Directory-Services |
UserRegistrar |
User-spezifische Services und Gateways |
SettingsRegistrar |
Settings-Services und Gateways |
AppServicesRegistrar |
Konkrete Top-Level-Services, die direkt in Actions genutzt werden |
Jeder Registrar implementiert das Interface ContainerRegistrar::register(AppContainer $container).
Wie Abhängigkeiten aufgelöst werden
// In ServiceFactoryRegistrar:
$container->set(UserServicesFactory::class, static fn (AppContainer $c) =>
new UserServicesFactory(
$c->get(AuthServicesFactory::class), // wird lazy aufgelöst
$c->get(UserRepositoryFactory::class),
$c->get(UserGatewayFactory::class),
$c->get(DatabaseSessionRepository::class)
)
);
Der Container übergibt sich selbst als $c an die Factory-Funktion —
so werden alle Abhängigkeiten automatisch und lazy aus dem Container gezogen.
Zirkuläre Abhängigkeiten werden über static fn (): Foo => $c->get(Foo::class) aufgelöst
(der Aufruf wird verzögert, bis er wirklich gebraucht wird).
Neuen Service registrieren
1. Service und ggf. Factory erstellen (wie bisher in core/Service/)
2. Im passenden Registrar eintragen:
// In AppServicesRegistrar (für Services, die direkt in Actions genutzt werden):
$container->set(MeinNeuerService::class, static fn (AppContainer $c) =>
new MeinNeuerService(
$c->get(UserServicesFactory::class),
$c->get(SettingGateway::class)
)
);
3. In der Action nutzen:
$meinService = app(\MintyPHP\Service\MeinNeuerService::class);
Faustregel: welcher Registrar?
- Neue Repository-Factory →
RepositoryFactoryRegistrar - Neue Service-Factory (mit Abhängigkeiten) →
ServiceFactoryRegistrar - Konkreter Service, der direkt in Actions per
app()abgerufen wird →AppServicesRegistrar - Klar einer Domain zugeordnet (Auth, Access, Directory, User, Settings) → der passende Domain-Registrar
Wichtige Regeln
- Nie
new SomeService(...)direkt in Action-Dateien schreiben — immerapp(SomeService::class)nutzen. - Nie Factory-Ketten in Actions (
app(SomeFactory::class)->createService()) — den konkreten Service direkt registrieren und perapp()holen. - Der Container lebt pro HTTP-Anfrage — er wird in
web/index.phpinitialisiert und übersetAppContainer()im globalen State abgelegt. - In CLI-Commands wird der Container separat initialisiert (über die Runtime-Bootstrap-Klassen unter
core/Console/Support/). - In
core/Service/**außerhalb von*Factory.phpniemalsnew ...Factory(),new ...Service()odernew ...Repository()direkt instanziieren.