invoker = new MiddlewareInvoker($this->container); $this->logger = $this->container->get(Logger::class); } public function handle(Request $request): Response { // Kontext mit dem Request erstellen $context = new MiddlewareContext($request); $this->stateManager = new MiddlewareStateManager()->forRequest($request); // Start der Middleware-Chain loggen error_log("🚀 MIDDLEWARE CHAIN START - URI: {$request->path}, Method: {$request->method->value}, Middleware-Count: " . count($this->middlewares)); // Middleware-Stack durchlaufen $resultContext = $this->doProcessMiddlewareStack($context, 0); // Ende der Middleware-Chain loggen if ($resultContext->hasResponse()) { error_log("✅ MIDDLEWARE CHAIN COMPLETE - Final Response Status: {$resultContext->response?->status->value}"); return $resultContext->response; } // Wenn keine Response vorhanden ist, ist das ein Fehler in der Middleware-Konfiguration error_log("❌ MIDDLEWARE CHAIN FAILED - No response created after processing all middlewares"); throw new \RuntimeException(sprintf( 'Keine Response nach Middleware-Chain erstellt. Stellen Sie sicher, dass eine DefaultResponseMiddleware konfiguriert ist. URI: %s, Method: %s, Middleware-Count: %d', $resultContext->request->path, $resultContext->request->method->value, count($this->middlewares) )); } private function doProcessMiddlewareStack(MiddlewareContext $context, int $index): MiddlewareContext { // Wenn das Ende der Middleware-Kette erreicht ist if ($index >= count($this->middlewares)) { $this->logDebug("Ende der Middleware-Kette erreicht", $context, $index); return $context; } $middleware = $this->middlewares[$index]; $middlewareName = $this->getMiddlewareName($middleware); // Start der Middleware loggen error_log("🔄 MIDDLEWARE #{$index} START: {$middlewareName}"); // Status VOR der Middleware loggen $this->logDebug("VOR Middleware #{$index} ({$middlewareName})", $context, $index); // Next-Handler erstellen, der zur nächsten Middleware weiterleitet $next = new readonly class ($this, $index, $middlewareName) implements Next { public function __construct( private HttpMiddlewareChain $chain, private int $index, private string $middlewareName ) { } public function __invoke(MiddlewareContext $context): MiddlewareContext { // Status beim Aufruf von $next() loggen $this->chain->logDebug("NEXT aufgerufen in #{$this->index} ({$this->middlewareName})", $context, $this->index); // Zur nächsten Middleware weitergehen $resultContext = $this->chain->processMiddlewareStack($context, $this->index + 1); // Status beim Rückgabewert von $next() loggen $this->chain->logDebug("NEXT Rückgabe an #{$this->index} ({$this->middlewareName})", $resultContext, $this->index); // Detaillierte Prüfung auf verlorene Response if ($context->hasResponse() && ! $resultContext->hasResponse()) { $this->chain->logError("RESPONSE VERLOREN zwischen Middleware #{$this->index} ({$this->middlewareName}) und nachfolgenden Middlewares!"); } return $resultContext; } }; // Response-Status VOR der Middleware merken $hadResponseBefore = $context->hasResponse(); $responseBeforeStatus = $hadResponseBefore ? $context->response?->status->value : null; // Middleware mit dem Invoker ausführen $resultContext = $this->invoker->invoke($middleware, $context, $next, $this->stateManager); // Response-Status NACH der Middleware prüfen $hasResponseAfter = $resultContext->hasResponse(); $responseAfterStatus = $hasResponseAfter ? $resultContext->response?->status->value : null; // Detaillierte Response-Analyse $this->analyzeResponseChanges( $middlewareName, $index, $hadResponseBefore, $hasResponseAfter, $responseBeforeStatus, $responseAfterStatus ); // Status NACH der Middleware loggen $this->logDebug("NACH Middleware #{$index} ({$middlewareName})", $resultContext, $index); return $resultContext; } /** * Ermittelt einen lesbaren Namen für die Middleware */ private function getMiddlewareName(mixed $middleware): string { if (is_string($middleware)) { return $middleware; } if (is_object($middleware)) { $className = get_class($middleware); return substr($className, strrpos($className, '\\') + 1); } return gettype($middleware); } /** * Analysiert Response-Änderungen durch eine Middleware */ private function analyzeResponseChanges( string $middlewareName, int $index, bool $hadResponseBefore, bool $hasResponseAfter, ?int $responseBeforeStatus, ?int $responseAfterStatus ): void { // Response wurde erstellt if (! $hadResponseBefore && $hasResponseAfter) { error_log("✅ RESPONSE CREATED by Middleware #{$index} ({$middlewareName}) - Status: {$responseAfterStatus}"); return; } // Response wurde verloren if ($hadResponseBefore && ! $hasResponseAfter) { error_log("❌ RESPONSE LOST by Middleware #{$index} ({$middlewareName}) - Previous Status: {$responseBeforeStatus}"); return; } // Response-Status wurde geändert if ($hadResponseBefore && $hasResponseAfter && $responseBeforeStatus !== $responseAfterStatus) { error_log("🔄 RESPONSE MODIFIED by Middleware #{$index} ({$middlewareName}) - Status: {$responseBeforeStatus} → {$responseAfterStatus}"); return; } // Response blieb unverändert (normal) if ($hadResponseBefore && $hasResponseAfter && $responseBeforeStatus === $responseAfterStatus) { error_log("➡️ RESPONSE PASSED THROUGH Middleware #{$index} ({$middlewareName}) - Status: {$responseAfterStatus}"); return; } // Keine Response vor und nach der Middleware (normal für frühe Middlewares) // @phpstan-ignore booleanNot.alwaysTrue if (! $hadResponseBefore && ! $hasResponseAfter) { error_log("⚪ NO RESPONSE before/after Middleware #{$index} ({$middlewareName})"); } } /** * Debug-Logging mit Context-Informationen */ public function logDebug(string $message, MiddlewareContext $context, int $index): void { $responseStatus = $context->hasResponse() ? "Response✅ (Status: " . ($context->response->status->value ?? 'unknown') . ")" : "Response❌"; } /** * Info-Logging für wichtige Events */ private function logInfo(string $message, array $context = []): void { $this->logger->info('Middleware Chain: {message}', array_merge([ 'message' => $message, 'component' => 'MiddlewareChain', ], $context)); } /** * Makes processMiddlewareStack accessible to anonymous NextHandler class */ public function processMiddlewareStack(MiddlewareContext $context, int $index): MiddlewareContext { return $this->doProcessMiddlewareStack($context, $index); } /** * Error-Logging für Probleme */ public function logError(string $message, array $context = []): void { $this->logger->error('Middleware Chain ERROR: {message}', array_merge([ 'message' => $message, 'component' => 'MiddlewareChain', 'stack_trace' => debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 5), ], $context)); } }