From 68c1917af9135f0b4ff170befa13f45e15ca6577 Mon Sep 17 00:00:00 2001 From: Maxime Steinhausser Date: Thu, 25 May 2017 10:01:20 +0200 Subject: [PATCH] [FrameworkBundle][Validator] Move the PSR-11 factory to the component --- UPGRADE-3.3.md | 11 +-- UPGRADE-4.0.md | 12 +-- .../Bundle/FrameworkBundle/CHANGELOG.md | 6 +- .../Resources/config/validator.xml | 2 +- .../ConstraintValidatorFactoryTest.php | 13 ++- .../Validator/ConstraintValidatorFactory.php | 34 +++---- .../Bundle/FrameworkBundle/composer.json | 4 +- src/Symfony/Component/Validator/CHANGELOG.md | 1 + .../ContainerConstraintValidatorFactory.php | 62 +++++++++++++ ...ontainerConstraintValidatorFactoryTest.php | 88 +++++++++++++++++++ 10 files changed, 181 insertions(+), 52 deletions(-) create mode 100644 src/Symfony/Component/Validator/ContainerConstraintValidatorFactory.php create mode 100644 src/Symfony/Component/Validator/Tests/ContainerConstraintValidatorFactoryTest.php diff --git a/UPGRADE-3.3.md b/UPGRADE-3.3.md index 3fd31e5799..05f44e549e 100644 --- a/UPGRADE-3.3.md +++ b/UPGRADE-3.3.md @@ -203,11 +203,6 @@ FrameworkBundle deprecated and will be removed in 4.0. Use the `Symfony\Component\PropertyInfo\DependencyInjection\PropertyInfoPass` class instead. - * The `ConstraintValidatorFactory::$validators` and `$container` properties - have been deprecated and will be removed in 4.0. - - * Extending `ConstraintValidatorFactory` is deprecated and won't be supported in 4.0. - * Class parameters related to routing have been deprecated and will be removed in 4.0. * router.options.generator_class * router.options.generator_base_class @@ -246,9 +241,9 @@ FrameworkBundle class has been deprecated and will be removed in 4.0. Use the `Symfony\Component\Workflow\DependencyInjection\ValidateWorkflowsPass` class instead. - * Passing an array of validators or validator aliases as the second argument of - `ConstraintValidatorFactory::__construct()` is deprecated since 3.3 and will - be removed in 4.0. Use the service locator instead. + * The `Symfony\Bundle\FrameworkBundle\Validator\ConstraintValidatorFactory` + class has been deprecated and will be removed in 4.0. + Use `Symfony\Component\Validator\ContainerConstraintValidatorFactory` instead. HttpFoundation -------------- diff --git a/UPGRADE-4.0.md b/UPGRADE-4.0.md index 566231d6ff..6c10190c7c 100644 --- a/UPGRADE-4.0.md +++ b/UPGRADE-4.0.md @@ -294,15 +294,6 @@ FrameworkBundle removed. Use the `Symfony\Component\PropertyInfo\DependencyInjection\PropertyInfoPass` class instead. - * The `ConstraintValidatorFactory::$validators` and `$container` properties - have been removed. - - * Extending `ConstraintValidatorFactory` is not supported anymore. - - * Passing an array of validators or validator aliases as the second argument of - `ConstraintValidatorFactory::__construct()` has been removed. - Use the service locator instead. - * Class parameters related to routing have been removed * router.options.generator_class * router.options.generator_base_class @@ -335,6 +326,9 @@ FrameworkBundle * The `Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ValidateWorkflowsPass` class has been removed. Use the `Symfony\Component\Workflow\DependencyInjection\ValidateWorkflowsPass` class instead. + + * The `Symfony\Bundle\FrameworkBundle\Validator\ConstraintValidatorFactory` class has been removed. + Use `Symfony\Component\Validator\ContainerConstraintValidatorFactory` instead. HttpFoundation -------------- diff --git a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md index b7473b5812..d25f4547d3 100644 --- a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md +++ b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md @@ -31,7 +31,6 @@ CHANGELOG * Deprecated `Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ConfigCachePass`. Use `Symfony\Component\Console\DependencyInjection\ConfigCachePass` instead. * Deprecated `PropertyInfoPass`, use `Symfony\Component\PropertyInfo\DependencyInjection\PropertyInfoPass` instead - * Deprecated extending `ConstraintValidatorFactory` * Deprecated `ControllerArgumentValueResolverPass`. Use `Symfony\Component\HttpKernel\DependencyInjection\ControllerArgumentValueResolverPass` instead * Deprecated `RoutingResolverPass`, use `Symfony\Component\Routing\DependencyInjection\RoutingResolverPass` instead @@ -47,9 +46,10 @@ CHANGELOG `Symfony\Component\Validator\DependencyInjection\AddValidatorInitializersPass` instead * Deprecated `AddConstraintValidatorsPass`, use `Symfony\Component\Validator\DependencyInjection\AddConstraintValidatorsPass` instead - * Deprecated `ValidateWorkflowsPass`, use + * Deprecated `ValidateWorkflowsPass`, use `Symfony\Component\Workflow\DependencyInjection\ValidateWorkflowsPass` instead - * Deprecated `ConstraintValidatorFactory::__construct()` second argument. + * Deprecated `ConstraintValidatorFactory`, use + `Symfony\Component\Validator\ContainerConstraintValidatorFactory` instead. 3.2.0 ----- diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/validator.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/validator.xml index f895eb4cc0..5f505e859c 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/validator.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/validator.xml @@ -59,7 +59,7 @@ - + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Validator/ConstraintValidatorFactoryTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Validator/ConstraintValidatorFactoryTest.php index 38a1190ade..f619584023 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Validator/ConstraintValidatorFactoryTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Validator/ConstraintValidatorFactoryTest.php @@ -19,6 +19,9 @@ use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Constraints\Blank as BlankConstraint; use Symfony\Component\Validator\ConstraintValidator; +/** + * @group legacy + */ class ConstraintValidatorFactoryTest extends TestCase { public function testGetInstanceCreatesValidator() @@ -27,7 +30,7 @@ class ConstraintValidatorFactoryTest extends TestCase $constraint = $this->getMockBuilder('Symfony\\Component\\Validator\\Constraint')->getMock(); $constraint - ->expects($this->once()) + ->expects($this->exactly(2)) ->method('validatedBy') ->will($this->returnValue($class)); @@ -63,7 +66,7 @@ class ConstraintValidatorFactoryTest extends TestCase $constraint = $this->getMockBuilder(Constraint::class)->getMock(); $constraint - ->expects($this->once()) + ->expects($this->exactly(2)) ->method('validatedBy') ->will($this->returnValue($service)); @@ -71,10 +74,6 @@ class ConstraintValidatorFactoryTest extends TestCase $this->assertSame($validator, $factory->getInstance($constraint)); } - /** - * @group legacy - * @expectedDeprecation Passing an array of validators or validator aliases as the second argument of "Symfony\Bundle\FrameworkBundle\Validator\ConstraintValidatorFactory::__construct" is deprecated since 3.3 and will be removed in 4.0. Use the service locator instead. - */ public function testGetInstanceReturnsServiceWithAlias() { $service = 'validator_constraint_service'; @@ -106,7 +105,7 @@ class ConstraintValidatorFactoryTest extends TestCase { $constraint = $this->getMockBuilder('Symfony\\Component\\Validator\\Constraint')->getMock(); $constraint - ->expects($this->once()) + ->expects($this->exactly(2)) ->method('validatedBy') ->will($this->returnValue('Fully\\Qualified\\ConstraintValidator\\Class\\Name')); diff --git a/src/Symfony/Bundle/FrameworkBundle/Validator/ConstraintValidatorFactory.php b/src/Symfony/Bundle/FrameworkBundle/Validator/ConstraintValidatorFactory.php index 49cd5fe113..1a9020ad68 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Validator/ConstraintValidatorFactory.php +++ b/src/Symfony/Bundle/FrameworkBundle/Validator/ConstraintValidatorFactory.php @@ -13,10 +13,12 @@ namespace Symfony\Bundle\FrameworkBundle\Validator; use Psr\Container\ContainerInterface; use Symfony\Component\Validator\Constraint; -use Symfony\Component\Validator\ConstraintValidatorFactoryInterface; use Symfony\Component\Validator\ConstraintValidatorInterface; -use Symfony\Component\Validator\Exception\ValidatorException; +use Symfony\Component\Validator\ContainerConstraintValidatorFactory; use Symfony\Component\Validator\Exception\UnexpectedTypeException; +use Symfony\Component\Validator\Exception\ValidatorException; + +@trigger_error(sprintf('The %s class is deprecated since version 3.3 and will be removed in 4.0. Use %s instead.', ConstraintValidatorFactory::class, ContainerConstraintValidatorFactory::class), E_USER_DEPRECATED); /** * Uses a service container to create constraint validators. @@ -38,24 +40,19 @@ use Symfony\Component\Validator\Exception\UnexpectedTypeException; * * @author Kris Wallsmith * - * @final since version 3.3 + * @deprecated since version 3.3 */ -class ConstraintValidatorFactory implements ConstraintValidatorFactoryInterface +class ConstraintValidatorFactory extends ContainerConstraintValidatorFactory { protected $container; protected $validators; - public function __construct(ContainerInterface $container, array $validators = null) + public function __construct(ContainerInterface $container, array $validators = array()) { - $this->container = $container; - - if (null !== $validators) { - @trigger_error(sprintf('Passing an array of validators or validator aliases as the second argument of "%s" is deprecated since 3.3 and will be removed in 4.0. Use the service locator instead.', __METHOD__), E_USER_DEPRECATED); - } else { - $validators = array(); - } + parent::__construct($container); $this->validators = $validators; + $this->container = $container; } /** @@ -73,17 +70,10 @@ class ConstraintValidatorFactory implements ConstraintValidatorFactoryInterface $name = $constraint->validatedBy(); if (!isset($this->validators[$name])) { - if ($this->container->has($name)) { - $this->validators[$name] = $this->container->get($name); - } else { - if (!class_exists($name)) { - throw new ValidatorException(sprintf('Constraint validator "%s" does not exist or it is not enabled. Check the "validatedBy" method in your constraint class "%s".', $name, get_class($constraint))); - } + return parent::getInstance($constraint); + } - $this->validators[$name] = new $name(); - } - } elseif (is_string($this->validators[$name])) { - // To be removed in 4.0 + if (is_string($this->validators[$name])) { $this->validators[$name] = $this->container->get($this->validators[$name]); } diff --git a/src/Symfony/Bundle/FrameworkBundle/composer.json b/src/Symfony/Bundle/FrameworkBundle/composer.json index 75725c32b9..70b922c1fc 100644 --- a/src/Symfony/Bundle/FrameworkBundle/composer.json +++ b/src/Symfony/Bundle/FrameworkBundle/composer.json @@ -49,7 +49,7 @@ "symfony/serializer": "~3.3", "symfony/translation": "~3.2", "symfony/templating": "~2.8|~3.0", - "symfony/validator": "~3.3", + "symfony/validator": "~3.3-rc2", "symfony/workflow": "~3.3", "symfony/yaml": "~3.2", "symfony/property-info": "~3.3", @@ -69,7 +69,7 @@ "symfony/property-info": "<3.3", "symfony/serializer": "<3.3", "symfony/translation": "<3.2", - "symfony/validator": "<3.3", + "symfony/validator": "<3.3-rc2", "symfony/workflow": "<3.3" }, "suggest": { diff --git a/src/Symfony/Component/Validator/CHANGELOG.md b/src/Symfony/Component/Validator/CHANGELOG.md index 7099496cfa..14f7b905a8 100644 --- a/src/Symfony/Component/Validator/CHANGELOG.md +++ b/src/Symfony/Component/Validator/CHANGELOG.md @@ -6,6 +6,7 @@ CHANGELOG * added `AddValidatorInitializersPass` * added `AddConstraintValidatorsPass` + * added `ContainerConstraintValidatorFactory` 3.2.0 ----- diff --git a/src/Symfony/Component/Validator/ContainerConstraintValidatorFactory.php b/src/Symfony/Component/Validator/ContainerConstraintValidatorFactory.php new file mode 100644 index 0000000000..a40be361f1 --- /dev/null +++ b/src/Symfony/Component/Validator/ContainerConstraintValidatorFactory.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator; + +use Psr\Container\ContainerInterface; +use Symfony\Component\Validator\Exception\UnexpectedTypeException; +use Symfony\Component\Validator\Exception\ValidatorException; + +/** + * Uses a service container to create constraint validators. + * + * @author Kris Wallsmith + */ +class ContainerConstraintValidatorFactory implements ConstraintValidatorFactoryInterface +{ + private $container; + private $validators; + + public function __construct(ContainerInterface $container) + { + $this->container = $container; + $this->validators = array(); + } + + /** + * {@inheritdoc} + * + * @throws ValidatorException When the validator class does not exist + * @throws UnexpectedTypeException When the validator is not an instance of ConstraintValidatorInterface + */ + public function getInstance(Constraint $constraint) + { + $name = $constraint->validatedBy(); + + if (!isset($this->validators[$name])) { + if ($this->container->has($name)) { + $this->validators[$name] = $this->container->get($name); + } else { + if (!class_exists($name)) { + throw new ValidatorException(sprintf('Constraint validator "%s" does not exist or it is not enabled. Check the "validatedBy" method in your constraint class "%s".', $name, get_class($constraint))); + } + + $this->validators[$name] = new $name(); + } + } + + if (!$this->validators[$name] instanceof ConstraintValidatorInterface) { + throw new UnexpectedTypeException($this->validators[$name], ConstraintValidatorInterface::class); + } + + return $this->validators[$name]; + } +} diff --git a/src/Symfony/Component/Validator/Tests/ContainerConstraintValidatorFactoryTest.php b/src/Symfony/Component/Validator/Tests/ContainerConstraintValidatorFactoryTest.php new file mode 100644 index 0000000000..92091484e2 --- /dev/null +++ b/src/Symfony/Component/Validator/Tests/ContainerConstraintValidatorFactoryTest.php @@ -0,0 +1,88 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\Container; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\Constraints\Blank as BlankConstraint; +use Symfony\Component\Validator\ConstraintValidator; +use Symfony\Component\Validator\ContainerConstraintValidatorFactory; + +class ContainerConstraintValidatorFactoryTest extends TestCase +{ + public function testGetInstanceCreatesValidator() + { + $class = get_class($this->getMockForAbstractClass(ConstraintValidator::class)); + + $constraint = $this->getMockBuilder(Constraint::class)->getMock(); + $constraint + ->expects($this->once()) + ->method('validatedBy') + ->will($this->returnValue($class)); + + $factory = new ContainerConstraintValidatorFactory(new Container()); + $this->assertInstanceOf($class, $factory->getInstance($constraint)); + } + + public function testGetInstanceReturnsExistingValidator() + { + $factory = new ContainerConstraintValidatorFactory(new Container()); + $v1 = $factory->getInstance(new BlankConstraint()); + $v2 = $factory->getInstance(new BlankConstraint()); + $this->assertSame($v1, $v2); + } + + public function testGetInstanceReturnsService() + { + $service = 'validator_constraint_service'; + $validator = $this->getMockForAbstractClass(ConstraintValidator::class); + + // mock ContainerBuilder b/c it implements TaggedContainerInterface + $container = $this->getMockBuilder(ContainerBuilder::class)->setMethods(array('get', 'has'))->getMock(); + $container + ->expects($this->once()) + ->method('get') + ->with($service) + ->willReturn($validator); + $container + ->expects($this->once()) + ->method('has') + ->with($service) + ->willReturn(true); + + $constraint = $this->getMockBuilder(Constraint::class)->getMock(); + $constraint + ->expects($this->once()) + ->method('validatedBy') + ->will($this->returnValue($service)); + + $factory = new ContainerConstraintValidatorFactory($container); + $this->assertSame($validator, $factory->getInstance($constraint)); + } + + /** + * @expectedException \Symfony\Component\Validator\Exception\ValidatorException + */ + public function testGetInstanceInvalidValidatorClass() + { + $constraint = $this->getMockBuilder(Constraint::class)->getMock(); + $constraint + ->expects($this->once()) + ->method('validatedBy') + ->will($this->returnValue('Fully\\Qualified\\ConstraintValidator\\Class\\Name')); + + $factory = new ContainerConstraintValidatorFactory(new Container()); + $factory->getInstance($constraint); + } +}