router = $router; } public function scan(string $scanPath): void { $this->scanControllers($scanPath); $this->registerRoutes(); } private function scanControllers(string $path): void { if (!is_dir($path)) { return; } $files = glob($path . '/**/*.php'); foreach ($files as $file) { $this->scanControllerFile($file); } } private function scanControllerFile(string $file): void { $className = $this->getClassNameFromFile($file); if ($className === null || class_exists($className) === false) { return; } try { $reflection = new ReflectionClass($className); // 检查是否有Controller注解 $controllerAttributes = $reflection->getAttributes(Controller::class); if (empty($controllerAttributes)) { return; } $controllerAttribute = $controllerAttributes[0]->newInstance(); $prefix = $controllerAttribute->prefix; // 扫描方法上的路由注解 $this->scanControllerMethods($className, $reflection, $prefix); } catch (ReflectionException $e) { error_log("Failed to scan controller $className: " . $e->getMessage()); } } private function scanControllerMethods(string $className, ReflectionClass $reflection, string $prefix): void { $methods = $reflection->getMethods(ReflectionMethod::IS_PUBLIC); foreach ($methods as $method) { if ($method->getName() === '__construct') { continue; } $this->scanMethodRoutes($className, $method, $prefix); } } private function scanMethodRoutes(string $className, ReflectionMethod $method, string $prefix): void { // 扫描GetRoute注解 $getRouteAttributes = $method->getAttributes(GetRoute::class); foreach ($getRouteAttributes as $attribute) { $routeAttribute = $attribute->newInstance(); $path = $prefix . $routeAttribute->path; $this->controllers[] = [ 'method' => 'GET', 'path' => $path, 'controller' => $className, 'action' => $method->getName() ]; } // 扫描PostRoute注解 $postRouteAttributes = $method->getAttributes(PostRoute::class); foreach ($postRouteAttributes as $attribute) { $routeAttribute = $attribute->newInstance(); $path = $prefix . $routeAttribute->path; $this->controllers[] = [ 'method' => 'POST', 'path' => $path, 'controller' => $className, 'action' => $method->getName() ]; } } private function registerRoutes(): void { foreach ($this->controllers as $route) { $handler = [$route['controller'], $route['action']]; switch ($route['method']) { case 'GET': $this->router->get($route['path'], $handler); break; case 'POST': $this->router->post($route['path'], $handler); break; case 'PUT': $this->router->put($route['path'], $handler); break; case 'DELETE': $this->router->delete($route['path'], $handler); break; } } } private function getClassNameFromFile(string $file): ?string { $content = file_get_contents($file); if (preg_match('/namespace\s+([^;]+);/', $content, $matches)) { $namespace = trim($matches[1]); $className = basename($file, '.php'); return $namespace . '\\' . $className; } return null; } public function getRoutes(): array { return $this->controllers; } }