container = $container; } public function get(string $path, array|callable $handler): self { return $this->add('GET', $path, $handler); } public function post(string $path, array|callable $handler): self { return $this->add('POST', $path, $handler); } public function put(string $path, array|callable $handler): self { return $this->add('PUT', $path, $handler); } public function delete(string $path, array|callable $handler): self { return $this->add('DELETE', $path, $handler); } public function add(string $method, string $path, array|callable $handler): self { $this->routes[] = [ 'method' => $method, 'path' => $path, 'handler' => $handler, ]; return $this; } public function dispatch(string $uri, string $method): void { foreach ($this->routes as $route) { if ($route['method'] !== $method) { continue; } $pattern = $this->convertToRegex($route['path']); if (preg_match($pattern, $uri, $matches)) { array_shift($matches); $this->handle($route['handler'], $matches); return; } } http_response_code(404); echo '404 Not Found'; } protected function convertToRegex(string $path): string { // Support {name:pattern} syntax for custom patterns (e.g., {path:.*}) $pattern = preg_replace_callback('/\{([a-zA-Z]+):([^}]+)\}/', function ($matches) { return '(' . $matches[2] . ')'; }, $path); // Support simple {name} syntax $pattern = preg_replace('/\{([a-zA-Z]+)\}/', '([^/]+)', $pattern); return '#^' . $pattern . '$#'; } protected function handle(array|callable $handler, array $params): void { if (is_callable($handler)) { call_user_func_array($handler, $params); return; } [$controllerClass, $method] = $handler; $controller = $this->resolveController($controllerClass); /** @var callable $callback */ $callback = [$controller, $method]; call_user_func_array($callback, $params); } /** * Resolve a controller using the container or direct instantiation. * * @param class-string $controllerClass */ protected function resolveController(string $controllerClass): Controller { // Use container if available (with autowiring) if ($this->container !== null) { return $this->container->get($controllerClass); } // Fallback to direct instantiation return new $controllerClass(); } }