From e9663cca6bca3b38dbfdcac0f0d9fabb1f0d10bb Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 23 Aug 2022 10:46:45 +0200 Subject: [PATCH] [HttpKernel] Replace ArgumentValueResolverInterface by ValueResolverInterface --- UPGRADE-6.2.md | 5 + .../ArgumentResolver/EntityValueResolver.php | 101 +--- .../EntityValueResolverTest.php | 504 +++++------------- .../Tests/Fixtures/Attribute/UuidIdEntity.php | 2 +- .../Doctrine/Tests/Fixtures/BaseUser.php | 9 + .../CompositeObjectNoToStringIdEntity.php | 9 + .../SingleIntIdStringWrapperNameEntity.php | 1 - .../PropertyInfo/Fixtures/DoctrineEnum.php | 2 +- .../Fixtures/DoctrineRelation.php | 2 +- src/Symfony/Bridge/Doctrine/composer.json | 4 +- .../FrameworkExtension.php | 3 + src/Symfony/Component/HttpKernel/CHANGELOG.md | 1 + .../Controller/ArgumentResolver.php | 29 +- .../BackedEnumValueResolver.php | 42 +- .../DateTimeValueResolver.php | 23 +- .../ArgumentResolver/DefaultValueResolver.php | 19 +- .../NotTaggedControllerValueResolver.php | 22 +- .../RequestAttributeValueResolver.php | 11 +- .../ArgumentResolver/RequestValueResolver.php | 11 +- .../ArgumentResolver/ServiceValueResolver.php | 24 +- .../ArgumentResolver/SessionValueResolver.php | 20 +- .../TraceableValueResolver.php | 13 +- .../ArgumentResolver/UidValueResolver.php | 21 +- .../VariadicValueResolver.php | 15 +- .../ArgumentValueResolverInterface.php | 2 + .../Controller/ValueResolverInterface.php | 28 + .../BackedEnumValueResolverTest.php | 18 +- .../DateTimeValueResolverTest.php | 30 +- .../NotTaggedControllerValueResolverTest.php | 20 +- .../ServiceValueResolverTest.php | 28 +- .../TraceableValueResolverTest.php | 3 + .../ArgumentResolver/UidValueResolverTest.php | 7 + .../Tests/Controller/ArgumentResolverTest.php | 3 + .../Component/HttpKernel/composer.json | 1 + .../AccessToken/QueryAccessTokenExtractor.php | 3 +- .../Http/Controller/UserValueResolver.php | 32 +- .../Http/Firewall/SwitchUserListener.php | 2 +- .../LoginLink/LoginLinkHandlerInterface.php | 2 +- .../JsonLoginAuthenticatorTest.php | 1 - .../Controller/UserValueResolverTest.php | 36 +- .../Component/Security/Http/composer.json | 1 + 41 files changed, 500 insertions(+), 610 deletions(-) create mode 100644 src/Symfony/Component/HttpKernel/Controller/ValueResolverInterface.php diff --git a/UPGRADE-6.2.md b/UPGRADE-6.2.md index 8271d0843738a..682ad84ff89dc 100644 --- a/UPGRADE-6.2.md +++ b/UPGRADE-6.2.md @@ -14,6 +14,11 @@ HttpFoundation * Deprecate `Request::getContentType()`, use `Request::getContentTypeFormat()` instead +HttpKernel +---------- + + * Deprecate `ArgumentValueResolverInterface`, use `ValueResolverInterface` instead + Ldap ---- diff --git a/src/Symfony/Bridge/Doctrine/ArgumentResolver/EntityValueResolver.php b/src/Symfony/Bridge/Doctrine/ArgumentResolver/EntityValueResolver.php index e5f45039eef22..77649065f6f0b 100644 --- a/src/Symfony/Bridge/Doctrine/ArgumentResolver/EntityValueResolver.php +++ b/src/Symfony/Bridge/Doctrine/ArgumentResolver/EntityValueResolver.php @@ -19,7 +19,7 @@ use Symfony\Bridge\Doctrine\Attribute\MapEntity; use Symfony\Component\ExpressionLanguage\ExpressionLanguage; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface; +use Symfony\Component\HttpKernel\Controller\ValueResolverInterface; use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; @@ -29,7 +29,7 @@ * @author Fabien Potencier * @author Jérémy Derussé */ -final class EntityValueResolver implements ArgumentValueResolverInterface +final class EntityValueResolver implements ValueResolverInterface { public function __construct( private ManagerRegistry $registry, @@ -41,66 +41,36 @@ public function __construct( /** * {@inheritdoc} */ - public function supports(Request $request, ArgumentMetadata $argument): bool + public function resolve(Request $request, ArgumentMetadata $argument): array { - if (!$this->registry->getManagerNames()) { - return false; - } - $options = $this->getMapEntityAttribute($argument); if (!$options->class || $options->disabled) { - return false; - } - - if (null === $options->expr && !($options->mapping || $options->exclude) && null === $this->getIdentifier($request, $options, $argument->getName())) { - return false; - } - - // Doctrine Entity? - if (!$objectManager = $this->getManager($options->objectManager, $options->class)) { - return false; + return []; } - - if ($objectManager->getMetadataFactory()->isTransient($options->class)) { - return false; + if (!$manager = $this->getManager($options->objectManager, $options->class)) { + return []; } - return null !== $options->expr || $this->getCriteria($request, $options, $objectManager); - } - - /** - * {@inheritdoc} - */ - public function resolve(Request $request, ArgumentMetadata $argument): iterable - { - $options = $this->getMapEntityAttribute($argument); - $name = $argument->getName(); - $objectManager = $this->getManager($options->objectManager, $options->class); - - $errorMessage = null; + $message = ''; if (null !== $options->expr) { - if (null === $object = $this->findViaExpression($objectManager, $request, $options)) { - $errorMessage = sprintf('The expression "%s" returned null', $options->expr); + if (null === $object = $this->findViaExpression($manager, $request, $options)) { + $message = sprintf(' The expression "%s" returned null.', $options->expr); } // find by identifier? - } elseif (false === $object = $this->find($objectManager, $request, $options, $name)) { + } elseif (false === $object = $this->find($manager, $request, $options, $argument->getName())) { // find by criteria - if (false === $object = $this->findOneBy($objectManager, $request, $options)) { - if (!$argument->isNullable()) { - throw new \LogicException(sprintf('Unable to guess how to get a Doctrine instance from the request information for parameter "%s".', $name)); - } - + if (!$criteria = $this->getCriteria($request, $options, $manager)) { + return []; + } + try { + $object = $manager->getRepository($options->class)->findOneBy($criteria); + } catch (NoResultException|ConversionException) { $object = null; } } if (null === $object && !$argument->isNullable()) { - $message = sprintf('"%s" object not found by the "%s" Argument Resolver.', $options->class, self::class); - if ($errorMessage) { - $message .= ' '.$errorMessage; - } - - throw new NotFoundHttpException($message); + throw new NotFoundHttpException(sprintf('"%s" object not found by "%s".', $options->class, self::class).$message); } return [$object]; @@ -112,18 +82,16 @@ private function getManager(?string $name, string $class): ?ObjectManager return $this->registry->getManagerForClass($class); } - if (!isset($this->registry->getManagerNames()[$name])) { - return null; - } - try { - return $this->registry->getManager($name); + $manager = $this->registry->getManager($name); } catch (\InvalidArgumentException) { return null; } + + return $manager->getMetadataFactory()->isTransient($class) ? null : $manager; } - private function find(ObjectManager $objectManager, Request $request, MapEntity $options, string $name): false|object|null + private function find(ObjectManager $manager, Request $request, MapEntity $options, string $name): false|object|null { if ($options->mapping || $options->exclude) { return false; @@ -134,15 +102,15 @@ private function find(ObjectManager $objectManager, Request $request, MapEntity return $id; } - if ($options->evictCache && $objectManager instanceof EntityManagerInterface) { - $cacheProvider = $objectManager->getCache(); + if ($options->evictCache && $manager instanceof EntityManagerInterface) { + $cacheProvider = $manager->getCache(); if ($cacheProvider && $cacheProvider->containsEntity($options->class, $id)) { $cacheProvider->evictEntity($options->class, $id); } } try { - return $objectManager->getRepository($options->class)->find($id); + return $manager->getRepository($options->class)->find($id); } catch (NoResultException|ConversionException) { return null; } @@ -179,20 +147,7 @@ private function getIdentifier(Request $request, MapEntity $options, string $nam return false; } - private function findOneBy(ObjectManager $objectManager, Request $request, MapEntity $options): false|object|null - { - if (!$criteria = $this->getCriteria($request, $options, $objectManager)) { - return false; - } - - try { - return $objectManager->getRepository($options->class)->findOneBy($criteria); - } catch (NoResultException|ConversionException) { - return null; - } - } - - private function getCriteria(Request $request, MapEntity $options, ObjectManager $objectManager): array + private function getCriteria(Request $request, MapEntity $options, ObjectManager $manager): array { if (null === $mapping = $options->mapping) { $mapping = $request->attributes->keys(); @@ -217,7 +172,7 @@ private function getCriteria(Request $request, MapEntity $options, ObjectManager } $criteria = []; - $metadata = $objectManager->getClassMetadata($options->class); + $metadata = $manager->getClassMetadata($options->class); foreach ($mapping as $attribute => $field) { if (!$metadata->hasField($field) && (!$metadata->hasAssociation($field) || !$metadata->isSingleValuedAssociation($field))) { @@ -234,13 +189,13 @@ private function getCriteria(Request $request, MapEntity $options, ObjectManager return $criteria; } - private function findViaExpression(ObjectManager $objectManager, Request $request, MapEntity $options): ?object + private function findViaExpression(ObjectManager $manager, Request $request, MapEntity $options): ?object { if (!$this->expressionLanguage) { throw new \LogicException(sprintf('You cannot use the "%s" if the ExpressionLanguage component is not available. Try running "composer require symfony/expression-language".', __CLASS__)); } - $repository = $objectManager->getRepository($options->class); + $repository = $manager->getRepository($options->class); $variables = array_merge($request->attributes->all(), ['repository' => $repository]); try { diff --git a/src/Symfony/Bridge/Doctrine/Tests/ArgumentResolver/EntityValueResolverTest.php b/src/Symfony/Bridge/Doctrine/Tests/ArgumentResolver/EntityValueResolverTest.php index 8856f0b9121a8..629a37256c0f2 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/ArgumentResolver/EntityValueResolverTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/ArgumentResolver/EntityValueResolverTest.php @@ -14,9 +14,9 @@ use Doctrine\DBAL\Types\ConversionException; use Doctrine\Persistence\ManagerRegistry; use Doctrine\Persistence\Mapping\ClassMetadata; -use Doctrine\Persistence\Mapping\ClassMetadataFactory; use Doctrine\Persistence\ObjectManager; use Doctrine\Persistence\ObjectRepository; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Symfony\Bridge\Doctrine\ArgumentResolver\EntityValueResolver; use Symfony\Bridge\Doctrine\Attribute\MapEntity; @@ -28,182 +28,78 @@ class EntityValueResolverTest extends TestCase { - public function testSupports() + public function testResolveWithoutClass() { - $registry = $this->getMockBuilder(ManagerRegistry::class)->getMock(); $manager = $this->getMockBuilder(ObjectManager::class)->getMock(); - $metadataFactory = $this->getMockBuilder(ClassMetadataFactory::class)->getMock(); - $classMetadata = $this->getMockBuilder(ClassMetadata::class)->getMock(); - $converter = new EntityValueResolver($registry); - - $registry->expects($this->once()) - ->method('getManagerNames') - ->with() - ->willReturn(['default' => 'default']); - - $registry->expects($this->once()) - ->method('getManagerForClass') - ->with('stdClass') - ->willReturn($manager); - $manager->expects($this->once()) - ->method('getMetadataFactory') - ->with() - ->willReturn($metadataFactory); - $metadataFactory->expects($this->once()) - ->method('isTransient') - ->with('stdClass') - ->willReturn(false); - $classMetadata->expects($this->once()) - ->method('hasField') - ->with('foo') - ->willReturn(true); - $manager->expects($this->once()) - ->method('getClassMetadata') - ->with('stdClass') - ->willReturn($classMetadata); - - $request = new Request(); - $request->attributes->set('foo', 'bar'); - $argument = $this->createArgument(); - - $this->assertTrue($converter->supports($request, $argument)); - } - - public function testSupportWithoutRegistry() - { - $registry = $this->getMockBuilder(ManagerRegistry::class)->getMock(); - $converter = new EntityValueResolver($registry); - - $registry->expects($this->once()) - ->method('getManagerNames') - ->with() - ->willReturn([]); - - $request = new Request(); - $argument = $this->createArgument(); - - $this->assertFalse($converter->supports($request, $argument)); - } - - public function testSupportWithoutClass() - { - $registry = $this->getMockBuilder(ManagerRegistry::class)->getMock(); - $converter = new EntityValueResolver($registry); - - $registry->expects($this->once()) - ->method('getManagerNames') - ->with() - ->willReturn(['default' => 'default']); + $registry = $this->createRegistry($manager); + $resolver = new EntityValueResolver($registry); $request = new Request(); $argument = new ArgumentMetadata('arg', null, false, false, null); - $this->assertFalse($converter->supports($request, $argument)); + $this->assertSame([], $resolver->resolve($request, $argument)); } - public function testSupportWithoutAttribute() + public function testResolveWithoutAttribute() { - $registry = $this->getMockBuilder(ManagerRegistry::class)->getMock(); - $converter = new EntityValueResolver($registry, null, new MapEntity(disabled: true)); - - $registry->expects($this->once()) - ->method('getManagerNames') - ->with() - ->willReturn(['default' => 'default']); + $manager = $this->getMockBuilder(ObjectManager::class)->getMock(); + $registry = $this->createRegistry($manager); + $resolver = new EntityValueResolver($registry, null, new MapEntity(disabled: true)); $request = new Request(); $argument = $this->createArgument(); - $this->assertFalse($converter->supports($request, $argument)); + $this->assertSame([], $resolver->resolve($request, $argument)); } - public function testSupportWithoutManager() + public function testResolveWithoutManager() { - $registry = $this->getMockBuilder(ManagerRegistry::class)->getMock(); - $converter = new EntityValueResolver($registry); - - $registry->expects($this->once()) - ->method('getManagerNames') - ->with() - ->willReturn(['default' => 'default']); - - $registry->expects($this->once()) - ->method('getManagerForClass') - ->with('stdClass') - ->willReturn(null); + $registry = $this->createRegistry(null); + $resolver = new EntityValueResolver($registry); $request = new Request(); $argument = $this->createArgument(); - $this->assertFalse($converter->supports($request, $argument)); - } - - public function testResolveWithNoIdAndData() - { - $registry = $this->getMockBuilder(ManagerRegistry::class)->getMock(); - $registry->expects($this->once()) - ->method('getManagerForClass') - ->willReturn($this->getMockBuilder(ObjectManager::class)->getMock()); - - $converter = new EntityValueResolver($registry); - - $this->expectException(\LogicException::class); - - $request = new Request(); - $argument = $this->createArgument(null, new MapEntity()); - - $converter->resolve($request, $argument); + $this->assertSame([], $resolver->resolve($request, $argument)); } public function testResolveWithNoIdAndDataOptional() { - $registry = $this->getMockBuilder(ManagerRegistry::class)->getMock(); - $registry->expects($this->once()) - ->method('getManagerForClass') - ->willReturn($this->getMockBuilder(ObjectManager::class)->getMock()); - - $converter = new EntityValueResolver($registry); + $manager = $this->getMockBuilder(ObjectManager::class)->getMock(); + $registry = $this->createRegistry($manager); + $resolver = new EntityValueResolver($registry); $request = new Request(); $argument = $this->createArgument(null, new MapEntity(), 'arg', true); - $ret = $converter->resolve($request, $argument); - - $this->assertYieldEquals([null], $ret); + $this->assertSame([], $resolver->resolve($request, $argument)); } public function testResolveWithStripNulls() { - $registry = $this->getMockBuilder(ManagerRegistry::class)->getMock(); - $converter = new EntityValueResolver($registry); + $manager = $this->getMockBuilder(ObjectManager::class)->getMock(); + $registry = $this->createRegistry($manager); + $resolver = new EntityValueResolver($registry); $request = new Request(); $request->attributes->set('arg', null); $argument = $this->createArgument('stdClass', new MapEntity(stripNull: true), 'arg', true); - $classMetadata = $this->getMockBuilder(ClassMetadata::class)->getMock(); - $manager = $this->getMockBuilder(ObjectManager::class)->getMock(); + $metadata = $this->getMockBuilder(ClassMetadata::class)->getMock(); + $metadata->expects($this->once()) + ->method('hasField') + ->with('arg') + ->willReturn(true); + $manager->expects($this->once()) ->method('getClassMetadata') ->with('stdClass') - ->willReturn($classMetadata); + ->willReturn($metadata); $manager->expects($this->never()) ->method('getRepository'); - $registry->expects($this->once()) - ->method('getManagerForClass') - ->with('stdClass') - ->willReturn($manager); - - $classMetadata->expects($this->once()) - ->method('hasField') - ->with('arg') - ->willReturn(true); - - $ret = $converter->resolve($request, $argument); - - $this->assertYieldEquals([null], $ret); + $this->assertSame([], $resolver->resolve($request, $argument)); } /** @@ -211,95 +107,75 @@ public function testResolveWithStripNulls() */ public function testResolveWithId(string|int $id) { - $registry = $this->getMockBuilder(ManagerRegistry::class)->getMock(); - $converter = new EntityValueResolver($registry); + $manager = $this->getMockBuilder(ObjectManager::class)->getMock(); + $registry = $this->createRegistry($manager); + $resolver = new EntityValueResolver($registry); $request = new Request(); $request->attributes->set('id', $id); $argument = $this->createArgument('stdClass', new MapEntity(id: 'id')); - $manager = $this->getMockBuilder(ObjectManager::class)->getMock(); - $objectRepository = $this->getMockBuilder(ObjectRepository::class)->getMock(); - $registry->expects($this->once()) - ->method('getManagerForClass') - ->with('stdClass') - ->willReturn($manager); - - $manager->expects($this->once()) - ->method('getRepository') - ->with('stdClass') - ->willReturn($objectRepository); - - $objectRepository->expects($this->once()) + $repository = $this->getMockBuilder(ObjectRepository::class)->getMock(); + $repository->expects($this->once()) ->method('find') ->with($id) ->willReturn($object = new \stdClass()); - $ret = $converter->resolve($request, $argument); + $manager->expects($this->once()) + ->method('getRepository') + ->with('stdClass') + ->willReturn($repository); - $this->assertYieldEquals([$object], $ret); + $this->assertSame([$object], $resolver->resolve($request, $argument)); } public function testResolveWithNullId() { - $registry = $this->getMockBuilder(ManagerRegistry::class)->getMock(); - $registry->expects($this->once()) - ->method('getManagerForClass') - ->willReturn($this->getMockBuilder(ObjectManager::class)->getMock()); - - $converter = new EntityValueResolver($registry); + $manager = $this->getMockBuilder(ObjectManager::class)->getMock(); + $registry = $this->createRegistry($manager); + $resolver = new EntityValueResolver($registry); $request = new Request(); $request->attributes->set('id', null); $argument = $this->createArgument(isNullable: true); - $ret = $converter->resolve($request, $argument); - - $this->assertYieldEquals([null], $ret); + $this->assertSame([null], $resolver->resolve($request, $argument)); } public function testResolveWithConversionFailedException() { - $registry = $this->getMockBuilder(ManagerRegistry::class)->getMock(); - $converter = new EntityValueResolver($registry); + $manager = $this->getMockBuilder(ObjectManager::class)->getMock(); + $registry = $this->createRegistry($manager); + $resolver = new EntityValueResolver($registry); $request = new Request(); $request->attributes->set('id', 'test'); $argument = $this->createArgument('stdClass', new MapEntity(id: 'id')); - $manager = $this->getMockBuilder(ObjectManager::class)->getMock(); - $objectRepository = $this->getMockBuilder(ObjectRepository::class)->getMock(); - $registry->expects($this->once()) - ->method('getManagerForClass') - ->with('stdClass') - ->willReturn($manager); + $repository = $this->getMockBuilder(ObjectRepository::class)->getMock(); + $repository->expects($this->once()) + ->method('find') + ->with('test') + ->will($this->throwException(new ConversionException())); $manager->expects($this->once()) ->method('getRepository') ->with('stdClass') - ->willReturn($objectRepository); - - $objectRepository->expects($this->once()) - ->method('find') - ->with('test') - ->will($this->throwException(new ConversionException())); + ->willReturn($repository); $this->expectException(NotFoundHttpException::class); - $converter->resolve($request, $argument); + $resolver->resolve($request, $argument); } public function testUsedProperIdentifier() { - $registry = $this->getMockBuilder(ManagerRegistry::class)->getMock(); - $registry->expects($this->once()) - ->method('getManagerForClass') - ->willReturn($this->getMockBuilder(ObjectManager::class)->getMock()); - - $converter = new EntityValueResolver($registry); + $manager = $this->getMockBuilder(ObjectManager::class)->getMock(); + $registry = $this->createRegistry($manager); + $resolver = new EntityValueResolver($registry); $request = new Request(); $request->attributes->set('id', 1); @@ -308,9 +184,7 @@ public function testUsedProperIdentifier() $argument = $this->createArgument('stdClass', new MapEntity(id: 'entity_id'), 'arg', true); - $ret = $converter->resolve($request, $argument); - - $this->assertYieldEquals([null], $ret); + $this->assertSame([null], $resolver->resolve($request, $argument)); } public function idsProvider(): iterable @@ -322,41 +196,31 @@ public function idsProvider(): iterable public function testResolveGuessOptional() { - $registry = $this->getMockBuilder(ManagerRegistry::class)->getMock(); - $converter = new EntityValueResolver($registry); + $manager = $this->getMockBuilder(ObjectManager::class)->getMock(); + $registry = $this->createRegistry($manager); + $resolver = new EntityValueResolver($registry); $request = new Request(); $request->attributes->set('guess', null); $argument = $this->createArgument('stdClass', new MapEntity(), 'arg', true); - $classMetadata = $this->getMockBuilder(ClassMetadata::class)->getMock(); - $manager = $this->getMockBuilder(ObjectManager::class)->getMock(); + $metadata = $this->getMockBuilder(ClassMetadata::class)->getMock(); $manager->expects($this->once()) ->method('getClassMetadata') ->with('stdClass') - ->willReturn($classMetadata); - - $objectRepository = $this->getMockBuilder(ObjectRepository::class)->getMock(); - $registry->expects($this->once()) - ->method('getManagerForClass') - ->with('stdClass') - ->willReturn($manager); + ->willReturn($metadata); $manager->expects($this->never())->method('getRepository'); - $objectRepository->expects($this->never())->method('find'); - $objectRepository->expects($this->never())->method('findOneBy'); - - $ret = $converter->resolve($request, $argument); - - $this->assertYieldEquals([null], $ret); + $this->assertSame([], $resolver->resolve($request, $argument)); } public function testResolveWithMappingAndExclude() { - $registry = $this->getMockBuilder(ManagerRegistry::class)->getMock(); - $converter = new EntityValueResolver($registry); + $manager = $this->getMockBuilder(ObjectManager::class)->getMock(); + $registry = $this->createRegistry($manager); + $resolver = new EntityValueResolver($registry); $request = new Request(); $request->attributes->set('foo', 1); @@ -367,144 +231,36 @@ public function testResolveWithMappingAndExclude() new MapEntity(mapping: ['foo' => 'Foo'], exclude: ['bar']) ); - $manager = $this->getMockBuilder(ObjectManager::class)->getMock(); $metadata = $this->getMockBuilder(ClassMetadata::class)->getMock(); - $repository = $this->getMockBuilder(ObjectRepository::class)->getMock(); - - $registry->expects($this->once()) - ->method('getManagerForClass') - ->with('stdClass') - ->willReturn($manager); + $metadata->expects($this->once()) + ->method('hasField') + ->with('Foo') + ->willReturn(true); $manager->expects($this->once()) ->method('getClassMetadata') - ->with('stdClass') - ->willReturn($metadata); - $manager->expects($this->once()) - ->method('getRepository') - ->with('stdClass') - ->willReturn($repository); - - $metadata->expects($this->once()) - ->method('hasField') - ->with('Foo') - ->willReturn(true); + ->with('stdClass') + ->willReturn($metadata); + $repository = $this->getMockBuilder(ObjectRepository::class)->getMock(); $repository->expects($this->once()) ->method('findOneBy') ->with(['Foo' => 1]) ->willReturn($object = new \stdClass()); - $ret = $converter->resolve($request, $argument); - - $this->assertYieldEquals([$object], $ret); - } - - public function testIgnoreMappingWhenAutoMappingDisabled() - { - $registry = $this->getMockBuilder(ManagerRegistry::class)->getMock(); - $registry->expects($this->once()) - ->method('getManagerForClass') - ->willReturn($this->getMockBuilder(ObjectManager::class)->getMock()); - - $converter = new EntityValueResolver($registry, null, new MapEntity(mapping: [])); - - $request = new Request(); - $request->attributes->set('foo', 1); - - $argument = $this->createArgument( - 'stdClass', - new MapEntity() - ); - - $metadata = $this->getMockBuilder(ClassMetadata::class)->getMock(); - - $metadata->expects($this->never()) - ->method('hasField'); - - $this->expectException(\LogicException::class); - - $ret = $converter->resolve($request, $argument); - - $this->assertYieldEquals([], $ret); - } - - public function testSupportsWithConfiguredObjectManager() - { - $registry = $this->getMockBuilder(ManagerRegistry::class)->getMock(); - $converter = new EntityValueResolver($registry); - - $argument = $this->createArgument('stdClass', new MapEntity(objectManager: 'foo')); - $metadataFactory = $this->getMockBuilder(ClassMetadataFactory::class)->getMock(); - $metadataFactory->expects($this->once()) - ->method('isTransient') - ->with('stdClass') - ->willReturn(false); - - $objectManager = $this->getMockBuilder(ObjectManager::class)->getMock(); - $objectManager->expects($this->once()) - ->method('getMetadataFactory') - ->willReturn($metadataFactory); - - $registry->expects($this->exactly(2)) - ->method('getManagerNames') - ->willReturn(['foo' => 'foo']); - - $classMetadata = $this->getMockBuilder(ClassMetadata::class)->getMock(); - $classMetadata->expects($this->once()) - ->method('hasField') - ->with('foo') - ->willReturn(true); - $objectManager->expects($this->once()) - ->method('getClassMetadata') + $manager->expects($this->once()) + ->method('getRepository') ->with('stdClass') - ->willReturn($classMetadata); + ->willReturn($repository); - $registry->expects($this->once()) - ->method('getManager') - ->with('foo') - ->willReturn($objectManager); - - $request = new Request(); - $request->attributes->set('foo', 'bar'); - $ret = $converter->supports($request, $argument); - - $this->assertTrue($ret, 'Should be supported'); - } - - public function testSupportsWithDifferentConfiguration() - { - $registry = $this->getMockBuilder(ManagerRegistry::class)->getMock(); - $converter = new EntityValueResolver($registry); - - $argument = $this->createArgument('DateTime'); - - $objectManager = $this->getMockBuilder(ObjectManager::class)->getMock(); - $objectManager->expects($this->never()) - ->method('getMetadataFactory'); - - $registry->expects($this->any()) - ->method('getManagerNames') - ->willReturn(['default' => 'default']); - - $registry->expects($this->never()) - ->method('getManager'); - - $ret = $converter->supports(new Request(), $argument); - - $this->assertFalse($ret, 'Should not be supported'); + $this->assertSame([$object], $resolver->resolve($request, $argument)); } public function testExceptionWithExpressionIfNoLanguageAvailable() { - $registry = $this->getMockBuilder(ManagerRegistry::class)->getMock(); - $registry->expects($this->once()) - ->method('getManagerForClass') - ->willReturn($this->getMockBuilder(ObjectManager::class)->getMock()); - - $converter = new EntityValueResolver($registry); - - $this->expectException(\LogicException::class); + $manager = $this->getMockBuilder(ObjectManager::class)->getMock(); + $registry = $this->createRegistry($manager); + $resolver = new EntityValueResolver($registry); $request = new Request(); $argument = $this->createArgument( @@ -513,14 +269,17 @@ public function testExceptionWithExpressionIfNoLanguageAvailable() 'arg1' ); - $converter->resolve($request, $argument); + $this->expectException(\LogicException::class); + + $resolver->resolve($request, $argument); } public function testExpressionFailureReturns404() { - $registry = $this->getMockBuilder(ManagerRegistry::class)->getMock(); + $manager = $this->getMockBuilder(ObjectManager::class)->getMock(); + $registry = $this->createRegistry($manager); $language = $this->getMockBuilder(ExpressionLanguage::class)->getMock(); - $converter = new EntityValueResolver($registry, $language); + $resolver = new EntityValueResolver($registry, $language); $this->expectException(NotFoundHttpException::class); @@ -531,33 +290,28 @@ public function testExpressionFailureReturns404() 'arg1' ); - $objectManager = $this->getMockBuilder(ObjectManager::class)->getMock(); - $objectRepository = $this->getMockBuilder(ObjectRepository::class)->getMock(); - - $objectManager->expects($this->once()) - ->method('getRepository') - ->willReturn($objectRepository); - + $repository = $this->getMockBuilder(ObjectRepository::class)->getMock(); // find should not be attempted on this repository as a fallback - $objectRepository->expects($this->never()) + $repository->expects($this->never()) ->method('find'); - $registry->expects($this->once()) - ->method('getManagerForClass') - ->willReturn($objectManager); + $manager->expects($this->once()) + ->method('getRepository') + ->willReturn($repository); $language->expects($this->once()) ->method('evaluate') ->willReturn(null); - $converter->resolve($request, $argument); + $resolver->resolve($request, $argument); } public function testExpressionMapsToArgument() { - $registry = $this->getMockBuilder(ManagerRegistry::class)->getMock(); + $manager = $this->getMockBuilder(ObjectManager::class)->getMock(); + $registry = $this->createRegistry($manager); $language = $this->getMockBuilder(ExpressionLanguage::class)->getMock(); - $converter = new EntityValueResolver($registry, $language); + $resolver = new EntityValueResolver($registry, $language); $request = new Request(); $request->attributes->set('id', 5); @@ -567,41 +321,32 @@ public function testExpressionMapsToArgument() 'arg1' ); - $objectManager = $this->getMockBuilder(ObjectManager::class)->getMock(); - $objectRepository = $this->getMockBuilder(ObjectRepository::class)->getMock(); - - $objectManager->expects($this->once()) - ->method('getRepository') - ->willReturn($objectRepository); - + $repository = $this->getMockBuilder(ObjectRepository::class)->getMock(); // find should not be attempted on this repository as a fallback - $objectRepository->expects($this->never()) + $repository->expects($this->never()) ->method('find'); - $registry->expects($this->once()) - ->method('getManagerForClass') - ->willReturn($objectManager); + $manager->expects($this->once()) + ->method('getRepository') + ->willReturn($repository); $language->expects($this->once()) ->method('evaluate') ->with('repository.findOneByCustomMethod(id)', [ - 'repository' => $objectRepository, + 'repository' => $repository, 'id' => 5, ]) ->willReturn($object = new \stdClass()); - $ret = $converter->resolve($request, $argument); - $this->assertYieldEquals([$object], $ret); + $this->assertSame([$object], $resolver->resolve($request, $argument)); } public function testExpressionSyntaxErrorThrowsException() { - $registry = $this->getMockBuilder(ManagerRegistry::class)->getMock(); + $manager = $this->getMockBuilder(ObjectManager::class)->getMock(); + $registry = $this->createRegistry($manager); $language = $this->getMockBuilder(ExpressionLanguage::class)->getMock(); - $converter = new EntityValueResolver($registry, $language); - - $this->expectException(\LogicException::class); - $this->expectExceptionMessage('syntax error message around position 10'); + $resolver = new EntityValueResolver($registry, $language); $request = new Request(); $argument = $this->createArgument( @@ -610,28 +355,22 @@ public function testExpressionSyntaxErrorThrowsException() 'arg1' ); - $objectManager = $this->getMockBuilder(ObjectManager::class)->getMock(); - $objectRepository = $this->getMockBuilder(ObjectRepository::class)->getMock(); - - $objectManager->expects($this->once()) - ->method('getRepository') - ->willReturn($objectRepository); - + $repository = $this->getMockBuilder(ObjectRepository::class)->getMock(); // find should not be attempted on this repository as a fallback - $objectRepository->expects($this->never()) + $repository->expects($this->never()) ->method('find'); - $registry->expects($this->once()) - ->method('getManagerForClass') - ->willReturn($objectManager); + $manager->expects($this->once()) + ->method('getRepository') + ->willReturn($repository); $language->expects($this->once()) ->method('evaluate') ->will($this->throwException(new SyntaxError('syntax error message', 10))); - $ret = $converter->resolve($request, $argument); - - $this->assertYieldEquals([null], $ret); + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('syntax error message around position 10'); + $resolver->resolve($request, $argument); } private function createArgument(string $class = null, MapEntity $entity = null, string $name = 'arg', bool $isNullable = false): ArgumentMetadata @@ -639,13 +378,18 @@ private function createArgument(string $class = null, MapEntity $entity = null, return new ArgumentMetadata($name, $class ?? \stdClass::class, false, false, null, $isNullable, $entity ? [$entity] : []); } - private function assertYieldEquals(array $expected, iterable $generator) + private function createRegistry(ObjectManager $manager = null): ManagerRegistry&MockObject { - $args = []; - foreach ($generator as $arg) { - $args[] = $arg; - } + $registry = $this->getMockBuilder(ManagerRegistry::class)->getMock(); + + $registry->expects($this->any()) + ->method('getManagerForClass') + ->willReturn($manager); + + $registry->expects($this->any()) + ->method('getManager') + ->willReturn($manager); - $this->assertEquals($expected, $args); + return $registry; } } diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Attribute/UuidIdEntity.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Attribute/UuidIdEntity.php index 3d28d4469c1fb..6d16ee75e54ae 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Attribute/UuidIdEntity.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Attribute/UuidIdEntity.php @@ -19,7 +19,7 @@ class UuidIdEntity { #[Id] - #[Column("uuid")] + #[Column('uuid')] protected $id; public function __construct($id) diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/BaseUser.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/BaseUser.php index f03157637b256..c8be89cc760e0 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/BaseUser.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/BaseUser.php @@ -1,5 +1,14 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace Symfony\Bridge\Doctrine\Tests\Fixtures; /** diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/CompositeObjectNoToStringIdEntity.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/CompositeObjectNoToStringIdEntity.php index 82811b89ed8c0..73a6419e2f0c0 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/CompositeObjectNoToStringIdEntity.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/CompositeObjectNoToStringIdEntity.php @@ -1,5 +1,14 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace Symfony\Bridge\Doctrine\Tests\Fixtures; use Doctrine\ORM\Mapping as ORM; diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleIntIdStringWrapperNameEntity.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleIntIdStringWrapperNameEntity.php index a06f4432a761a..8f4c3663d2148 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleIntIdStringWrapperNameEntity.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleIntIdStringWrapperNameEntity.php @@ -14,7 +14,6 @@ use Doctrine\ORM\Mapping\Column; use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\Id; -use Symfony\Bridge\Doctrine\Tests\Fixtures\Type\StringWrapper; /** @Entity */ class SingleIntIdStringWrapperNameEntity diff --git a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineEnum.php b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineEnum.php index fd5271fc47730..8a63a4e8db248 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineEnum.php +++ b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineEnum.php @@ -11,9 +11,9 @@ namespace Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures; -use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\Column; use Doctrine\ORM\Mapping\Entity; +use Doctrine\ORM\Mapping\Id; /** * @Entity diff --git a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineRelation.php b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineRelation.php index 61a658096add0..91115abb719ec 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineRelation.php +++ b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineRelation.php @@ -14,8 +14,8 @@ use Doctrine\ORM\Mapping\Column; use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\Id; -use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\JoinColumn; +use Doctrine\ORM\Mapping\ManyToOne; /** * @Entity diff --git a/src/Symfony/Bridge/Doctrine/composer.json b/src/Symfony/Bridge/Doctrine/composer.json index 8de4371499e8d..c68fe94ba5ac8 100644 --- a/src/Symfony/Bridge/Doctrine/composer.json +++ b/src/Symfony/Bridge/Doctrine/composer.json @@ -30,7 +30,7 @@ "symfony/config": "^5.4|^6.0", "symfony/dependency-injection": "^5.4|^6.0", "symfony/form": "^5.4.9|^6.0.9", - "symfony/http-kernel": "^5.4|^6.0", + "symfony/http-kernel": "^6.2", "symfony/messenger": "^5.4|^6.0", "symfony/doctrine-messenger": "^5.4|^6.0", "symfony/property-access": "^5.4|^6.0", @@ -57,7 +57,7 @@ "symfony/cache": "<5.4", "symfony/dependency-injection": "<5.4", "symfony/form": "<5.4", - "symfony/http-kernel": "<5.4", + "symfony/http-kernel": "<6.2", "symfony/messenger": "<5.4", "symfony/property-info": "<5.4", "symfony/security-bundle": "<5.4", diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index ea76107b01440..bfd53358618d9 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -87,6 +87,7 @@ use Symfony\Component\HttpKernel\Controller\ArgumentResolver\BackedEnumValueResolver; use Symfony\Component\HttpKernel\Controller\ArgumentResolver\UidValueResolver; use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface; +use Symfony\Component\HttpKernel\Controller\ValueResolverInterface; use Symfony\Component\HttpKernel\DataCollector\DataCollectorInterface; use Symfony\Component\HttpKernel\DependencyInjection\Extension; use Symfony\Component\Lock\LockFactory; @@ -590,6 +591,8 @@ public function load(array $configs, ContainerBuilder $container) ->addTag('container.service_subscriber'); $container->registerForAutoconfiguration(ArgumentValueResolverInterface::class) ->addTag('controller.argument_value_resolver'); + $container->registerForAutoconfiguration(ValueResolverInterface::class) + ->addTag('controller.argument_value_resolver'); $container->registerForAutoconfiguration(AbstractController::class) ->addTag('controller.service_arguments'); $container->registerForAutoconfiguration(DataCollectorInterface::class) diff --git a/src/Symfony/Component/HttpKernel/CHANGELOG.md b/src/Symfony/Component/HttpKernel/CHANGELOG.md index 1ed8f3a7108d1..991f9c217334a 100644 --- a/src/Symfony/Component/HttpKernel/CHANGELOG.md +++ b/src/Symfony/Component/HttpKernel/CHANGELOG.md @@ -8,6 +8,7 @@ CHANGELOG * Add `ControllerEvent::getAttributes()` to handle attributes on controllers * Add `#[Cache]` to describe the default HTTP cache headers on controllers * Add `absolute_uri` option to surrogate fragment renderers + * Add `ValueResolverInterface` and deprecate `ArgumentValueResolverInterface` 6.1 --- diff --git a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver.php b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver.php index 6359611e5c0cd..6d961318ddb98 100644 --- a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver.php +++ b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver.php @@ -16,6 +16,7 @@ use Symfony\Component\HttpKernel\Controller\ArgumentResolver\RequestAttributeValueResolver; use Symfony\Component\HttpKernel\Controller\ArgumentResolver\RequestValueResolver; use Symfony\Component\HttpKernel\Controller\ArgumentResolver\SessionValueResolver; +use Symfony\Component\HttpKernel\Controller\ArgumentResolver\TraceableValueResolver; use Symfony\Component\HttpKernel\Controller\ArgumentResolver\VariadicValueResolver; use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadataFactory; use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadataFactoryInterface; @@ -31,7 +32,7 @@ final class ArgumentResolver implements ArgumentResolverInterface private iterable $argumentValueResolvers; /** - * @param iterable $argumentValueResolvers + * @param iterable $argumentValueResolvers */ public function __construct(ArgumentMetadataFactoryInterface $argumentMetadataFactory = null, iterable $argumentValueResolvers = []) { @@ -49,24 +50,28 @@ public function getArguments(Request $request, callable $controller): array foreach ($this->argumentMetadataFactory->createArgumentMetadata($controller, ...$reflectors) as $metadata) { foreach ($this->argumentValueResolvers as $resolver) { - if (!$resolver->supports($request, $metadata)) { + if ((!$resolver instanceof ValueResolverInterface || $resolver instanceof TraceableValueResolver) && !$resolver->supports($request, $metadata)) { continue; } - $resolved = $resolver->resolve($request, $metadata); + $count = 0; + foreach ($resolver->resolve($request, $metadata) as $argument) { + ++$count; + $arguments[] = $argument; + } - $atLeastOne = false; - foreach ($resolved as $append) { - $atLeastOne = true; - $arguments[] = $append; + if (1 < $count && !$metadata->isVariadic()) { + throw new \InvalidArgumentException(sprintf('"%s::resolve()" must yield at most one value for non-variadic arguments.', get_debug_type($resolver))); } - if (!$atLeastOne) { - throw new \InvalidArgumentException(sprintf('"%s::resolve()" must yield at least one value.', get_debug_type($resolver))); + if ($count) { + // continue to the next controller argument + continue 2; } - // continue to the next controller argument - continue 2; + if (!$resolver instanceof ValueResolverInterface) { + throw new \InvalidArgumentException(sprintf('"%s::resolve()" must yield at least one value.', get_debug_type($resolver))); + } } $representative = $controller; @@ -74,7 +79,7 @@ public function getArguments(Request $request, callable $controller): array if (\is_array($representative)) { $representative = sprintf('%s::%s()', \get_class($representative[0]), $representative[1]); } elseif (\is_object($representative)) { - $representative = \get_class($representative); + $representative = get_debug_type($representative); } throw new \RuntimeException(sprintf('Controller "%s" requires that you provide a value for the "$%s" argument. Either the argument is nullable and no null value has been provided, no default value has been provided or because there is a non optional argument after this one.', $representative, $metadata->getName())); diff --git a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/BackedEnumValueResolver.php b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/BackedEnumValueResolver.php index 8a84ac130bdca..4f0ca76d30226 100644 --- a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/BackedEnumValueResolver.php +++ b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/BackedEnumValueResolver.php @@ -13,6 +13,7 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface; +use Symfony\Component\HttpKernel\Controller\ValueResolverInterface; use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; @@ -21,11 +22,18 @@ * leading to a 404 Not Found if the attribute value isn't a valid backing value for the enum type. * * @author Maxime Steinhausser + * + * @final since Symfony 6.2 */ -class BackedEnumValueResolver implements ArgumentValueResolverInterface +class BackedEnumValueResolver implements ArgumentValueResolverInterface, ValueResolverInterface { + /** + * @deprecated since Symfony 6.2, use resolve() instead + */ public function supports(Request $request, ArgumentMetadata $argument): bool { + @trigger_deprecation('symfony/http-kernel', '6.2', 'The "%s()" method is deprecated, use "resolve()" instead.', __METHOD__); + if (!is_subclass_of($argument->getType(), \BackedEnum::class)) { return false; } @@ -43,31 +51,43 @@ public function supports(Request $request, ArgumentMetadata $argument): bool public function resolve(Request $request, ArgumentMetadata $argument): iterable { + if (!is_subclass_of($argument->getType(), \BackedEnum::class)) { + return []; + } + + if ($argument->isVariadic()) { + // only target route path parameters, which cannot be variadic. + return []; + } + + // do not support if no value can be resolved at all + // letting the \Symfony\Component\HttpKernel\Controller\ArgumentResolver\DefaultValueResolver be used + // or \Symfony\Component\HttpKernel\Controller\ArgumentResolver fail with a meaningful error. + if (!$request->attributes->has($argument->getName())) { + return []; + } + $value = $request->attributes->get($argument->getName()); if (null === $value) { - yield null; - - return; + return [null]; } if ($value instanceof \BackedEnum) { - yield $value; - - return; + return [$value]; } if (!\is_int($value) && !\is_string($value)) { - throw new \LogicException(sprintf('Could not resolve the "%s $%s" controller argument: expecting an int or string, got %s.', $argument->getType(), $argument->getName(), get_debug_type($value))); + throw new \LogicException(sprintf('Could not resolve the "%s $%s" controller argument: expecting an int or string, got "%s".', $argument->getType(), $argument->getName(), get_debug_type($value))); } /** @var class-string<\BackedEnum> $enumType */ $enumType = $argument->getType(); try { - yield $enumType::from($value); - } catch (\ValueError $error) { - throw new NotFoundHttpException(sprintf('Could not resolve the "%s $%s" controller argument: %s', $argument->getType(), $argument->getName(), $error->getMessage()), $error); + return [$enumType::from($value)]; + } catch (\ValueError $e) { + throw new NotFoundHttpException(sprintf('Could not resolve the "%s $%s" controller argument: ', $argument->getType(), $argument->getName()).$e->getMessage(), $e); } } } diff --git a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/DateTimeValueResolver.php b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/DateTimeValueResolver.php index 55007bef7bc87..d310fee19aaa5 100644 --- a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/DateTimeValueResolver.php +++ b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/DateTimeValueResolver.php @@ -14,6 +14,7 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Attribute\MapDateTime; use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface; +use Symfony\Component\HttpKernel\Controller\ValueResolverInterface; use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; @@ -23,33 +24,35 @@ * @author Benjamin Eberlei * @author Tim Goudriaan */ -final class DateTimeValueResolver implements ArgumentValueResolverInterface +final class DateTimeValueResolver implements ArgumentValueResolverInterface, ValueResolverInterface { /** - * {@inheritdoc} + * @deprecated since Symfony 6.2, use resolve() instead */ public function supports(Request $request, ArgumentMetadata $argument): bool { + @trigger_deprecation('symfony/http-kernel', '6.2', 'The "%s()" method is deprecated, use "resolve()" instead.', __METHOD__); + return is_a($argument->getType(), \DateTimeInterface::class, true) && $request->attributes->has($argument->getName()); } /** * {@inheritdoc} */ - public function resolve(Request $request, ArgumentMetadata $argument): iterable + public function resolve(Request $request, ArgumentMetadata $argument): array { + if (!is_a($argument->getType(), \DateTimeInterface::class, true) || !$request->attributes->has($argument->getName())) { + return []; + } + $value = $request->attributes->get($argument->getName()); if ($value instanceof \DateTimeInterface) { - yield $value; - - return; + return [$value]; } if ($argument->isNullable() && !$value) { - yield null; - - return; + return [null]; } $class = \DateTimeInterface::class === $argument->getType() ? \DateTimeImmutable::class : $argument->getType(); @@ -83,6 +86,6 @@ public function resolve(Request $request, ArgumentMetadata $argument): iterable throw new NotFoundHttpException(sprintf('Invalid date given for parameter "%s".', $argument->getName())); } - yield $date; + return [$date]; } } diff --git a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/DefaultValueResolver.php b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/DefaultValueResolver.php index 32a0e071d6884..01d8cf9255290 100644 --- a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/DefaultValueResolver.php +++ b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/DefaultValueResolver.php @@ -13,6 +13,7 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface; +use Symfony\Component\HttpKernel\Controller\ValueResolverInterface; use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata; /** @@ -20,21 +21,31 @@ * * @author Iltar van der Berg */ -final class DefaultValueResolver implements ArgumentValueResolverInterface +final class DefaultValueResolver implements ArgumentValueResolverInterface, ValueResolverInterface { /** - * {@inheritdoc} + * @deprecated since Symfony 6.2, use resolve() instead */ public function supports(Request $request, ArgumentMetadata $argument): bool { + @trigger_deprecation('symfony/http-kernel', '6.2', 'The "%s()" method is deprecated, use "resolve()" instead.', __METHOD__); + return $argument->hasDefaultValue() || (null !== $argument->getType() && $argument->isNullable() && !$argument->isVariadic()); } /** * {@inheritdoc} */ - public function resolve(Request $request, ArgumentMetadata $argument): iterable + public function resolve(Request $request, ArgumentMetadata $argument): array { - yield $argument->hasDefaultValue() ? $argument->getDefaultValue() : null; + if ($argument->hasDefaultValue()) { + return [$argument->getDefaultValue()]; + } + + if (null !== $argument->getType() && $argument->isNullable() && !$argument->isVariadic()) { + return [null]; + } + + return []; } } diff --git a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/NotTaggedControllerValueResolver.php b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/NotTaggedControllerValueResolver.php index 22a4d2ae74498..7b8293ab4ff4b 100644 --- a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/NotTaggedControllerValueResolver.php +++ b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/NotTaggedControllerValueResolver.php @@ -15,6 +15,7 @@ use Symfony\Component\DependencyInjection\Exception\RuntimeException; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface; +use Symfony\Component\HttpKernel\Controller\ValueResolverInterface; use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata; /** @@ -22,7 +23,7 @@ * * @author Simeon Kolev */ -final class NotTaggedControllerValueResolver implements ArgumentValueResolverInterface +final class NotTaggedControllerValueResolver implements ArgumentValueResolverInterface, ValueResolverInterface { private ContainerInterface $container; @@ -32,10 +33,12 @@ public function __construct(ContainerInterface $container) } /** - * {@inheritdoc} + * @deprecated since Symfony 6.2, use resolve() instead */ public function supports(Request $request, ArgumentMetadata $argument): bool { + @trigger_deprecation('symfony/http-kernel', '6.2', 'The "%s()" method is deprecated, use "resolve()" instead.', __METHOD__); + $controller = $request->attributes->get('_controller'); if (\is_array($controller) && \is_callable($controller, true) && \is_string($controller[0])) { @@ -58,21 +61,28 @@ public function supports(Request $request, ArgumentMetadata $argument): bool /** * {@inheritdoc} */ - public function resolve(Request $request, ArgumentMetadata $argument): iterable + public function resolve(Request $request, ArgumentMetadata $argument): array { - if (\is_array($controller = $request->attributes->get('_controller'))) { + $controller = $request->attributes->get('_controller'); + + if (\is_array($controller) && \is_callable($controller, true) && \is_string($controller[0])) { $controller = $controller[0].'::'.$controller[1]; + } elseif (!\is_string($controller) || '' === $controller) { + return []; } if ('\\' === $controller[0]) { $controller = ltrim($controller, '\\'); } - if (!$this->container->has($controller)) { - $i = strrpos($controller, ':'); + if (!$this->container->has($controller) && false !== $i = strrpos($controller, ':')) { $controller = substr($controller, 0, $i).strtolower(substr($controller, $i)); } + if ($this->container->has($controller)) { + return []; + } + $what = sprintf('argument $%s of "%s()"', $argument->getName(), $controller); $message = sprintf('Could not resolve %s, maybe you forgot to register the controller as a service or missed tagging it with the "controller.service_arguments"?', $what); diff --git a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/RequestAttributeValueResolver.php b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/RequestAttributeValueResolver.php index c62d327b65a2c..2362e0b8c9110 100644 --- a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/RequestAttributeValueResolver.php +++ b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/RequestAttributeValueResolver.php @@ -13,6 +13,7 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface; +use Symfony\Component\HttpKernel\Controller\ValueResolverInterface; use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata; /** @@ -20,21 +21,23 @@ * * @author Iltar van der Berg */ -final class RequestAttributeValueResolver implements ArgumentValueResolverInterface +final class RequestAttributeValueResolver implements ArgumentValueResolverInterface, ValueResolverInterface { /** - * {@inheritdoc} + * @deprecated since Symfony 6.2, use resolve() instead */ public function supports(Request $request, ArgumentMetadata $argument): bool { + @trigger_deprecation('symfony/http-kernel', '6.2', 'The "%s()" method is deprecated, use "resolve()" instead.', __METHOD__); + return !$argument->isVariadic() && $request->attributes->has($argument->getName()); } /** * {@inheritdoc} */ - public function resolve(Request $request, ArgumentMetadata $argument): iterable + public function resolve(Request $request, ArgumentMetadata $argument): array { - yield $request->attributes->get($argument->getName()); + return !$argument->isVariadic() && $request->attributes->has($argument->getName()) ? [$request->attributes->get($argument->getName())] : []; } } diff --git a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/RequestValueResolver.php b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/RequestValueResolver.php index 75cbd97edbbfa..01781430b69d9 100644 --- a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/RequestValueResolver.php +++ b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/RequestValueResolver.php @@ -13,6 +13,7 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface; +use Symfony\Component\HttpKernel\Controller\ValueResolverInterface; use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata; /** @@ -20,21 +21,23 @@ * * @author Iltar van der Berg */ -final class RequestValueResolver implements ArgumentValueResolverInterface +final class RequestValueResolver implements ArgumentValueResolverInterface, ValueResolverInterface { /** - * {@inheritdoc} + * @deprecated since Symfony 6.2, use resolve() instead */ public function supports(Request $request, ArgumentMetadata $argument): bool { + @trigger_deprecation('symfony/http-kernel', '6.2', 'The "%s()" method is deprecated, use "resolve()" instead.', __METHOD__); + return Request::class === $argument->getType() || is_subclass_of($argument->getType(), Request::class); } /** * {@inheritdoc} */ - public function resolve(Request $request, ArgumentMetadata $argument): iterable + public function resolve(Request $request, ArgumentMetadata $argument): array { - yield $request; + return Request::class === $argument->getType() || is_subclass_of($argument->getType(), Request::class) ? [$request] : []; } } diff --git a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/ServiceValueResolver.php b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/ServiceValueResolver.php index 7122a6204581f..a540f160f7554 100644 --- a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/ServiceValueResolver.php +++ b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/ServiceValueResolver.php @@ -15,6 +15,7 @@ use Symfony\Component\DependencyInjection\Exception\RuntimeException; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface; +use Symfony\Component\HttpKernel\Controller\ValueResolverInterface; use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata; /** @@ -22,7 +23,7 @@ * * @author Nicolas Grekas */ -final class ServiceValueResolver implements ArgumentValueResolverInterface +final class ServiceValueResolver implements ArgumentValueResolverInterface, ValueResolverInterface { private ContainerInterface $container; @@ -32,10 +33,12 @@ public function __construct(ContainerInterface $container) } /** - * {@inheritdoc} + * @deprecated since Symfony 6.2, use resolve() instead */ public function supports(Request $request, ArgumentMetadata $argument): bool { + @trigger_deprecation('symfony/http-kernel', '6.2', 'The "%s()" method is deprecated, use "resolve()" instead.', __METHOD__); + $controller = $request->attributes->get('_controller'); if (\is_array($controller) && \is_callable($controller, true) && \is_string($controller[0])) { @@ -58,23 +61,30 @@ public function supports(Request $request, ArgumentMetadata $argument): bool /** * {@inheritdoc} */ - public function resolve(Request $request, ArgumentMetadata $argument): iterable + public function resolve(Request $request, ArgumentMetadata $argument): array { - if (\is_array($controller = $request->attributes->get('_controller'))) { + $controller = $request->attributes->get('_controller'); + + if (\is_array($controller) && \is_callable($controller, true) && \is_string($controller[0])) { $controller = $controller[0].'::'.$controller[1]; + } elseif (!\is_string($controller) || '' === $controller) { + return []; } if ('\\' === $controller[0]) { $controller = ltrim($controller, '\\'); } - if (!$this->container->has($controller)) { - $i = strrpos($controller, ':'); + if (!$this->container->has($controller) && false !== $i = strrpos($controller, ':')) { $controller = substr($controller, 0, $i).strtolower(substr($controller, $i)); } + if (!$this->container->has($controller) || !$this->container->get($controller)->has($argument->getName())) { + return []; + } + try { - yield $this->container->get($controller)->get($argument->getName()); + return [$this->container->get($controller)->get($argument->getName())]; } catch (RuntimeException $e) { $what = sprintf('argument $%s of "%s()"', $argument->getName(), $controller); $message = preg_replace('/service "\.service_locator\.[^"]++"/', $what, $e->getMessage()); diff --git a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/SessionValueResolver.php b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/SessionValueResolver.php index a1e6b431595c3..d8b4fb5c00daa 100644 --- a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/SessionValueResolver.php +++ b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/SessionValueResolver.php @@ -14,6 +14,7 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Session\SessionInterface; use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface; +use Symfony\Component\HttpKernel\Controller\ValueResolverInterface; use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata; /** @@ -21,13 +22,15 @@ * * @author Iltar van der Berg */ -final class SessionValueResolver implements ArgumentValueResolverInterface +final class SessionValueResolver implements ArgumentValueResolverInterface, ValueResolverInterface { /** - * {@inheritdoc} + * @deprecated since Symfony 6.2, use resolve() instead */ public function supports(Request $request, ArgumentMetadata $argument): bool { + @trigger_deprecation('symfony/http-kernel', '6.2', 'The "%s()" method is deprecated, use "resolve()" instead.', __METHOD__); + if (!$request->hasSession()) { return false; } @@ -43,8 +46,17 @@ public function supports(Request $request, ArgumentMetadata $argument): bool /** * {@inheritdoc} */ - public function resolve(Request $request, ArgumentMetadata $argument): iterable + public function resolve(Request $request, ArgumentMetadata $argument): array { - yield $request->getSession(); + if (!$request->hasSession()) { + return []; + } + + $type = $argument->getType(); + if (SessionInterface::class !== $type && !is_subclass_of($type, SessionInterface::class)) { + return []; + } + + return $request->getSession() instanceof $type ? [$request->getSession()] : []; } } diff --git a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/TraceableValueResolver.php b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/TraceableValueResolver.php index a84513223b347..b16f8efa5bb4a 100644 --- a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/TraceableValueResolver.php +++ b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/TraceableValueResolver.php @@ -13,6 +13,7 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface; +use Symfony\Component\HttpKernel\Controller\ValueResolverInterface; use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata; use Symfony\Component\Stopwatch\Stopwatch; @@ -21,22 +22,28 @@ * * @author Iltar van der Berg */ -final class TraceableValueResolver implements ArgumentValueResolverInterface +final class TraceableValueResolver implements ArgumentValueResolverInterface, ValueResolverInterface { private ArgumentValueResolverInterface $inner; private Stopwatch $stopwatch; - public function __construct(ArgumentValueResolverInterface $inner, Stopwatch $stopwatch) + public function __construct(ArgumentValueResolverInterface|ValueResolverInterface $inner, Stopwatch $stopwatch) { $this->inner = $inner; $this->stopwatch = $stopwatch; } /** - * {@inheritdoc} + * @deprecated since Symfony 6.2, use resolve() instead */ public function supports(Request $request, ArgumentMetadata $argument): bool { + if ($this->inner instanceof ValueResolverInterface) { + return true; + } + + @trigger_deprecation('symfony/http-kernel', '6.2', 'The "%s()" method is deprecated, use "resolve()" instead.', __METHOD__); + $method = \get_class($this->inner).'::'.__FUNCTION__; $this->stopwatch->start($method, 'controller.argument_value_resolver'); diff --git a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/UidValueResolver.php b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/UidValueResolver.php index feafa56f2f65a..c7b1f8a43d266 100644 --- a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/UidValueResolver.php +++ b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/UidValueResolver.php @@ -13,17 +13,20 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface; +use Symfony\Component\HttpKernel\Controller\ValueResolverInterface; use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Component\Uid\AbstractUid; -final class UidValueResolver implements ArgumentValueResolverInterface +final class UidValueResolver implements ArgumentValueResolverInterface, ValueResolverInterface { /** - * {@inheritdoc} + * @deprecated since Symfony 6.2, use resolve() instead */ public function supports(Request $request, ArgumentMetadata $argument): bool { + @trigger_deprecation('symfony/http-kernel', '6.2', 'The "%s()" method is deprecated, use "resolve()" instead.', __METHOD__); + return !$argument->isVariadic() && \is_string($request->attributes->get($argument->getName())) && null !== $argument->getType() @@ -33,13 +36,19 @@ public function supports(Request $request, ArgumentMetadata $argument): bool /** * {@inheritdoc} */ - public function resolve(Request $request, ArgumentMetadata $argument): iterable + public function resolve(Request $request, ArgumentMetadata $argument): array { - /** @var class-string $uidClass */ - $uidClass = $argument->getType(); + if ($argument->isVariadic() + || !\is_string($value = $request->attributes->get($argument->getName())) + || null === ($uidClass = $argument->getType()) + || !is_subclass_of($argument->getType(), AbstractUid::class, true) + ) { + return []; + } + /* @var class-string $uidClass */ try { - return [$uidClass::fromString($request->attributes->get($argument->getName()))]; + return [$uidClass::fromString($value)]; } catch (\InvalidArgumentException $e) { throw new NotFoundHttpException(sprintf('The uid for the "%s" parameter is invalid.', $argument->getName()), $e); } diff --git a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/VariadicValueResolver.php b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/VariadicValueResolver.php index a8f7e0f44014e..cf89d4feaf00d 100644 --- a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/VariadicValueResolver.php +++ b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/VariadicValueResolver.php @@ -13,6 +13,7 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface; +use Symfony\Component\HttpKernel\Controller\ValueResolverInterface; use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata; /** @@ -20,27 +21,33 @@ * * @author Iltar van der Berg */ -final class VariadicValueResolver implements ArgumentValueResolverInterface +final class VariadicValueResolver implements ArgumentValueResolverInterface, ValueResolverInterface { /** - * {@inheritdoc} + * @deprecated since Symfony 6.2, use resolve() instead */ public function supports(Request $request, ArgumentMetadata $argument): bool { + @trigger_deprecation('symfony/http-kernel', '6.2', 'The "%s()" method is deprecated, use "resolve()" instead.', __METHOD__); + return $argument->isVariadic() && $request->attributes->has($argument->getName()); } /** * {@inheritdoc} */ - public function resolve(Request $request, ArgumentMetadata $argument): iterable + public function resolve(Request $request, ArgumentMetadata $argument): array { + if (!$argument->isVariadic() || !$request->attributes->has($argument->getName())) { + return []; + } + $values = $request->attributes->get($argument->getName()); if (!\is_array($values)) { throw new \InvalidArgumentException(sprintf('The action argument "...$%1$s" is required to be an array, the request attribute "%1$s" contains a type of "%2$s" instead.', $argument->getName(), get_debug_type($values))); } - yield from $values; + return $values; } } diff --git a/src/Symfony/Component/HttpKernel/Controller/ArgumentValueResolverInterface.php b/src/Symfony/Component/HttpKernel/Controller/ArgumentValueResolverInterface.php index 3570bebf3912e..9c3b1a016218a 100644 --- a/src/Symfony/Component/HttpKernel/Controller/ArgumentValueResolverInterface.php +++ b/src/Symfony/Component/HttpKernel/Controller/ArgumentValueResolverInterface.php @@ -18,6 +18,8 @@ * Responsible for resolving the value of an argument based on its metadata. * * @author Iltar van der Berg + * + * @deprecated since Symfony 6.2, implement ValueResolverInterface instead */ interface ArgumentValueResolverInterface { diff --git a/src/Symfony/Component/HttpKernel/Controller/ValueResolverInterface.php b/src/Symfony/Component/HttpKernel/Controller/ValueResolverInterface.php new file mode 100644 index 0000000000000..a861705cb7bb6 --- /dev/null +++ b/src/Symfony/Component/HttpKernel/Controller/ValueResolverInterface.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Controller; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata; + +/** + * Responsible for resolving the value of an argument based on its metadata. + * + * @author Nicolas Grekas + */ +interface ValueResolverInterface +{ + /** + * Returns the possible value(s). + */ + public function resolve(Request $request, ArgumentMetadata $argument): iterable; +} diff --git a/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/BackedEnumValueResolverTest.php b/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/BackedEnumValueResolverTest.php index 03fbe6b7678ea..dc0645440826c 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/BackedEnumValueResolverTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/BackedEnumValueResolverTest.php @@ -21,12 +21,18 @@ class BackedEnumValueResolverTest extends TestCase { /** + * In Symfony 7, keep this test case but remove the call to supports(). + * + * @group legacy * @dataProvider provideTestSupportsData */ public function testSupports(Request $request, ArgumentMetadata $metadata, bool $expectedSupport) { $resolver = new BackedEnumValueResolver(); + if (!$expectedSupport) { + $this->assertSame([], $resolver->resolve($request, $metadata)); + } self::assertSame($expectedSupport, $resolver->supports($request, $metadata)); } @@ -76,7 +82,7 @@ public function testResolve(Request $request, ArgumentMetadata $metadata, $expec /** @var \Generator $results */ $results = $resolver->resolve($request, $metadata); - self::assertSame($expected, iterator_to_array($results)); + self::assertSame($expected, $results); } public function provideTestResolveData(): iterable @@ -112,9 +118,7 @@ public function testResolveThrowsNotFoundOnInvalidValue() $this->expectException(NotFoundHttpException::class); $this->expectExceptionMessage('Could not resolve the "Symfony\Component\HttpKernel\Tests\Fixtures\Suit $suit" controller argument: "foo" is not a valid backing value for enum'); - /** @var \Generator $results */ - $results = $resolver->resolve($request, $metadata); - iterator_to_array($results); + $resolver->resolve($request, $metadata); } public function testResolveThrowsOnUnexpectedType() @@ -124,11 +128,9 @@ public function testResolveThrowsOnUnexpectedType() $metadata = self::createArgumentMetadata('suit', Suit::class); $this->expectException(\LogicException::class); - $this->expectExceptionMessage('Could not resolve the "Symfony\Component\HttpKernel\Tests\Fixtures\Suit $suit" controller argument: expecting an int or string, got bool.'); + $this->expectExceptionMessage('Could not resolve the "Symfony\Component\HttpKernel\Tests\Fixtures\Suit $suit" controller argument: expecting an int or string, got "bool".'); - /** @var \Generator $results */ - $results = $resolver->resolve($request, $metadata); - iterator_to_array($results); + $resolver->resolve($request, $metadata); } private static function createRequest(array $attributes = []): Request diff --git a/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/DateTimeValueResolverTest.php b/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/DateTimeValueResolverTest.php index e1c3d662c6ece..4d53ec6aff24f 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/DateTimeValueResolverTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/DateTimeValueResolverTest.php @@ -39,6 +39,9 @@ public function getTimeZones() yield ['Etc/GMT-14']; } + /** + * @group legacy + */ public function testSupports() { $resolver = new DateTimeValueResolver(); @@ -56,6 +59,15 @@ public function testSupports() $this->assertFalse($resolver->supports($request, $argument)); } + public function testUnsupportedArgument() + { + $resolver = new DateTimeValueResolver(); + + $argument = new ArgumentMetadata('dummy', \stdClass::class, false, false, null); + $request = self::requestWithAttributes(['dummy' => 'now']); + $this->assertSame([], $resolver->resolve($request, $argument)); + } + /** * @dataProvider getTimeZones */ @@ -67,9 +79,7 @@ public function testFullDate(string $timezone) $argument = new ArgumentMetadata('dummy', \DateTime::class, false, false, null); $request = self::requestWithAttributes(['dummy' => '2012-07-21 00:00:00']); - /** @var \Generator $results */ $results = $resolver->resolve($request, $argument); - $results = iterator_to_array($results); $this->assertCount(1, $results); $this->assertInstanceOf(\DateTime::class, $results[0]); @@ -88,9 +98,7 @@ public function testUnixTimestamp(string $timezone) $argument = new ArgumentMetadata('dummy', \DateTime::class, false, false, null); $request = self::requestWithAttributes(['dummy' => '989541720']); - /** @var \Generator $results */ $results = $resolver->resolve($request, $argument); - $results = iterator_to_array($results); $this->assertCount(1, $results); $this->assertInstanceOf(\DateTime::class, $results[0]); @@ -105,9 +113,7 @@ public function testNullableWithEmptyAttribute() $argument = new ArgumentMetadata('dummy', \DateTime::class, false, false, null, true); $request = self::requestWithAttributes(['dummy' => '']); - /** @var \Generator $results */ $results = $resolver->resolve($request, $argument); - $results = iterator_to_array($results); $this->assertCount(1, $results); $this->assertNull($results[0]); @@ -120,9 +126,7 @@ public function testPreviouslyConvertedAttribute() $argument = new ArgumentMetadata('dummy', \DateTime::class, false, false, null, true); $request = self::requestWithAttributes(['dummy' => $datetime = new \DateTime()]); - /** @var \Generator $results */ $results = $resolver->resolve($request, $argument); - $results = iterator_to_array($results); $this->assertCount(1, $results); $this->assertSame($datetime, $results[0]); @@ -136,9 +140,7 @@ public function testCustomClass() $argument = new ArgumentMetadata('dummy', FooDateTime::class, false, false, null); $request = self::requestWithAttributes(['dummy' => '2016-09-08 00:00:00']); - /** @var \Generator $results */ $results = $resolver->resolve($request, $argument); - $results = iterator_to_array($results); $this->assertCount(1, $results); $this->assertInstanceOf(FooDateTime::class, $results[0]); @@ -156,9 +158,7 @@ public function testDateTimeImmutable(string $timezone) $argument = new ArgumentMetadata('dummy', \DateTimeImmutable::class, false, false, null); $request = self::requestWithAttributes(['dummy' => '2016-09-08 00:00:00 +05:00']); - /** @var \Generator $results */ $results = $resolver->resolve($request, $argument); - $results = iterator_to_array($results); $this->assertCount(1, $results); $this->assertInstanceOf(\DateTimeImmutable::class, $results[0]); @@ -179,9 +179,7 @@ public function testWithFormat(string $timezone) ]); $request = self::requestWithAttributes(['dummy' => '09-08-16 12:34:56']); - /** @var \Generator $results */ $results = $resolver->resolve($request, $argument); - $results = iterator_to_array($results); $this->assertCount(1, $results); $this->assertInstanceOf(\DateTimeImmutable::class, $results[0]); @@ -217,9 +215,7 @@ public function test404Exception(ArgumentMetadata $argument, Request $request) $this->expectException(NotFoundHttpException::class); $this->expectExceptionMessage('Invalid date given for parameter "dummy".'); - /** @var \Generator $results */ - $results = $resolver->resolve($request, $argument); - iterator_to_array($results); + $resolver->resolve($request, $argument); } private static function requestWithAttributes(array $attributes): Request diff --git a/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/NotTaggedControllerValueResolverTest.php b/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/NotTaggedControllerValueResolverTest.php index 3cf2f0f18562e..25214afdfa48a 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/NotTaggedControllerValueResolverTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/NotTaggedControllerValueResolverTest.php @@ -20,6 +20,9 @@ class NotTaggedControllerValueResolverTest extends TestCase { + /** + * @group legacy + */ public function testDoSupportWhenControllerDoNotExists() { $resolver = new NotTaggedControllerValueResolver(new ServiceLocator([])); @@ -29,6 +32,11 @@ public function testDoSupportWhenControllerDoNotExists() $this->assertTrue($resolver->supports($request, $argument)); } + /** + * In Symfony 7, keep this test case but remove the call to supports(). + * + * @group legacy + */ public function testDoNotSupportWhenControllerExists() { $resolver = new NotTaggedControllerValueResolver(new ServiceLocator([ @@ -42,15 +50,21 @@ public function testDoNotSupportWhenControllerExists() ])); $argument = new ArgumentMetadata('dummy', \stdClass::class, false, false, null); $request = $this->requestWithAttributes(['_controller' => 'App\\Controller\\Mine::method']); - + $this->assertSame([], $resolver->resolve($request, $argument)); $this->assertFalse($resolver->supports($request, $argument)); } + /** + * In Symfony 7, keep this test case but remove the call to supports(). + * + * @group legacy + */ public function testDoNotSupportEmptyController() { $resolver = new NotTaggedControllerValueResolver(new ServiceLocator([])); $argument = new ArgumentMetadata('dummy', \stdClass::class, false, false, null); $request = $this->requestWithAttributes(['_controller' => '']); + $this->assertSame([], $resolver->resolve($request, $argument)); $this->assertFalse($resolver->supports($request, $argument)); } @@ -61,7 +75,6 @@ public function testController() $resolver = new NotTaggedControllerValueResolver(new ServiceLocator([])); $argument = new ArgumentMetadata('dummy', \stdClass::class, false, false, null); $request = $this->requestWithAttributes(['_controller' => 'App\\Controller\\Mine::method']); - $this->assertTrue($resolver->supports($request, $argument)); $resolver->resolve($request, $argument); } @@ -72,7 +85,6 @@ public function testControllerWithATrailingBackSlash() $resolver = new NotTaggedControllerValueResolver(new ServiceLocator([])); $argument = new ArgumentMetadata('dummy', \stdClass::class, false, false, null); $request = $this->requestWithAttributes(['_controller' => '\\App\\Controller\\Mine::method']); - $this->assertTrue($resolver->supports($request, $argument)); $resolver->resolve($request, $argument); } @@ -83,7 +95,6 @@ public function testControllerWithMethodNameStartUppercase() $resolver = new NotTaggedControllerValueResolver(new ServiceLocator([])); $argument = new ArgumentMetadata('dummy', \stdClass::class, false, false, null); $request = $this->requestWithAttributes(['_controller' => 'App\\Controller\\Mine::Method']); - $this->assertTrue($resolver->supports($request, $argument)); $resolver->resolve($request, $argument); } @@ -94,7 +105,6 @@ public function testControllerNameIsAnArray() $resolver = new NotTaggedControllerValueResolver(new ServiceLocator([])); $argument = new ArgumentMetadata('dummy', \stdClass::class, false, false, null); $request = $this->requestWithAttributes(['_controller' => ['App\\Controller\\Mine', 'method']]); - $this->assertTrue($resolver->supports($request, $argument)); $resolver->resolve($request, $argument); } diff --git a/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/ServiceValueResolverTest.php b/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/ServiceValueResolverTest.php index 69b9511c05230..7bc4259439e97 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/ServiceValueResolverTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/ServiceValueResolverTest.php @@ -22,12 +22,18 @@ class ServiceValueResolverTest extends TestCase { + /** + * In Symfony 7, keep this test case but remove the call to supports(). + * + * @group legacy + */ public function testDoNotSupportWhenControllerDoNotExists() { $resolver = new ServiceValueResolver(new ServiceLocator([])); $argument = new ArgumentMetadata('dummy', DummyService::class, false, false, null); $request = $this->requestWithAttributes(['_controller' => 'my_controller']); + $this->assertSame([], $resolver->resolve($request, $argument)); $this->assertFalse($resolver->supports($request, $argument)); } @@ -46,8 +52,7 @@ public function testExistingController() $request = $this->requestWithAttributes(['_controller' => 'App\\Controller\\Mine::method']); $argument = new ArgumentMetadata('dummy', DummyService::class, false, false, null); - $this->assertTrue($resolver->supports($request, $argument)); - $this->assertYieldEquals([new DummyService()], $resolver->resolve($request, $argument)); + $this->assertEquals([new DummyService()], $resolver->resolve($request, $argument)); } public function testExistingControllerWithATrailingBackSlash() @@ -65,8 +70,7 @@ public function testExistingControllerWithATrailingBackSlash() $request = $this->requestWithAttributes(['_controller' => '\\App\\Controller\\Mine::method']); $argument = new ArgumentMetadata('dummy', DummyService::class, false, false, null); - $this->assertTrue($resolver->supports($request, $argument)); - $this->assertYieldEquals([new DummyService()], $resolver->resolve($request, $argument)); + $this->assertEquals([new DummyService()], $resolver->resolve($request, $argument)); } public function testExistingControllerWithMethodNameStartUppercase() @@ -83,8 +87,7 @@ public function testExistingControllerWithMethodNameStartUppercase() $request = $this->requestWithAttributes(['_controller' => 'App\\Controller\\Mine::Method']); $argument = new ArgumentMetadata('dummy', DummyService::class, false, false, null); - $this->assertTrue($resolver->supports($request, $argument)); - $this->assertYieldEquals([new DummyService()], $resolver->resolve($request, $argument)); + $this->assertEquals([new DummyService()], $resolver->resolve($request, $argument)); } public function testControllerNameIsAnArray() @@ -102,8 +105,7 @@ public function testControllerNameIsAnArray() $request = $this->requestWithAttributes(['_controller' => ['App\\Controller\\Mine', 'method']]); $argument = new ArgumentMetadata('dummy', DummyService::class, false, false, null); - $this->assertTrue($resolver->supports($request, $argument)); - $this->assertYieldEquals([new DummyService()], $resolver->resolve($request, $argument)); + $this->assertEquals([new DummyService()], $resolver->resolve($request, $argument)); } public function testErrorIsTruncated() @@ -133,16 +135,6 @@ private function requestWithAttributes(array $attributes) return $request; } - - private function assertYieldEquals(array $expected, \Generator $generator) - { - $args = []; - foreach ($generator as $arg) { - $args[] = $arg; - } - - $this->assertEquals($expected, $args); - } } class DummyService diff --git a/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/TraceableValueResolverTest.php b/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/TraceableValueResolverTest.php index 91446c45ab73e..bf5c42f8c2cfa 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/TraceableValueResolverTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/TraceableValueResolverTest.php @@ -20,6 +20,9 @@ class TraceableValueResolverTest extends TestCase { + /** + * @group legacy + */ public function testTimingsInSupports() { $stopwatch = new Stopwatch(); diff --git a/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/UidValueResolverTest.php b/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/UidValueResolverTest.php index 64065699c17bc..766c028873fd1 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/UidValueResolverTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/UidValueResolverTest.php @@ -25,10 +25,17 @@ class UidValueResolverTest extends TestCase { /** + * In Symfony 7, keep this test case but remove the call to supports() + * + * @group legacy * @dataProvider provideSupports */ public function testSupports(bool $expected, Request $request, ArgumentMetadata $argument) { + if (!$expected) { + $this->assertSame([], (new UidValueResolver())->resolve($request, $argument)); + } + $this->assertSame($expected, (new UidValueResolver())->supports($request, $argument)); } diff --git a/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolverTest.php b/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolverTest.php index 1f8b0d68076b5..65ff564f1a90b 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolverTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolverTest.php @@ -173,6 +173,9 @@ public function testGetVariadicArgumentsWithoutArrayInRequest() self::$resolver->getArguments($request, $controller); } + /** + * @group legacy + */ public function testGetArgumentWithoutArray() { $this->expectException(\InvalidArgumentException::class); diff --git a/src/Symfony/Component/HttpKernel/composer.json b/src/Symfony/Component/HttpKernel/composer.json index 30bc75c4e05f8..2c92557cbb6fb 100644 --- a/src/Symfony/Component/HttpKernel/composer.json +++ b/src/Symfony/Component/HttpKernel/composer.json @@ -17,6 +17,7 @@ ], "require": { "php": ">=8.1", + "symfony/deprecation-contracts": "^2.1|^3", "symfony/error-handler": "^6.1", "symfony/event-dispatcher": "^5.4|^6.0", "symfony/http-foundation": "^5.4|^6.0", diff --git a/src/Symfony/Component/Security/Http/AccessToken/QueryAccessTokenExtractor.php b/src/Symfony/Component/Security/Http/AccessToken/QueryAccessTokenExtractor.php index 4e5b14aa735b7..ff558904dc2f0 100644 --- a/src/Symfony/Component/Security/Http/AccessToken/QueryAccessTokenExtractor.php +++ b/src/Symfony/Component/Security/Http/AccessToken/QueryAccessTokenExtractor.php @@ -32,8 +32,7 @@ final class QueryAccessTokenExtractor implements AccessTokenExtractorInterface public function __construct( private readonly string $parameter = self::PARAMETER, - ) - { + ) { } public function extractAccessToken(Request $request): ?string diff --git a/src/Symfony/Component/Security/Http/Controller/UserValueResolver.php b/src/Symfony/Component/Security/Http/Controller/UserValueResolver.php index 9b875f1d07b9b..a4c21f7719854 100644 --- a/src/Symfony/Component/Security/Http/Controller/UserValueResolver.php +++ b/src/Symfony/Component/Security/Http/Controller/UserValueResolver.php @@ -13,6 +13,7 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface; +use Symfony\Component\HttpKernel\Controller\ValueResolverInterface; use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; use Symfony\Component\Security\Core\Exception\AccessDeniedException; @@ -24,7 +25,7 @@ * * @author Iltar van der Berg */ -final class UserValueResolver implements ArgumentValueResolverInterface +final class UserValueResolver implements ArgumentValueResolverInterface, ValueResolverInterface { private TokenStorageInterface $tokenStorage; @@ -33,8 +34,13 @@ public function __construct(TokenStorageInterface $tokenStorage) $this->tokenStorage = $tokenStorage; } + /** + * @deprecated since Symfony 6.2, use resolve() instead + */ public function supports(Request $request, ArgumentMetadata $argument): bool { + @trigger_deprecation('symfony/http-kernel', '6.2', 'The "%s()" method is deprecated, use "resolve()" instead.', __METHOD__); + // with the attribute, the type can be any UserInterface implementation // otherwise, the type must be UserInterface if (UserInterface::class !== $argument->getType() && !$argument->getAttributesOfType(CurrentUser::class, ArgumentMetadata::IS_INSTANCEOF)) { @@ -49,19 +55,31 @@ public function supports(Request $request, ArgumentMetadata $argument): bool return true; } - public function resolve(Request $request, ArgumentMetadata $argument): iterable + public function resolve(Request $request, ArgumentMetadata $argument): array { + // with the attribute, the type can be any UserInterface implementation + // otherwise, the type must be UserInterface + if (UserInterface::class !== $argument->getType() && !$argument->getAttributesOfType(CurrentUser::class, ArgumentMetadata::IS_INSTANCEOF)) { + return []; + } $user = $this->tokenStorage->getToken()?->getUser(); + // if no user is present but a default value exists we delegate to DefaultValueResolver + if ($argument->hasDefaultValue() && null === $user) { + return []; + } + if (null === $user) { if (!$argument->isNullable()) { throw new AccessDeniedException(sprintf('There is no logged-in user to pass to $%s, make the argument nullable if you want to allow anonymous access to the action.', $argument->getName())); } - yield null; - } elseif (null === $argument->getType() || $user instanceof ($argument->getType())) { - yield $user; - } else { - throw new AccessDeniedException(sprintf('The logged-in user is an instance of "%s" but a user of type "%s" is expected.', $user::class, $argument->getType())); + + return [null]; } + if (null === $argument->getType() || $user instanceof ($argument->getType())) { + return [$user]; + } + + throw new AccessDeniedException(sprintf('The logged-in user is an instance of "%s" but a user of type "%s" is expected.', $user::class, $argument->getType())); } } diff --git a/src/Symfony/Component/Security/Http/Firewall/SwitchUserListener.php b/src/Symfony/Component/Security/Http/Firewall/SwitchUserListener.php index 07f684f467d99..1ce9276d43bc7 100644 --- a/src/Symfony/Component/Security/Http/Firewall/SwitchUserListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/SwitchUserListener.php @@ -53,7 +53,7 @@ class SwitchUserListener extends AbstractListener private bool $stateless; private ?string $targetUrl; - public function __construct(TokenStorageInterface $tokenStorage, UserProviderInterface $provider, UserCheckerInterface $userChecker, string $firewallName, AccessDecisionManagerInterface $accessDecisionManager, LoggerInterface $logger = null, string $usernameParameter = '_switch_user', string $role = 'ROLE_ALLOWED_TO_SWITCH', EventDispatcherInterface $dispatcher = null, bool $stateless = false, ?string $targetUrl = null) + public function __construct(TokenStorageInterface $tokenStorage, UserProviderInterface $provider, UserCheckerInterface $userChecker, string $firewallName, AccessDecisionManagerInterface $accessDecisionManager, LoggerInterface $logger = null, string $usernameParameter = '_switch_user', string $role = 'ROLE_ALLOWED_TO_SWITCH', EventDispatcherInterface $dispatcher = null, bool $stateless = false, string $targetUrl = null) { if ('' === $firewallName) { throw new \InvalidArgumentException('$firewallName must not be empty.'); diff --git a/src/Symfony/Component/Security/Http/LoginLink/LoginLinkHandlerInterface.php b/src/Symfony/Component/Security/Http/LoginLink/LoginLinkHandlerInterface.php index 16af51964842c..5edc9b6130b3f 100644 --- a/src/Symfony/Component/Security/Http/LoginLink/LoginLinkHandlerInterface.php +++ b/src/Symfony/Component/Security/Http/LoginLink/LoginLinkHandlerInterface.php @@ -26,7 +26,7 @@ interface LoginLinkHandlerInterface * * @param int|null $lifetime When not null, the argument overrides any default lifetime previously set */ - public function createLoginLink(UserInterface $user, Request $request = null /*, int $lifetime = null */): LoginLinkDetails; + public function createLoginLink(UserInterface $user, Request $request = null /* , int $lifetime = null */): LoginLinkDetails; /** * Validates if this request contains a login link and returns the associated User. diff --git a/src/Symfony/Component/Security/Http/Tests/Authenticator/JsonLoginAuthenticatorTest.php b/src/Symfony/Component/Security/Http/Tests/Authenticator/JsonLoginAuthenticatorTest.php index 150f54a8581ae..ad3f75353731a 100644 --- a/src/Symfony/Component/Security/Http/Tests/Authenticator/JsonLoginAuthenticatorTest.php +++ b/src/Symfony/Component/Security/Http/Tests/Authenticator/JsonLoginAuthenticatorTest.php @@ -146,7 +146,6 @@ public function provideEmptyAuthenticateData() { $request = new Request([], [], [], [], [], ['HTTP_CONTENT_TYPE' => 'application/json'], '{"username": "", "password": "notempty"}'); yield [$request]; - } public function testAuthenticationFailureWithoutTranslator() diff --git a/src/Symfony/Component/Security/Http/Tests/Controller/UserValueResolverTest.php b/src/Symfony/Component/Security/Http/Tests/Controller/UserValueResolverTest.php index 09bbe637322fd..4ee4c0a07d46e 100644 --- a/src/Symfony/Component/Security/Http/Tests/Controller/UserValueResolverTest.php +++ b/src/Symfony/Component/Security/Http/Tests/Controller/UserValueResolverTest.php @@ -27,21 +27,33 @@ class UserValueResolverTest extends TestCase { + /** + * In Symfony 7, keep this test case but remove the call to supports() + * + * @group legacy + */ public function testSupportsFailsWithNoType() { $tokenStorage = new TokenStorage(); $resolver = new UserValueResolver($tokenStorage); $metadata = new ArgumentMetadata('foo', null, false, false, null); + $this->assertSame([], $resolver->resolve(Request::create('/'), $metadata)); $this->assertFalse($resolver->supports(Request::create('/'), $metadata)); } + /** + * In Symfony 7, keep this test case but remove the call to supports() + * + * @group legacy + */ public function testSupportsFailsWhenDefaultValAndNoUser() { $tokenStorage = new TokenStorage(); $resolver = new UserValueResolver($tokenStorage); $metadata = new ArgumentMetadata('foo', UserInterface::class, false, true, new InMemoryUser('username', 'password')); + $this->assertSame([], $resolver->resolve(Request::create('/'), $metadata)); $this->assertFalse($resolver->supports(Request::create('/'), $metadata)); } @@ -55,8 +67,7 @@ public function testResolveSucceedsWithUserInterface() $resolver = new UserValueResolver($tokenStorage); $metadata = new ArgumentMetadata('foo', UserInterface::class, false, false, null); - $this->assertTrue($resolver->supports(Request::create('/'), $metadata)); - $this->assertSame([$user], iterator_to_array($resolver->resolve(Request::create('/'), $metadata))); + $this->assertSame([$user], $resolver->resolve(Request::create('/'), $metadata)); } public function testResolveSucceedsWithSubclassType() @@ -69,8 +80,7 @@ public function testResolveSucceedsWithSubclassType() $resolver = new UserValueResolver($tokenStorage); $metadata = new ArgumentMetadata('foo', InMemoryUser::class, false, false, null, false, [new CurrentUser()]); - $this->assertTrue($resolver->supports(Request::create('/'), $metadata)); - $this->assertSame([$user], iterator_to_array($resolver->resolve(Request::create('/'), $metadata))); + $this->assertSame([$user], $resolver->resolve(Request::create('/'), $metadata)); } public function testResolveSucceedsWithNullableParamAndNoUser() @@ -82,8 +92,7 @@ public function testResolveSucceedsWithNullableParamAndNoUser() $resolver = new UserValueResolver($tokenStorage); $metadata = new ArgumentMetadata('foo', InMemoryUser::class, false, false, null, true, [new CurrentUser()]); - $this->assertTrue($resolver->supports(Request::create('/'), $metadata)); - $this->assertSame([null], iterator_to_array($resolver->resolve(Request::create('/'), $metadata))); + $this->assertSame([null], $resolver->resolve(Request::create('/'), $metadata)); } public function testResolveSucceedsWithNullableAttribute() @@ -97,8 +106,7 @@ public function testResolveSucceedsWithNullableAttribute() $metadata = $this->createMock(ArgumentMetadata::class); $metadata = new ArgumentMetadata('foo', null, false, false, null, false, [new CurrentUser()]); - $this->assertTrue($resolver->supports(Request::create('/'), $metadata)); - $this->assertSame([$user], iterator_to_array($resolver->resolve(Request::create('/'), $metadata))); + $this->assertSame([$user], $resolver->resolve(Request::create('/'), $metadata)); } public function testResolveSucceedsWithTypedAttribute() @@ -112,8 +120,7 @@ public function testResolveSucceedsWithTypedAttribute() $metadata = $this->createMock(ArgumentMetadata::class); $metadata = new ArgumentMetadata('foo', InMemoryUser::class, false, false, null, false, [new CurrentUser()]); - $this->assertTrue($resolver->supports(Request::create('/'), $metadata)); - $this->assertSame([$user], iterator_to_array($resolver->resolve(Request::create('/'), $metadata))); + $this->assertSame([$user], $resolver->resolve(Request::create('/'), $metadata)); } public function testResolveThrowsAccessDeniedWithWrongUserClass() @@ -126,10 +133,9 @@ public function testResolveThrowsAccessDeniedWithWrongUserClass() $resolver = new UserValueResolver($tokenStorage); $metadata = new ArgumentMetadata('foo', InMemoryUser::class, false, false, null, false, [new CurrentUser()]); - $this->assertTrue($resolver->supports(Request::create('/'), $metadata)); $this->expectException(AccessDeniedException::class); $this->expectExceptionMessageMatches('/^The logged-in user is an instance of "Mock_UserInterface[^"]+" but a user of type "Symfony\\\\Component\\\\Security\\\\Core\\\\User\\\\InMemoryUser" is expected.$/'); - iterator_to_array($resolver->resolve(Request::create('/'), $metadata)); + $resolver->resolve(Request::create('/'), $metadata); } public function testResolveThrowsAccessDeniedWithAttributeAndNoUser() @@ -139,10 +145,9 @@ public function testResolveThrowsAccessDeniedWithAttributeAndNoUser() $resolver = new UserValueResolver($tokenStorage); $metadata = new ArgumentMetadata('foo', UserInterface::class, false, false, null, false, [new CurrentUser()]); - $this->assertTrue($resolver->supports(Request::create('/'), $metadata)); $this->expectException(AccessDeniedException::class); $this->expectExceptionMessage('There is no logged-in user to pass to $foo, make the argument nullable if you want to allow anonymous access to the action.'); - iterator_to_array($resolver->resolve(Request::create('/'), $metadata)); + $resolver->resolve(Request::create('/'), $metadata); } public function testResolveThrowsAcessDeniedWithNoToken() @@ -151,9 +156,8 @@ public function testResolveThrowsAcessDeniedWithNoToken() $resolver = new UserValueResolver($tokenStorage); $metadata = new ArgumentMetadata('foo', UserInterface::class, false, false, null); - $this->assertTrue($resolver->supports(Request::create('/'), $metadata)); $this->expectException(AccessDeniedException::class); - iterator_to_array($resolver->resolve(Request::create('/'), $metadata)); + $resolver->resolve(Request::create('/'), $metadata); } public function testIntegration() diff --git a/src/Symfony/Component/Security/Http/composer.json b/src/Symfony/Component/Security/Http/composer.json index 4d7a35f724947..66876a3e38e4b 100644 --- a/src/Symfony/Component/Security/Http/composer.json +++ b/src/Symfony/Component/Security/Http/composer.json @@ -17,6 +17,7 @@ ], "require": { "php": ">=8.1", + "symfony/deprecation-contracts": "^2.1|^3", "symfony/security-core": "^6.0", "symfony/http-foundation": "^5.4|^6.0", "symfony/http-kernel": "^6.2", pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy