model ? $this->resolveTemplate($result->model) : $result->template, metaData: $result->metaData, data: $result->model ? get_object_vars($result->model) + $result->data : $result->data, layout: '', slots: $result->slots, isPartial: $this->isSpaRequest(), ); } public function respond(Response|ActionResult $result): Response { if ($result instanceof Response) { return $result; } if ($this->isSpaRequest() && $result instanceof ViewResult) { return $this->createSpaResponse($result); } return match(true) { $result instanceof ViewResult => new HttpResponse( status: $result->status, body: $this->renderTemplate($this->getContext($result)) ), $result instanceof JsonResult => new JsonResponse( body : $result->data, status : $result->status, ), $result instanceof Redirect => new RedirectResponse(new Uri($result->target)), $result instanceof SseResult => new SseResponse($result, $result->callback), $result instanceof WebSocketResult => $this->createWebSocketResponse($result), $result instanceof FileResult => new HttpResponse( #headers: new Headers()->with('Content-Type', $result->mimeType), body: $result->filePath ), default => throw new \RuntimeException('Unbekanntes Ergebnisobjekt: ' . get_class($result)), }; } private function createWebSocketResponse(WebSocketResult $result): WebSocketResponse { // WebSocket-Key aus Request-Headers abrufen $websocketKey = $_SERVER['HTTP_SEC_WEBSOCKET_KEY'] ?? ''; return new WebSocketResponse($result, $websocketKey); } private function renderTemplate(RenderContext $context): string { // Force log to file to ensure visibility file_put_contents('/tmp/debug.log', "RouteResponder::renderTemplate STARTED for template: " . $context->template . "\n", FILE_APPEND); file_put_contents('/tmp/debug.log', "Template data keys: " . implode(', ', array_keys($context->data)) . "\n", FILE_APPEND); error_log("RouteResponder::renderTemplate STARTED for template: " . $context->template); error_log("Template renderer class: " . get_class($this->templateRenderer)); // Log the class before calling render error_log("RouteResponder: About to call render on class: " . get_class($this->templateRenderer)); file_put_contents('/tmp/debug.log', "About to call render on: " . get_class($this->templateRenderer) . "\n", FILE_APPEND); $result = $this->templateRenderer->render($context); file_put_contents('/tmp/debug.log', "RouteResponder::renderTemplate COMPLETED, result length: " . strlen($result) . "\n", FILE_APPEND); file_put_contents('/tmp/debug.log', "Result contains {{: " . (str_contains($result, '{{') ? 'YES' : 'NO') . "\n", FILE_APPEND); error_log("RouteResponder::renderTemplate COMPLETED, result length: " . strlen($result)); error_log("Result contains {{ model.title }}: " . (str_contains($result, '{{ model.title }}') ? 'YES' : 'NO')); return $result; } private function isSpaRequest(): bool { $xmlHttpRequest = $this->request->headers->getFirst('X-Requested-With'); $hasSpaRequest = $this->request->headers->has('X-SPA-Request'); // Fallback: Direkt aus $_SERVER lesen (für Nginx FastCGI) if (empty($xmlHttpRequest)) { $xmlHttpRequest = $_SERVER['HTTP_X_REQUESTED_WITH'] ?? ''; } if (! $hasSpaRequest) { $hasSpaRequest = ! empty($_SERVER['HTTP_X_SPA_REQUEST']); } // DEBUG: Log SPA detection error_log("SPA Detection - xmlHttpRequest: '{$xmlHttpRequest}', hasSpaRequest: " . ($hasSpaRequest ? 'true' : 'false')); error_log("SPA Detection - Headers: " . json_encode($this->request->headers->toArray())); error_log("SPA Detection - SERVER vars: X-Requested-With='" . ($_SERVER['HTTP_X_REQUESTED_WITH'] ?? 'null') . "', X-SPA-Request='" . ($_SERVER['HTTP_X_SPA_REQUEST'] ?? 'null') . "'"); $isSpa = $xmlHttpRequest === 'XMLHttpRequest' && $hasSpaRequest; error_log("SPA Detection - Final result: " . ($isSpa ? 'TRUE (returning JSON)' : 'FALSE (returning HTML)')); return $isSpa; } private function createSpaResponse(ViewResult $result): JsonResponse { $context = $this->getContext($result); return new JsonResponse([ 'html' => $this->templateRenderer->renderPartial($context), 'title' => $result->metaData->title ?? '', 'meta' => [ 'description' => $result->metaData->description ?? '', // Add other meta data as needed ], ]); } private function resolveTemplate(?object $model): string { if ($model === null) { return 'test'; } $ref = new \ReflectionClass($model); $attrs = $ref->getAttributes(Template::class); if ($attrs === []) { return 'test'; #throw new \RuntimeException("Fehlendes #[Template] Attribut in {$ref->getName()}"); } /** @var Template $attr */ $attr = $attrs[0]->newInstance(); return $attr->path; } }