Files
michaelschiemer/docs/claude/session-binding-initializer.md
2025-11-24 21:28:25 +01:00

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

  1. Lazy Binding: SessionInterface wird als Closure registriert, die erst ausgeführt wird, wenn SessionInterface tatsächlich benötigt wird.

  2. Fallback-Mechanismus:

    • Wenn SessionMiddleware bereits gelaufen ist, wird die bereits gebundene Session verwendet.
    • Andernfalls wird eine neue Session ohne Request-Context erstellt.
  3. 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:

  1. Prüfe, ob SessionBindingInitializer das #[Initializer] Attribut hat (ohne ContextType-Filter).
  2. Leere den Discovery-Cache: php console.php discovery:clear-cache
  3. 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]

Siehe auch