*/ public static function pathProvider(): array { return [ 'the export route wins over the code lookup' => ['/api/travels/export', 'api_travel_export'], // Regression: a dateCode requirement of `.+` instead of `[^/]+` widens the segment // across slashes, and this four-segment path is then swallowed by byCode(). 'availability is not shadowed by the code lookup' => [ '/api/travels/12794/225444/2026-12-30/availability', 'api_travel_hotel_availability', ], 'numeric ids still reach byId' => ['/api/travels/12794/225444', 'api_travel_single_id'], 'a date code alone reaches byCode' => ['/api/travels/SBW261226', 'api_travel_single_code'], 'a date and hotel code reach byCode' => ['/api/travels/SBW261226/H1', 'api_travel_single_code'], 'the mapping index is untouched' => ['/api/travels', 'api_travel_mapping'], ]; } #[DataProvider('pathProvider')] public function testPathResolvesToTheIntendedRoute(string $path, string $expected): void { $matcher = new UrlMatcher($this->collection(), new RequestContext('', 'GET')); self::assertSame($expected, $matcher->match($path)['_route']); } public function testDateCodeNeverSpansSlashes(): void { $route = $this->collection()->get('api_travel_single_code'); self::assertNotNull($route); self::assertStringNotContainsString( '.+', (string) $route->getRequirement('dateCode'), 'dateCode must keep the single-segment class, or it shadows the routes below it', ); } /** * Reflects the controller's own #[Route] attributes, in declaration order. */ private function collection(): RouteCollection { $class = new \ReflectionClass(TravelController::class); $prefix = ''; foreach ($class->getAttributes(RouteAttribute::class) as $attribute) { $prefix = (string) $attribute->newInstance()->path; } $collection = new RouteCollection(); foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) { foreach ($method->getAttributes(RouteAttribute::class) as $attribute) { $route = $attribute->newInstance(); $collection->add((string) $route->name, new Route( $prefix.$route->path, $route->defaults, $route->requirements, [], '', [], $route->methods, )); } } return $collection; } }