diff --git a/UPGRADE-6.4.md b/UPGRADE-6.4.md index 4bb687b19ac14..a534b3beeeb22 100644 --- a/UPGRADE-6.4.md +++ b/UPGRADE-6.4.md @@ -64,6 +64,8 @@ Routing ------- * Add native return type to `AnnotationClassLoader::setResolver()` + * Deprecate Doctrine annotations support in favor of native attributes + * Change the constructor signature of `AnnotationClassLoader` to `__construct(?string $env = null)`, passing an annotation reader as first argument is deprecated Security -------- diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 846639ebda2ac..696cf9093d6be 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -144,6 +144,7 @@ use Symfony\Component\RateLimiter\Storage\CacheStorage; use Symfony\Component\RemoteEvent\Attribute\AsRemoteEventConsumer; use Symfony\Component\RemoteEvent\RemoteEvent; +use Symfony\Component\Routing\Loader\AnnotationClassLoader; use Symfony\Component\Routing\Loader\Psr4DirectoryLoader; use Symfony\Component\Scheduler\Attribute\AsSchedule; use Symfony\Component\Scheduler\Messenger\SchedulerTransportFactory; @@ -1179,6 +1180,13 @@ private function registerRouterConfiguration(array $config, ContainerBuilder $co if (!class_exists(Psr4DirectoryLoader::class)) { $container->removeDefinition('routing.loader.psr4'); } + + if ($this->isInitializedConfigEnabled('annotations') && (new \ReflectionClass(AnnotationClassLoader::class))->hasProperty('reader')) { + $container->getDefinition('routing.loader.annotation')->setArguments([ + new Reference('annotation_reader'), + '%kernel.environment%', + ]); + } } private function registerSessionConfiguration(array $config, ContainerBuilder $container, PhpFileLoader $loader): void diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.php index 86a7cf874629c..10745b225e684 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.php @@ -94,7 +94,6 @@ ->set('routing.loader.annotation', AnnotatedRouteControllerLoader::class) ->args([ - service('annotation_reader')->nullOnInvalid(), '%kernel.environment%', ]) ->tag('routing.loader', ['priority' => -10]) diff --git a/src/Symfony/Bundle/FrameworkBundle/composer.json b/src/Symfony/Bundle/FrameworkBundle/composer.json index b0fac1a188e58..ba0096cfc52a8 100644 --- a/src/Symfony/Bundle/FrameworkBundle/composer.json +++ b/src/Symfony/Bundle/FrameworkBundle/composer.json @@ -30,7 +30,7 @@ "symfony/polyfill-mbstring": "~1.0", "symfony/filesystem": "^5.4|^6.0|^7.0", "symfony/finder": "^5.4|^6.0|^7.0", - "symfony/routing": "^6.1|^7.0" + "symfony/routing": "^6.4|^7.0" }, "require-dev": { "doctrine/annotations": "^1.13.1|^2", diff --git a/src/Symfony/Component/Routing/CHANGELOG.md b/src/Symfony/Component/Routing/CHANGELOG.md index 570e09421ab60..c0685780955a3 100644 --- a/src/Symfony/Component/Routing/CHANGELOG.md +++ b/src/Symfony/Component/Routing/CHANGELOG.md @@ -6,6 +6,8 @@ CHANGELOG * Add FQCN and FQCN::method aliases for routes loaded from attributes/annotations when applicable * Add native return type to `AnnotationClassLoader::setResolver()` + * Deprecate Doctrine annotations support in favor of native attributes + * Change the constructor signature of `AnnotationClassLoader` to `__construct(?string $env = null)`, passing an annotation reader as first argument is deprecated 6.2 --- diff --git a/src/Symfony/Component/Routing/Loader/AnnotationClassLoader.php b/src/Symfony/Component/Routing/Loader/AnnotationClassLoader.php index ca12eb8399c82..1af0b1b1f36a1 100644 --- a/src/Symfony/Component/Routing/Loader/AnnotationClassLoader.php +++ b/src/Symfony/Component/Routing/Loader/AnnotationClassLoader.php @@ -26,33 +26,14 @@ * time, this method should define some PHP callable to be called for the route * (a controller in MVC speak). * - * The @Route annotation can be set on the class (for global parameters), + * The #[Route] attribute can be set on the class (for global parameters), * and on each method. * - * The @Route annotation main value is the route path. The annotation also + * The #[Route] attribute main value is the route path. The attribute also * recognizes several parameters: requirements, options, defaults, schemes, * methods, host, and name. The name parameter is mandatory. * Here is an example of how you should be able to use it: - * /** - * * @Route("/Blog") - * * / - * class Blog - * { - * /** - * * @Route("/", name="blog_index") - * * / - * public function index() - * { - * } - * /** - * * @Route("/{id}", name="blog_post", requirements = {"id" = "\d+"}) - * * / - * public function show() - * { - * } - * } * - * On PHP 8, the annotation class can be used as an attribute as well: * #[Route('/Blog')] * class Blog * { @@ -71,7 +52,16 @@ */ abstract class AnnotationClassLoader implements LoaderInterface { + /** + * @var Reader|null + * + * @deprecated in Symfony 6.4, this property will be removed in Symfony 7. + */ protected $reader; + + /** + * @var string|null + */ protected $env; /** @@ -84,10 +74,27 @@ abstract class AnnotationClassLoader implements LoaderInterface */ protected $defaultRouteIndex = 0; - public function __construct(Reader $reader = null, string $env = null) + private bool $hasDeprecatedAnnotations = false; + + /** + * @param string|null $env + */ + public function __construct($env = null) { - $this->reader = $reader; - $this->env = $env; + if ($env instanceof Reader || null === $env && \func_num_args() > 1 && null !== func_get_arg(1)) { + trigger_deprecation('symfony/routing', '6.4', 'Passing an instance of "%s" as first and the environment as second argument to "%s" is deprecated. Pass the environment as first argument instead.', Reader::class, __METHOD__); + + $this->reader = $env; + $env = \func_num_args() > 1 ? func_get_arg(1) : null; + } + + if (\is_string($env) || null === $env) { + $this->env = $env; + } elseif ($env instanceof \Stringable || \is_scalar($env)) { + $this->env = (string) $env; + } else { + throw new \TypeError(__METHOD__.sprintf(': Parameter $env was expected to be a string or null, "%s" given.', get_debug_type($env))); + } } /** @@ -116,43 +123,48 @@ public function load(mixed $class, string $type = null): RouteCollection throw new \InvalidArgumentException(sprintf('Annotations from class "%s" cannot be read as it is abstract.', $class->getName())); } - $globals = $this->getGlobals($class); + $this->hasDeprecatedAnnotations = false; - $collection = new RouteCollection(); - $collection->addResource(new FileResource($class->getFileName())); - - if ($globals['env'] && $this->env !== $globals['env']) { - return $collection; - } + try { + $globals = $this->getGlobals($class); + $collection = new RouteCollection(); + $collection->addResource(new FileResource($class->getFileName())); + if ($globals['env'] && $this->env !== $globals['env']) { + return $collection; + } + $fqcnAlias = false; + foreach ($class->getMethods() as $method) { + $this->defaultRouteIndex = 0; + $routeNamesBefore = array_keys($collection->all()); + foreach ($this->getAnnotations($method) as $annot) { + $this->addRoute($collection, $annot, $globals, $class, $method); + if ('__invoke' === $method->name) { + $fqcnAlias = true; + } + } - $fqcnAlias = false; - foreach ($class->getMethods() as $method) { - $this->defaultRouteIndex = 0; - $routeNamesBefore = array_keys($collection->all()); - foreach ($this->getAnnotations($method) as $annot) { - $this->addRoute($collection, $annot, $globals, $class, $method); - if ('__invoke' === $method->name) { + if (1 === $collection->count() - \count($routeNamesBefore)) { + $newRouteName = current(array_diff(array_keys($collection->all()), $routeNamesBefore)); + $collection->addAlias(sprintf('%s::%s', $class->name, $method->name), $newRouteName); + } + } + if (0 === $collection->count() && $class->hasMethod('__invoke')) { + $globals = $this->resetGlobals(); + foreach ($this->getAnnotations($class) as $annot) { + $this->addRoute($collection, $annot, $globals, $class, $class->getMethod('__invoke')); $fqcnAlias = true; } } - - if (1 === $collection->count() - \count($routeNamesBefore)) { - $newRouteName = current(array_diff(array_keys($collection->all()), $routeNamesBefore)); - $collection->addAlias(sprintf('%s::%s', $class->name, $method->name), $newRouteName); + if ($fqcnAlias && 1 === $collection->count()) { + $collection->addAlias($class->name, $invokeRouteName = key($collection->all())); + $collection->addAlias(sprintf('%s::__invoke', $class->name), $invokeRouteName); } - } - if (0 === $collection->count() && $class->hasMethod('__invoke')) { - $globals = $this->resetGlobals(); - foreach ($this->getAnnotations($class) as $annot) { - $this->addRoute($collection, $annot, $globals, $class, $class->getMethod('__invoke')); - $fqcnAlias = true; + if ($this->hasDeprecatedAnnotations) { + trigger_deprecation('symfony/routing', '6.4', 'Class "%s" uses Doctrine Annotations to configure routes, which is deprecated. Use PHP attributes instead.', $class->getName()); } - } - - if ($fqcnAlias && 1 === $collection->count()) { - $collection->addAlias($class->name, $invokeRouteName = key($collection->all())); - $collection->addAlias(sprintf('%s::__invoke', $class->name), $invokeRouteName); + } finally { + $this->hasDeprecatedAnnotations = false; } return $collection; @@ -279,7 +291,7 @@ protected function getDefaultRouteName(\ReflectionClass $class, \ReflectionMetho } /** - * @return array + * @return array */ protected function getGlobals(\ReflectionClass $class) { @@ -289,8 +301,8 @@ protected function getGlobals(\ReflectionClass $class) if ($attribute = $class->getAttributes($this->routeAnnotationClass, \ReflectionAttribute::IS_INSTANCEOF)[0] ?? null) { $annot = $attribute->newInstance(); } - if (!$annot && $this->reader) { - $annot = $this->reader->getClassAnnotation($class, $this->routeAnnotationClass); + if (!$annot && $annot = $this->reader?->getClassAnnotation($class, $this->routeAnnotationClass)) { + $this->hasDeprecatedAnnotations = true; } if ($annot) { @@ -377,11 +389,9 @@ protected function createRoute(string $path, array $defaults, array $requirement abstract protected function configureRoute(Route $route, \ReflectionClass $class, \ReflectionMethod $method, object $annot); /** - * @param \ReflectionClass|\ReflectionMethod $reflection - * * @return iterable */ - private function getAnnotations(object $reflection): iterable + private function getAnnotations(\ReflectionClass|\ReflectionMethod $reflection): iterable { foreach ($reflection->getAttributes($this->routeAnnotationClass, \ReflectionAttribute::IS_INSTANCEOF) as $attribute) { yield $attribute->newInstance(); @@ -397,6 +407,8 @@ private function getAnnotations(object $reflection): iterable foreach ($annotations as $annotation) { if ($annotation instanceof $this->routeAnnotationClass) { + $this->hasDeprecatedAnnotations = true; + yield $annotation; } } diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/AnnotatedClasses/AbstractClass.php b/src/Symfony/Component/Routing/Tests/Fixtures/AnnotatedClasses/AbstractClass.php index bd7ea962c144c..c6de0161aac9b 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/AnnotatedClasses/AbstractClass.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/AnnotatedClasses/AbstractClass.php @@ -11,10 +11,13 @@ namespace Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses; +use Symfony\Component\Routing\Annotation\Route; + abstract class AbstractClass { abstract public function abstractRouteAction(); + #[Route('/path/to/route/{arg1}')] public function routeAction($arg1, $arg2 = 'defaultValue2', $arg3 = 'defaultValue3') { } diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/OtherAnnotatedClasses/VariadicClass.php b/src/Symfony/Component/Routing/Tests/Fixtures/OtherAnnotatedClasses/VariadicClass.php index 729c9b4d07b38..01c14ed658294 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/OtherAnnotatedClasses/VariadicClass.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/OtherAnnotatedClasses/VariadicClass.php @@ -11,8 +11,11 @@ namespace Symfony\Component\Routing\Tests\Fixtures\OtherAnnotatedClasses; +use Symfony\Component\Routing\Annotation\Route; + class VariadicClass { + #[Route('/path/to/{id}')] public function routeAction(...$params) { } diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/TraceableAnnotationClassLoader.php b/src/Symfony/Component/Routing/Tests/Fixtures/TraceableAnnotationClassLoader.php new file mode 100644 index 0000000000000..ebc37c840bb19 --- /dev/null +++ b/src/Symfony/Component/Routing/Tests/Fixtures/TraceableAnnotationClassLoader.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Tests\Fixtures; + +use Symfony\Component\Routing\Loader\AnnotationClassLoader; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; + +final class TraceableAnnotationClassLoader extends AnnotationClassLoader +{ + /** @var list */ + public array $foundClasses = []; + + public function load(mixed $class, string $type = null): RouteCollection + { + if (!is_string($class)) { + throw new \InvalidArgumentException(sprintf('Expected string, got "%s"', get_debug_type($class))); + } + + $this->foundClasses[] = $class; + + return parent::load($class, $type); + } + + protected function configureRoute(Route $route, \ReflectionClass $class, \ReflectionMethod $method, object $annot): void + { + } +} diff --git a/src/Symfony/Component/Routing/Tests/Loader/AbstractAnnotationLoaderTestCase.php b/src/Symfony/Component/Routing/Tests/Loader/AbstractAnnotationLoaderTestCase.php deleted file mode 100644 index e743ef2e35d50..0000000000000 --- a/src/Symfony/Component/Routing/Tests/Loader/AbstractAnnotationLoaderTestCase.php +++ /dev/null @@ -1,34 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Routing\Tests\Loader; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\Routing\Loader\AnnotationClassLoader; - -abstract class AbstractAnnotationLoaderTestCase extends TestCase -{ - public function getReader() - { - return $this->getMockBuilder(\Doctrine\Common\Annotations\Reader::class) - ->disableOriginalConstructor() - ->getMock() - ; - } - - public function getClassLoader($reader) - { - return $this->getMockBuilder(AnnotationClassLoader::class) - ->setConstructorArgs([$reader]) - ->getMockForAbstractClass() - ; - } -} diff --git a/src/Symfony/Component/Routing/Tests/Loader/AnnotationClassLoaderTestCase.php b/src/Symfony/Component/Routing/Tests/Loader/AnnotationClassLoaderTestCase.php index 75589835e48f9..f8e54d6a75da6 100644 --- a/src/Symfony/Component/Routing/Tests/Loader/AnnotationClassLoaderTestCase.php +++ b/src/Symfony/Component/Routing/Tests/Loader/AnnotationClassLoaderTestCase.php @@ -18,10 +18,7 @@ abstract class AnnotationClassLoaderTestCase extends TestCase { - /** - * @var AnnotationClassLoader - */ - protected $loader; + protected AnnotationClassLoader $loader; /** * @dataProvider provideTestSupportsChecksResource @@ -31,7 +28,7 @@ public function testSupportsChecksResource($resource, $expectedSupports) $this->assertSame($expectedSupports, $this->loader->supports($resource), '->supports() returns true if the resource is loadable'); } - public static function provideTestSupportsChecksResource() + public static function provideTestSupportsChecksResource(): array { return [ ['class', true], diff --git a/src/Symfony/Component/Routing/Tests/Loader/AnnotationClassLoaderWithAnnotationsTest.php b/src/Symfony/Component/Routing/Tests/Loader/AnnotationClassLoaderWithAnnotationsTest.php index 3988d4354a0d2..f53e4e3ef3baf 100644 --- a/src/Symfony/Component/Routing/Tests/Loader/AnnotationClassLoaderWithAnnotationsTest.php +++ b/src/Symfony/Component/Routing/Tests/Loader/AnnotationClassLoaderWithAnnotationsTest.php @@ -12,19 +12,17 @@ namespace Symfony\Component\Routing\Tests\Loader; use Doctrine\Common\Annotations\AnnotationReader; -use Symfony\Component\Routing\Loader\AnnotationClassLoader; -use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\Tests\Fixtures\TraceableAnnotationClassLoader; +/** + * @group legacy + */ class AnnotationClassLoaderWithAnnotationsTest extends AnnotationClassLoaderTestCase { protected function setUp(string $env = null): void { $reader = new AnnotationReader(); - $this->loader = new class($reader, $env) extends AnnotationClassLoader { - protected function configureRoute(Route $route, \ReflectionClass $class, \ReflectionMethod $method, object $annot): void - { - } - }; + $this->loader = new TraceableAnnotationClassLoader($reader, $env); } public function testDefaultRouteName() diff --git a/src/Symfony/Component/Routing/Tests/Loader/AnnotationClassLoaderWithAttributesTest.php b/src/Symfony/Component/Routing/Tests/Loader/AnnotationClassLoaderWithAttributesTest.php index d613ba09b298b..2fe0f903189fd 100644 --- a/src/Symfony/Component/Routing/Tests/Loader/AnnotationClassLoaderWithAttributesTest.php +++ b/src/Symfony/Component/Routing/Tests/Loader/AnnotationClassLoaderWithAttributesTest.php @@ -11,18 +11,13 @@ namespace Symfony\Component\Routing\Tests\Loader; -use Symfony\Component\Routing\Loader\AnnotationClassLoader; -use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\Tests\Fixtures\TraceableAnnotationClassLoader; class AnnotationClassLoaderWithAttributesTest extends AnnotationClassLoaderTestCase { protected function setUp(string $env = null): void { - $this->loader = new class(null, $env) extends AnnotationClassLoader { - protected function configureRoute(Route $route, \ReflectionClass $class, \ReflectionMethod $method, object $annot): void - { - } - }; + $this->loader = new TraceableAnnotationClassLoader($env); } public function testDefaultRouteName() diff --git a/src/Symfony/Component/Routing/Tests/Loader/AnnotationDirectoryLoaderTest.php b/src/Symfony/Component/Routing/Tests/Loader/AnnotationDirectoryLoaderTest.php index 5a7bc8cbbda53..a7c7f957dfc36 100644 --- a/src/Symfony/Component/Routing/Tests/Loader/AnnotationDirectoryLoaderTest.php +++ b/src/Symfony/Component/Routing/Tests/Loader/AnnotationDirectoryLoaderTest.php @@ -11,63 +11,38 @@ namespace Symfony\Component\Routing\Tests\Loader; +use PHPUnit\Framework\TestCase; use Symfony\Component\Config\FileLocator; use Symfony\Component\Routing\Loader\AnnotationDirectoryLoader; +use Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\BarClass; +use Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\BazClass; +use Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\EncodingClass; +use Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\FooClass; +use Symfony\Component\Routing\Tests\Fixtures\TraceableAnnotationClassLoader; -class AnnotationDirectoryLoaderTest extends AbstractAnnotationLoaderTestCase +class AnnotationDirectoryLoaderTest extends TestCase { - protected $loader; - protected $reader; + private AnnotationDirectoryLoader $loader; + private TraceableAnnotationClassLoader $classLoader; protected function setUp(): void { parent::setUp(); - $this->reader = $this->getReader(); - $this->loader = new AnnotationDirectoryLoader(new FileLocator(), $this->getClassLoader($this->reader)); + $this->classLoader = new TraceableAnnotationClassLoader(); + $this->loader = new AnnotationDirectoryLoader(new FileLocator(), $this->classLoader); } public function testLoad() { - $this->reader->expects($this->exactly(4))->method('getClassAnnotation'); - - $this->reader - ->expects($this->any()) - ->method('getMethodAnnotations') - ->willReturn([]) - ; - - $this->reader - ->expects($this->any()) - ->method('getClassAnnotations') - ->willReturn([]) - ; - $this->loader->load(__DIR__.'/../Fixtures/AnnotatedClasses'); - } - - public function testLoadIgnoresHiddenDirectories() - { - $this->expectAnnotationsToBeReadFrom([ - 'Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\BarClass', - 'Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\BazClass', - 'Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\FooClass', - 'Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\EncodingClass', - ]); - - $this->reader - ->expects($this->any()) - ->method('getMethodAnnotations') - ->willReturn([]) - ; - - $this->reader - ->expects($this->any()) - ->method('getClassAnnotations') - ->willReturn([]) - ; - $this->loader->load(__DIR__.'/../Fixtures/AnnotatedClasses'); + self::assertSame([ + BarClass::class, + BazClass::class, + EncodingClass::class, + FooClass::class, + ], $this->classLoader->foundClasses); } public function testSupports() @@ -89,29 +64,13 @@ public function testItSupportsAnyAnnotation() public function testLoadFileIfLocatedResourceIsFile() { - $this->reader->expects($this->exactly(1))->method('getClassAnnotation'); - - $this->reader - ->expects($this->any()) - ->method('getMethodAnnotations') - ->willReturn([]) - ; - $this->loader->load(__DIR__.'/../Fixtures/AnnotatedClasses/FooClass.php'); + self::assertSame([FooClass::class], $this->classLoader->foundClasses); } public function testLoadAbstractClass() { - $this->reader->expects($this->never())->method('getClassAnnotation'); - $this->reader->expects($this->never())->method('getMethodAnnotations'); - - $this->loader->load(__DIR__.'/../Fixtures/AnnotatedClasses/AbstractClass.php'); - } - - private function expectAnnotationsToBeReadFrom(array $classes) - { - $this->reader->expects($this->exactly(\count($classes))) - ->method('getClassAnnotation') - ->with($this->callback(fn (\ReflectionClass $class) => \in_array($class->getName(), $classes))); + self::assertNull($this->loader->load(__DIR__.'/../Fixtures/AnnotatedClasses/AbstractClass.php')); + self::assertSame([], $this->classLoader->foundClasses); } } diff --git a/src/Symfony/Component/Routing/Tests/Loader/AnnotationFileLoaderTest.php b/src/Symfony/Component/Routing/Tests/Loader/AnnotationFileLoaderTest.php index 473368156382f..d92760ae729cb 100644 --- a/src/Symfony/Component/Routing/Tests/Loader/AnnotationFileLoaderTest.php +++ b/src/Symfony/Component/Routing/Tests/Loader/AnnotationFileLoaderTest.php @@ -11,35 +11,45 @@ namespace Symfony\Component\Routing\Tests\Loader; +use Doctrine\Common\Annotations\AnnotationReader; +use PHPUnit\Framework\TestCase; use Symfony\Component\Config\FileLocator; -use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Loader\AnnotationFileLoader; - -class AnnotationFileLoaderTest extends AbstractAnnotationLoaderTestCase +use Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\FooClass; +use Symfony\Component\Routing\Tests\Fixtures\AttributesFixtures\AttributesClassParamAfterCommaController; +use Symfony\Component\Routing\Tests\Fixtures\AttributesFixtures\AttributesClassParamAfterParenthesisController; +use Symfony\Component\Routing\Tests\Fixtures\AttributesFixtures\AttributesClassParamInlineAfterCommaController; +use Symfony\Component\Routing\Tests\Fixtures\AttributesFixtures\AttributesClassParamInlineAfterParenthesisController; +use Symfony\Component\Routing\Tests\Fixtures\AttributesFixtures\AttributesClassParamInlineQuotedAfterCommaController; +use Symfony\Component\Routing\Tests\Fixtures\AttributesFixtures\AttributesClassParamInlineQuotedAfterParenthesisController; +use Symfony\Component\Routing\Tests\Fixtures\AttributesFixtures\AttributesClassParamQuotedAfterCommaController; +use Symfony\Component\Routing\Tests\Fixtures\AttributesFixtures\AttributesClassParamQuotedAfterParenthesisController; +use Symfony\Component\Routing\Tests\Fixtures\OtherAnnotatedClasses\VariadicClass; +use Symfony\Component\Routing\Tests\Fixtures\TraceableAnnotationClassLoader; + +class AnnotationFileLoaderTest extends TestCase { - protected $loader; - protected $reader; + private AnnotationFileLoader $loader; + private TraceableAnnotationClassLoader $classLoader; protected function setUp(): void { parent::setUp(); - $this->reader = $this->getReader(); - $this->loader = new AnnotationFileLoader(new FileLocator(), $this->getClassLoader($this->reader)); + $this->classLoader = new TraceableAnnotationClassLoader(); + $this->loader = new AnnotationFileLoader(new FileLocator(), $this->classLoader); } public function testLoad() { - $this->reader->expects($this->once())->method('getClassAnnotation'); - - $this->loader->load(__DIR__.'/../Fixtures/AnnotatedClasses/FooClass.php'); + self::assertCount(0, $this->loader->load(__DIR__.'/../Fixtures/AnnotatedClasses/FooClass.php')); + self::assertSame([FooClass::class], $this->classLoader->foundClasses); } public function testLoadTraitWithClassConstant() { - $this->reader->expects($this->never())->method('getClassAnnotation'); - - $this->loader->load(__DIR__.'/../Fixtures/AnnotatedClasses/FooTrait.php'); + self::assertCount(0, $this->loader->load(__DIR__.'/../Fixtures/AnnotatedClasses/FooTrait.php')); + self::assertSame([], $this->classLoader->foundClasses); } public function testLoadFileWithoutStartTag() @@ -51,28 +61,26 @@ public function testLoadFileWithoutStartTag() public function testLoadVariadic() { - $route = new Route('/path/to/{id}'); - $this->reader->expects($this->once())->method('getClassAnnotation'); - $this->reader->expects($this->once())->method('getMethodAnnotations') - ->willReturn([$route]); - - $this->loader->load(__DIR__.'/../Fixtures/OtherAnnotatedClasses/VariadicClass.php'); + self::assertCount(1, $this->loader->load(__DIR__.'/../Fixtures/OtherAnnotatedClasses/VariadicClass.php')); + self::assertSame([VariadicClass::class], $this->classLoader->foundClasses); } + /** + * @group legacy + */ public function testLoadAnonymousClass() { - $this->reader->expects($this->never())->method('getClassAnnotation'); - $this->reader->expects($this->never())->method('getMethodAnnotations'); + $this->classLoader = new TraceableAnnotationClassLoader(new AnnotationReader()); + $this->loader = new AnnotationFileLoader(new FileLocator(), $this->classLoader); - $this->loader->load(__DIR__.'/../Fixtures/OtherAnnotatedClasses/AnonymousClassInTrait.php'); + self::assertCount(0, $this->loader->load(__DIR__.'/../Fixtures/OtherAnnotatedClasses/AnonymousClassInTrait.php')); + self::assertSame([], $this->classLoader->foundClasses); } public function testLoadAbstractClass() { - $this->reader->expects($this->never())->method('getClassAnnotation'); - $this->reader->expects($this->never())->method('getMethodAnnotations'); - - $this->loader->load(__DIR__.'/../Fixtures/AnnotatedClasses/AbstractClass.php'); + self::assertNull($this->loader->load(__DIR__.'/../Fixtures/AnnotatedClasses/AbstractClass.php')); + self::assertSame([], $this->classLoader->foundClasses); } public function testSupports() @@ -89,57 +97,49 @@ public function testSupports() public function testLoadAttributesClassAfterComma() { - $this->reader->expects($this->once())->method('getClassAnnotation'); - - $this->loader->load(__DIR__.'/../Fixtures/AttributesFixtures/AttributesClassParamAfterCommaController.php'); + self::assertCount(0, $this->loader->load(__DIR__.'/../Fixtures/AttributesFixtures/AttributesClassParamAfterCommaController.php')); + self::assertSame([AttributesClassParamAfterCommaController::class], $this->classLoader->foundClasses); } public function testLoadAttributesInlineClassAfterComma() { - $this->reader->expects($this->once())->method('getClassAnnotation'); - - $this->loader->load(__DIR__.'/../Fixtures/AttributesFixtures/AttributesClassParamInlineAfterCommaController.php'); + self::assertCount(0, $this->loader->load(__DIR__.'/../Fixtures/AttributesFixtures/AttributesClassParamInlineAfterCommaController.php')); + self::assertSame([AttributesClassParamInlineAfterCommaController::class], $this->classLoader->foundClasses); } public function testLoadAttributesQuotedClassAfterComma() { - $this->reader->expects($this->once())->method('getClassAnnotation'); - - $this->loader->load(__DIR__.'/../Fixtures/AttributesFixtures/AttributesClassParamQuotedAfterCommaController.php'); + self::assertCount(0, $this->loader->load(__DIR__.'/../Fixtures/AttributesFixtures/AttributesClassParamQuotedAfterCommaController.php')); + self::assertSame([AttributesClassParamQuotedAfterCommaController::class], $this->classLoader->foundClasses); } public function testLoadAttributesInlineQuotedClassAfterComma() { - $this->reader->expects($this->once())->method('getClassAnnotation'); - - $this->loader->load(__DIR__.'/../Fixtures/AttributesFixtures/AttributesClassParamInlineQuotedAfterCommaController.php'); + self::assertCount(0, $this->loader->load(__DIR__.'/../Fixtures/AttributesFixtures/AttributesClassParamInlineQuotedAfterCommaController.php')); + self::assertSame([AttributesClassParamInlineQuotedAfterCommaController::class], $this->classLoader->foundClasses); } public function testLoadAttributesClassAfterParenthesis() { - $this->reader->expects($this->once())->method('getClassAnnotation'); - - $this->loader->load(__DIR__.'/../Fixtures/AttributesFixtures/AttributesClassParamAfterParenthesisController.php'); + self::assertCount(0, $this->loader->load(__DIR__.'/../Fixtures/AttributesFixtures/AttributesClassParamAfterParenthesisController.php')); + self::assertSame([AttributesClassParamAfterParenthesisController::class], $this->classLoader->foundClasses); } public function testLoadAttributesInlineClassAfterParenthesis() { - $this->reader->expects($this->once())->method('getClassAnnotation'); - - $this->loader->load(__DIR__.'/../Fixtures/AttributesFixtures/AttributesClassParamInlineAfterParenthesisController.php'); + self::assertCount(0, $this->loader->load(__DIR__.'/../Fixtures/AttributesFixtures/AttributesClassParamInlineAfterParenthesisController.php')); + self::assertSame([AttributesClassParamInlineAfterParenthesisController::class], $this->classLoader->foundClasses); } public function testLoadAttributesQuotedClassAfterParenthesis() { - $this->reader->expects($this->once())->method('getClassAnnotation'); - - $this->loader->load(__DIR__.'/../Fixtures/AttributesFixtures/AttributesClassParamQuotedAfterParenthesisController.php'); + self::assertCount(0, $this->loader->load(__DIR__.'/../Fixtures/AttributesFixtures/AttributesClassParamQuotedAfterParenthesisController.php')); + self::assertSame([AttributesClassParamQuotedAfterParenthesisController::class], $this->classLoader->foundClasses); } public function testLoadAttributesInlineQuotedClassAfterParenthesis() { - $this->reader->expects($this->once())->method('getClassAnnotation'); - - $this->loader->load(__DIR__.'/../Fixtures/AttributesFixtures/AttributesClassParamInlineQuotedAfterParenthesisController.php'); + self::assertCount(0, $this->loader->load(__DIR__.'/../Fixtures/AttributesFixtures/AttributesClassParamInlineQuotedAfterParenthesisController.php')); + self::assertSame([AttributesClassParamInlineQuotedAfterParenthesisController::class], $this->classLoader->foundClasses); } } diff --git a/src/Symfony/Component/Routing/Tests/Loader/DirectoryLoaderTest.php b/src/Symfony/Component/Routing/Tests/Loader/DirectoryLoaderTest.php index d4f2fd3263b48..b78874195cc8e 100644 --- a/src/Symfony/Component/Routing/Tests/Loader/DirectoryLoaderTest.php +++ b/src/Symfony/Component/Routing/Tests/Loader/DirectoryLoaderTest.php @@ -11,28 +11,28 @@ namespace Symfony\Component\Routing\Tests\Loader; +use PHPUnit\Framework\TestCase; use Symfony\Component\Config\FileLocator; use Symfony\Component\Config\Loader\LoaderResolver; use Symfony\Component\Routing\Loader\AnnotationFileLoader; use Symfony\Component\Routing\Loader\DirectoryLoader; use Symfony\Component\Routing\Loader\YamlFileLoader; use Symfony\Component\Routing\RouteCollection; +use Symfony\Component\Routing\Tests\Fixtures\TraceableAnnotationClassLoader; -class DirectoryLoaderTest extends AbstractAnnotationLoaderTestCase +class DirectoryLoaderTest extends TestCase { - private $loader; - private $reader; + private DirectoryLoader $loader; protected function setUp(): void { parent::setUp(); $locator = new FileLocator(); - $this->reader = $this->getReader(); $this->loader = new DirectoryLoader($locator); $resolver = new LoaderResolver([ new YamlFileLoader($locator), - new AnnotationFileLoader($locator, $this->getClassLoader($this->reader)), + new AnnotationFileLoader($locator, new TraceableAnnotationClassLoader()), $this->loader, ]); $this->loader->setResolver($resolver);