routes = []; $this->routesByPath = []; $this->routesByController = []; $this->routesSorted = false; } public function onIncrementalScanStart(): void { // Bei inkrementellem Scan behalten wir bestehende Routen // markieren sie aber als unsortiert $this->routesSorted = false; } public function onIncrementalScanComplete(): void { // Nach inkrementellem Scan sortieren $this->sortRoutes(); } public function visitClass(string $className, string $filePath): void { if (!class_exists($className)) { return; } try { $reflection = new \ReflectionClass($className); // Prüfe, ob die Klasse überhaupt Methoden mit Route-Attributen hat $hasRouteMethods = false; foreach ($reflection->getMethods() as $method) { if (!empty($method->getAttributes(RouteAttribute::class))) { $hasRouteMethods = true; break; } } if (!$hasRouteMethods) { return; } // Nur Methoden mit Route-Attribut verarbeiten foreach ($reflection->getMethods() as $method) { $routeAttributes = $method->getAttributes(RouteAttribute::class); foreach ($routeAttributes as $routeAttr) { /** @var RouteAttribute $route */ $route = $routeAttr->newInstance(); $routeData = [ 'path' => $route->path, 'method' => $route->method, 'controller' => $className, 'action' => $method->getName(), 'middleware' => $route->middleware ?? [], 'priority' => $route->priority ?? 0 ]; $this->routes[] = $routeData; // Erstelle Indizes für schnelleren Zugriff $path = strtolower($route->path); $httpMethod = strtoupper($route->method->value); $key = "{$httpMethod}:{$path}"; if (!isset($this->routesByPath[$key])) { $this->routesByPath[$key] = []; } $this->routesByPath[$key][] = $routeData; if (!isset($this->routesByController[$className])) { $this->routesByController[$className] = []; } $this->routesByController[$className][] = $routeData; } } } catch (\Throwable $e) { error_log("Fehler beim Verarbeiten der Routen für {$className}: " . $e->getMessage()); } } public function onScanComplete(): void { // Sortiere Routen nach Priorität $this->sortRoutes(); } /** * Sortiert die Routen nach Priorität (absteigend) */ private function sortRoutes(): void { if ($this->routesSorted) { return; } // Sortiere Hauptrouten-Array usort($this->routes, function($a, $b) { return $b['priority'] <=> $a['priority']; }); // Sortiere Indizes foreach ($this->routesByPath as $key => $routes) { usort($this->routesByPath[$key], function($a, $b) { return $b['priority'] <=> $a['priority']; }); } foreach ($this->routesByController as $className => $routes) { usort($this->routesByController[$className], function($a, $b) { return $b['priority'] <=> $a['priority']; }); } $this->routesSorted = true; } public function loadFromCache(Cache $cache): void { $cacheItem = $cache->get($this->getCacheKey()); if ($cacheItem->isHit) { if (is_array($cacheItem->value) && isset($cacheItem->value['routes'], $cacheItem->value['byPath'], $cacheItem->value['byController'])) { $this->routes = $cacheItem->value['routes']; $this->routesByPath = $cacheItem->value['byPath']; $this->routesByController = $cacheItem->value['byController']; $this->routesSorted = true; } } } public function getCacheKey(): string { return 'routes'; } public function getCacheableData(): mixed { // Stelle sicher, dass die Routen sortiert sind, bevor wir sie cachen if (!$this->routesSorted) { $this->sortRoutes(); } return [ 'routes' => $this->routes, 'byPath' => $this->routesByPath, 'byController' => $this->routesByController ]; } /** * Gibt alle gefundenen Routen zurück */ public function getRoutes(): array { if (!$this->routesSorted) { $this->sortRoutes(); } return $this->routes; } /** * Findet eine Route für einen bestimmten Pfad und HTTP-Methode */ public function findRoute(string $path, string $httpMethod): ?array { $path = strtolower(rtrim($path, '/')); $httpMethod = strtoupper($httpMethod); $key = "{$httpMethod}:{$path}"; // Direkte Übereinstimmung if (isset($this->routesByPath[$key]) && !empty($this->routesByPath[$key])) { return $this->routesByPath[$key][0]; // Höchste Priorität zuerst } // Prüfe auf parametrisierte Routen, wenn keine direkte Übereinstimmung foreach ($this->routesByPath as $routeKey => $routes) { list($routeMethod, $routePath) = explode(':', $routeKey, 2); if ($routeMethod !== $httpMethod) { continue; } // Konvertiere Route-Pfad in ein reguläres Ausdrucksmuster $pattern = $this->routePathToRegex($routePath); if (preg_match($pattern, $path)) { return $routes[0]; // Höchste Priorität zuerst } } return null; } /** * Konvertiert einen Route-Pfad in ein reguläres Ausdrucksmuster */ private function routePathToRegex(string $routePath): string { // Ersetze Platzhalter wie {id} durch Regex-Gruppen $pattern = preg_replace('/\{([^}]+)\}/', '([^/]+)', $routePath); return '/^' . str_replace('/', '\/', $pattern) . '$/'; } /** * Gibt alle Routen für einen bestimmten Controller zurück */ public function getRoutesForController(string $controllerClass): array { return $this->routesByController[$controllerClass] ?? []; } }