container = $container; $this->request = new Request(); $this->response = new Response(); $this->session = new Session(); $this->router = new Router(); } /** * Boot and run the application. */ public function run(): void { try { // Match request to route $routeInfo = $this->router->resolve($this->request); $callback = $routeInfo['callback']; $middlewares = $routeInfo['middleware']; $params = $routeInfo['params']; // Inject matched route parameters into Request $this->request->setRouteParams($params); // Run Middleware Chain $this->executeMiddlewareChain($middlewares, function() use ($callback) { // Execute Route action if (is_callable($callback)) { $response = $callback($this->request, $this->response); } else { [$controllerClass, $method] = $callback; $controller = $this->container->get($controllerClass); $response = $controller->$method($this->request, $this->response); } // Auto-output string responses as HTML if (is_string($response)) { $this->response->html($response); } }); } catch (Throwable $e) { $this->handleException($e); } } /** * Executes the array of middlewares sequentially before firing destination action. */ private function executeMiddlewareChain(array $middlewares, callable $destination): void { $runner = function (int $index) use ($middlewares, $destination, &$runner) { if ($index >= count($middlewares)) { $destination(); return; } $middlewareClass = $middlewares[$index]; /** @var MiddlewareInterface $middleware */ $middleware = $this->container->get($middlewareClass); $middleware->handle($this->request, $this->response, function() use ($runner, $index) { $runner($index + 1); }); }; $runner(0); } /** * Global exception handler. */ private function handleException(Throwable $e): void { $code = $e->getCode(); if ($code < 100 || $code > 599) { $code = 500; } // Return API error JSON for API routes or JSON requests if ($this->request->isJson() || str_starts_with($this->request->getPath(), '/api')) { $this->response->json([ 'success' => false, 'error' => $e->getMessage(), 'code' => $code ], $code); return; } // Render error templates for Web requests $viewPath = __DIR__ . "/../../resources/views/errors/{$code}.php"; if (!file_exists($viewPath)) { $viewPath = __DIR__ . '/../../resources/views/errors/500.php'; } if (file_exists($viewPath)) { ob_start(); $message = $e->getMessage(); include $viewPath; $content = ob_get_clean(); $this->response->html($content, $code); } else { $this->response->html("

Error {$code}

{$e->getMessage()}

", $code); } } }