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 = new $controllerClass(); call_user_func_array([$controller, $method], $params); } }