$id]); } #[Route('/images/{filename}')] public function showImage(string $filename): FileResult { return new FileResult("/path/to/{$filename}"); } #[Route('/user/{userId}/post/{postId}')] public function showUserPost(int $userId, string $postId): JsonResult { return new JsonResult(['userId' => $userId, 'postId' => $postId]); } #[Route('/api/search/{query?}')] public function search(?string $query = null): JsonResult { return new JsonResult(['query' => $query]); } } describe('Dynamic Routing Parameter Extraction', function () { beforeEach(function () { $this->discoveryVisitor = new UnifiedRouteVisitor(); $this->routeCompiler = new RouteCompiler(); }); test('discovers routes with dynamic parameters', function () { $this->discoveryVisitor->onScanStart(); // UnifiedRouteVisitor benötigt ClassName und FilePath Value Objects $className = \App\Framework\Core\ValueObjects\ClassName::fromString(TestDynamicController::class); $filePath = \App\Framework\Filesystem\FilePath::create('test-file.php'); $reflection = new \App\Framework\Reflection\WrappedReflectionClass(new \ReflectionClass(TestDynamicController::class)); $this->discoveryVisitor->visitClass($className, $filePath, $reflection); $routes = $this->discoveryVisitor->getResults(); expect($routes)->toHaveCount(4); // Test route with single parameter $singleParamRoute = null; foreach ($routes as $route) { if ($route['path'] === '/test/{id}') { $singleParamRoute = $route; break; } } expect($singleParamRoute)->not->toBeNull(); expect($singleParamRoute['controller'])->toBe(TestDynamicController::class); expect($singleParamRoute['action'])->toBe('showById'); // Test image route (the problematic one) $imageRoute = null; foreach ($routes as $route) { if ($route['path'] === '/images/{filename}') { $imageRoute = $route; break; } } expect($imageRoute)->not->toBeNull(); expect($imageRoute['controller'])->toBe(TestDynamicController::class); expect($imageRoute['action'])->toBe('showImage'); // Test route with multiple parameters $multiParamRoute = null; foreach ($routes as $route) { if ($route['path'] === '/user/{userId}/post/{postId}') { $multiParamRoute = $route; break; } } expect($multiParamRoute)->not->toBeNull(); }); test('extracts parameter information correctly', function () { $this->discoveryVisitor->onScanStart(); // UnifiedRouteVisitor benötigt ClassName und FilePath Value Objects $className = \App\Framework\Core\ValueObjects\ClassName::fromString(TestDynamicController::class); $filePath = \App\Framework\Filesystem\FilePath::create('test-file.php'); $reflection = new \App\Framework\Reflection\WrappedReflectionClass(new \ReflectionClass(TestDynamicController::class)); $this->discoveryVisitor->visitClass($className, $filePath, $reflection); $routes = $this->discoveryVisitor->getResults(); // Suche die Image Route $imageRoute = null; foreach ($routes as $route) { if ($route['path'] === '/images/{filename}') { $imageRoute = $route; break; } } // Prüfe ob Parameter-Informationen vorhanden sind if (isset($imageRoute['parameters'])) { expect($imageRoute['parameters'])->toBeArray(); // Debug: Zeige Parameter-Struktur error_log('Image Route Parameters: ' . json_encode($imageRoute['parameters'])); // Suche nach filename Parameter $filenameParam = null; foreach ($imageRoute['parameters'] as $param) { if ($param['name'] === 'filename') { $filenameParam = $param; break; } } if ($filenameParam) { expect($filenameParam['name'])->toBe('filename'); expect($filenameParam['type'])->toBe('string'); // Das ist wahrscheinlich null! expect($filenameParam['isBuiltin'])->toBeTrue(); } else { error_log('Filename parameter not found in parameters array'); } } else { error_log('No parameters found in route array'); expect($imageRoute)->toHaveKey('parameters'); // Dieser Test wird wahrscheinlich fehlschlagen } }); test('compiles dynamic routes with parameter names', function () { $this->discoveryVisitor->onScanStart(); // UnifiedRouteVisitor benötigt ClassName und FilePath Value Objects $className = \App\Framework\Core\ValueObjects\ClassName::fromString(TestDynamicController::class); $filePath = \App\Framework\Filesystem\FilePath::create('test-file.php'); $reflection = new \App\Framework\Reflection\WrappedReflectionClass(new \ReflectionClass(TestDynamicController::class)); $this->discoveryVisitor->visitClass($className, $filePath, $reflection); $routes = $this->discoveryVisitor->getResults(); $compiledRoutes = $this->routeCompiler->compile($routes); // Prüfe GET routes expect($compiledRoutes)->toHaveKey('GET'); expect($compiledRoutes['GET'])->toHaveKey('dynamic'); $dynamicRoutes = $compiledRoutes['GET']['dynamic']; expect($dynamicRoutes)->toBeArray(); expect(count($dynamicRoutes))->toBeGreaterThan(0); // Suche Image Route in compilierten Routes $imageRoute = null; foreach ($dynamicRoutes as $route) { if ($route instanceof DynamicRoute && $route->path === '/images/{filename}') { $imageRoute = $route; break; } } expect($imageRoute)->not->toBeNull(); expect($imageRoute)->toBeInstanceOf(DynamicRoute::class); // Prüfe Parameter Names expect($imageRoute->paramNames)->toBe(['filename']); // Prüfe compiled regex expect($imageRoute->regex)->toBeString(); expect($imageRoute->regex)->toMatch('/^~.*\/images\/.*\$~/'); // Regex für /images/{filename} // Prüfe Parameter Details error_log('Compiled Image Route Parameters: ' . json_encode($imageRoute->parameters)); if (! empty($imageRoute->parameters)) { $filenameParam = null; foreach ($imageRoute->parameters as $param) { if ($param['name'] === 'filename') { $filenameParam = $param; break; } } if ($filenameParam) { expect($filenameParam['type'])->not->toBeNull(); // Das sollte nicht null sein! expect($filenameParam['type'])->toBe('string'); } } }); test('handles multiple parameter types correctly', function () { $this->discoveryVisitor->onScanStart(); // UnifiedRouteVisitor benötigt ClassName und FilePath Value Objects $className = \App\Framework\Core\ValueObjects\ClassName::fromString(TestDynamicController::class); $filePath = \App\Framework\Filesystem\FilePath::create('test-file.php'); $reflection = new \App\Framework\Reflection\WrappedReflectionClass(new \ReflectionClass(TestDynamicController::class)); $this->discoveryVisitor->visitClass($className, $filePath, $reflection); $routes = $this->discoveryVisitor->getResults(); // Test verschiedene Parameter-Typen $userPostRoute = null; foreach ($routes as $route) { if ($route['path'] === '/user/{userId}/post/{postId}') { $userPostRoute = $route; break; } } if (isset($userPostRoute['parameters'])) { $parameters = $userPostRoute['parameters']; // Suche nach userId (int) und postId (string) Parametern $userIdParam = null; $postIdParam = null; foreach ($parameters as $param) { if ($param['name'] === 'userId') { $userIdParam = $param; } elseif ($param['name'] === 'postId') { $postIdParam = $param; } } if ($userIdParam) { expect($userIdParam['type'])->toBe('int'); expect($userIdParam['isBuiltin'])->toBeTrue(); } if ($postIdParam) { expect($postIdParam['type'])->toBe('string'); expect($postIdParam['isBuiltin'])->toBeTrue(); } } }); test('handles optional parameters correctly', function () { $this->discoveryVisitor->onScanStart(); // UnifiedRouteVisitor benötigt ClassName und FilePath Value Objects $className = \App\Framework\Core\ValueObjects\ClassName::fromString(TestDynamicController::class); $filePath = \App\Framework\Filesystem\FilePath::create('test-file.php'); $reflection = new \App\Framework\Reflection\WrappedReflectionClass(new \ReflectionClass(TestDynamicController::class)); $this->discoveryVisitor->visitClass($className, $filePath, $reflection); $routes = $this->discoveryVisitor->getResults(); // Test optionale Parameter $searchRoute = null; foreach ($routes as $route) { if ($route['path'] === '/api/search/{query?}') { $searchRoute = $route; break; } } if (isset($searchRoute['parameters'])) { $parameters = $searchRoute['parameters']; $queryParam = null; foreach ($parameters as $param) { if ($param['name'] === 'query') { $queryParam = $param; break; } } if ($queryParam) { expect($queryParam['type'])->toBe('string'); expect($queryParam['isOptional'])->toBeTrue(); expect($queryParam['hasDefault'])->toBeTrue(); expect($queryParam['default'])->toBeNull(); } } }); }); describe('Dynamic Route Parameter Processing Debug', function () { test('shows current parameter extraction workflow', function () { $discoveryVisitor = new UnifiedRouteVisitor(); $discoveryVisitor->onScanStart(); $className = \App\Framework\Core\ValueObjects\ClassName::fromString(TestDynamicController::class); $filePath = \App\Framework\Filesystem\FilePath::create('test-file.php'); $reflection = new \App\Framework\Reflection\WrappedReflectionClass(new \ReflectionClass(TestDynamicController::class)); $discoveryVisitor->visitClass($className, $filePath, $reflection); $routes = $discoveryVisitor->getResults(); // Debug: Zeige alle gefundenen Routes error_log('=== All discovered routes ==='); foreach ($routes as $i => $route) { error_log("Route {$i}: " . json_encode([ 'path' => $route['path'], 'controller' => $route['controller'], 'action' => $route['action'], 'has_parameters' => isset($route['parameters']), 'parameter_count' => isset($route['parameters']) ? count($route['parameters']) : 0, ])); if (isset($route['parameters'])) { foreach ($route['parameters'] as $j => $param) { error_log(" Parameter {$j}: " . json_encode($param)); } } } // Debug: Zeige RouteDiscoveryVisitor interne Struktur error_log('=== RouteDiscoveryVisitor internals ==='); // Wir können nicht auf private Properties zugreifen, aber wir können testen expect(true)->toBeTrue(); // Placeholder - der Test ist für Debug-Zwecke }); });