4.9 KiB
Session Binding Initializer
Übersicht
Der SessionBindingInitializer löst das Problem, dass SessionInterface während der Bootstrap-Phase noch nicht verfügbar ist, wenn Initializer ausgeführt werden, die SessionInterface als Dependency benötigen.
Problem
Timing-Problem: SessionInterface wird normalerweise von SessionMiddleware während der Request-Verarbeitung gebunden. Initializer laufen jedoch während der Bootstrap-Phase, bevor ein Request verarbeitet wird. Dies führt zu Dependency-Injection-Fehlern, wenn Initializer SessionInterface als Dependency benötigen.
Beispiel: ActionAuthorizationCheckerInitializer benötigt SessionInterface, um SessionBasedAuthorizationChecker zu erstellen. Ohne SessionBindingInitializer würde die Dependency-Injection fehlschlagen.
Lösung
Der SessionBindingInitializer registriert SessionInterface als lazy binding im Container:
#[Initializer]
public function __invoke(): void
{
$sessionManager = $this->sessionManager;
$this->container->singleton(SessionInterface::class, function(Container $c) use ($sessionManager) {
// Check if Session is already bound (by Middleware)
if ($c->has(Session::class)) {
return $c->get(Session::class);
}
// Fallback: Create new session without request context
// This will be overridden by SessionMiddleware when request is available
return $sessionManager->createNewSession();
});
}
Funktionsweise
-
Lazy Binding:
SessionInterfacewird als Closure registriert, die erst ausgeführt wird, wennSessionInterfacetatsächlich benötigt wird. -
Fallback-Mechanismus:
- Wenn
SessionMiddlewarebereits gelaufen ist, wird die bereits gebundeneSessionverwendet. - Andernfalls wird eine neue Session ohne Request-Context erstellt.
- Wenn
-
Override durch Middleware:
SessionMiddlewareüberschreibt das lazy binding mit der tatsächlichen Session-Instanz, wenn ein Request verarbeitet wird.
Verwendung
Initializer, die SessionInterface benötigen, können es einfach als Dependency injizieren:
final readonly class ActionAuthorizationCheckerInitializer
{
public function __construct(
private Container $container
) {
}
#[Initializer]
public function __invoke(): ActionAuthorizationChecker
{
// SessionInterface wird automatisch vom lazy binding bereitgestellt
$session = $this->container->get(SessionInterface::class);
$checker = new SessionBasedAuthorizationChecker($session);
$this->container->singleton(ActionAuthorizationChecker::class, $checker);
return $checker;
}
}
Wichtige Hinweise
Kein ContextType-Filter
Wichtig: Der SessionBindingInitializer hat keinen ContextType-Filter:
#[Initializer] // ✅ Kein ContextType::WEB Filter
public function __invoke(): void
Grund: Die Discovery läuft während der Bootstrap-Phase, wenn der Context noch cli-script ist. Ein ContextType::WEB Filter würde dazu führen, dass der Initializer übersprungen wird und SessionInterface nicht registriert wird.
Discovery-Timing
Die Discovery scannt Initializer während der Bootstrap-Phase, bevor Requests verarbeitet werden. Initializer mit ContextType-Filtern werden nur ausgeführt, wenn der aktuelle Context passt. Da die Bootstrap-Phase im cli-script Context läuft, werden Initializer mit ContextType::WEB Filter übersprungen.
Lösung: Initializer, die für die Dependency-Registrierung benötigt werden, sollten keinen ContextType-Filter haben, auch wenn sie nur für WEB-Contexts verwendet werden.
Dateien
- Initializer:
src/Framework/Http/Session/SessionBindingInitializer.php - Verwendet von:
src/Framework/LiveComponents/Security/ActionAuthorizationCheckerInitializer.php - Middleware:
src/Framework/Http/Session/SessionMiddleware.php
Troubleshooting
Problem: "Cannot instantiate interface SessionInterface"
Ursache: SessionBindingInitializer wurde nicht gefunden oder nicht ausgeführt.
Lösung:
- Prüfe, ob
SessionBindingInitializerdas#[Initializer]Attribut hat (ohne ContextType-Filter). - Leere den Discovery-Cache:
php console.php discovery:clear-cache - Prüfe die Logs, ob der Initializer gefunden wurde.
Problem: Initializer wird nicht gefunden
Ursache: ContextType-Filter verhindert, dass der Initializer während der Bootstrap-Phase gefunden wird.
Lösung: Entferne den ContextType-Filter vom #[Initializer] Attribut:
// ❌ Falsch: Wird während Bootstrap übersprungen
#[Initializer(ContextType::WEB)]
// ✅ Richtig: Wird während Bootstrap gefunden
#[Initializer]