Skip to content

Commit 69374da

Browse files
committed
feature #21730 [DependencyInjection] Use a service locator in AddConstraintValidatorsPass (GuilhemN)
This PR was squashed before being merged into the 3.3-dev branch (closes #21730). Discussion ---------- [DependencyInjection] Use a service locator in AddConstraintValidatorsPass | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | no | BC breaks? | no | Deprecations? | yes | Tests pass? | yes | Fixed tickets | | License | MIT | Doc PR | Use a service locator to load constraint validators: it allows them to be private. Commits ------- 597b6bc [DependencyInjection] Use a service locator in AddConstraintValidatorsPass
2 parents 8734adc + 597b6bc commit 69374da

File tree

8 files changed

+58
-66
lines changed

8 files changed

+58
-66
lines changed

UPGRADE-3.3.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,11 @@ FrameworkBundle
143143
deprecated and will be removed in 4.0. Use the `Symfony\Component\PropertyInfo\DependencyInjection\PropertyInfoPass`
144144
class instead.
145145

146+
* The `ConstraintValidatorFactory::$validators` and `$container` properties
147+
have been deprecated and will be removed in 4.0.
148+
149+
* Extending `ConstraintValidatorFactory` is deprecated and won't be supported in 4.0.
150+
146151
HttpKernel
147152
-----------
148153

UPGRADE-4.0.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,11 @@ FrameworkBundle
202202
removed. Use the `Symfony\Component\PropertyInfo\DependencyInjection\PropertyInfoPass`
203203
class instead.
204204

205+
* The `ConstraintValidatorFactory::$validators` and `$container` properties
206+
have been removed.
207+
208+
* Extending `ConstraintValidatorFactory` is not supported anymore.
209+
205210
HttpFoundation
206211
---------------
207212

@@ -243,7 +248,7 @@ HttpKernel
243248

244249
* The `Psr6CacheClearer::addPool()` method has been removed. Pass an array of pools indexed
245250
by name to the constructor instead.
246-
251+
247252
* The `LazyLoadingFragmentHandler::addRendererService()` method has been removed.
248253

249254
* The `X-Status-Code` header method of setting a custom status code in the
@@ -310,7 +315,7 @@ Translation
310315
TwigBundle
311316
----------
312317

313-
* The `ContainerAwareRuntimeLoader` class has been removed. Use the
318+
* The `ContainerAwareRuntimeLoader` class has been removed. Use the
314319
Twig `Twig_ContainerRuntimeLoader` class instead.
315320

316321
TwigBridge

src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@ CHANGELOG
1717
* Deprecated `FormPass`, use `Symfony\Component\Form\DependencyInjection\FormPass` instead
1818
* Deprecated `SessionListener`
1919
* Deprecated `TestSessionListener`
20-
* Deprecated `Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ConfigCachePass`.
20+
* Deprecated `Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ConfigCachePass`.
2121
Use `Symfony\Component\Console\DependencyInjection\ConfigCachePass` instead.
2222
* Deprecated `PropertyInfoPass`, use `Symfony\Component\PropertyInfo\DependencyInjection\PropertyInfoPass` instead
23+
* Deprecated extending `ConstraintValidatorFactory`
2324

2425
3.2.0
2526
-----
@@ -31,7 +32,7 @@ CHANGELOG
3132
* Removed `symfony/asset` from the list of required dependencies in `composer.json`
3233
* The `Resources/public/images/*` files have been removed.
3334
* The `Resources/public/css/*.css` files have been removed (they are now inlined in TwigBundle).
34-
* Added possibility to prioritize form type extensions with `'priority'` attribute on tags `form.type_extension`
35+
* Added possibility to prioritize form type extensions with `'priority'` attribute on tags `form.type_extension`
3536

3637
3.1.0
3738
-----

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddConstraintValidatorsPass.php

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,10 @@
1111

1212
namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler;
1313

14+
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
1415
use Symfony\Component\DependencyInjection\ContainerBuilder;
1516
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
16-
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
17+
use Symfony\Component\DependencyInjection\Reference;
1718

1819
class AddConstraintValidatorsPass implements CompilerPassInterface
1920
{
@@ -25,23 +26,19 @@ public function process(ContainerBuilder $container)
2526

2627
$validators = array();
2728
foreach ($container->findTaggedServiceIds('validator.constraint_validator') as $id => $attributes) {
28-
if (isset($attributes[0]['alias'])) {
29-
$validators[$attributes[0]['alias']] = $id;
30-
}
31-
3229
$definition = $container->getDefinition($id);
3330

34-
if (!$definition->isPublic()) {
35-
throw new InvalidArgumentException(sprintf('The service "%s" must be public as it can be lazy-loaded.', $id));
31+
if ($definition->isAbstract()) {
32+
continue;
3633
}
3734

38-
if ($definition->isAbstract()) {
39-
throw new InvalidArgumentException(sprintf('The service "%s" must not be abstract as it can be lazy-loaded.', $id));
35+
if (isset($attributes[0]['alias'])) {
36+
$validators[$attributes[0]['alias']] = new Reference($id);
4037
}
4138

42-
$validators[$definition->getClass()] = $id;
39+
$validators[$definition->getClass()] = new Reference($id);
4340
}
4441

45-
$container->getDefinition('validator.validator_factory')->replaceArgument(1, $validators);
42+
$container->getDefinition('validator.validator_factory')->replaceArgument(0, new ServiceLocatorArgument($validators));
4643
}
4744
}

src/Symfony/Bundle/FrameworkBundle/Resources/config/validator.xml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,7 @@
5757
</service>
5858

5959
<service id="validator.validator_factory" class="Symfony\Bundle\FrameworkBundle\Validator\ConstraintValidatorFactory" public="false">
60-
<argument type="service" id="service_container" />
61-
<argument type="collection" />
60+
<argument type="service-locator" /> <!-- Constraint validators locator -->
6261
</service>
6362

6463
<service id="validator.expression" class="Symfony\Component\Validator\Constraints\ExpressionValidator">

src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/AddConstraintValidatorsPassTest.php

Lines changed: 20 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -13,56 +13,34 @@
1313

1414
use PHPUnit\Framework\TestCase;
1515
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddConstraintValidatorsPass;
16+
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
17+
use Symfony\Component\DependencyInjection\ContainerBuilder;
18+
use Symfony\Component\DependencyInjection\Reference;
1619

1720
class AddConstraintValidatorsPassTest extends TestCase
1821
{
1922
public function testThatConstraintValidatorServicesAreProcessed()
2023
{
21-
$services = array(
22-
'my_constraint_validator_service1' => array(0 => array('alias' => 'my_constraint_validator_alias1')),
23-
'my_constraint_validator_service2' => array(),
24-
);
25-
26-
$validatorFactoryDefinition = $this->getMockBuilder('Symfony\Component\DependencyInjection\Definition')->getMock();
27-
$container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerBuilder')->setMethods(array('findTaggedServiceIds', 'getDefinition', 'hasDefinition'))->getMock();
28-
29-
$validatorDefinition1 = $this->getMockBuilder('Symfony\Component\DependencyInjection\Definition')->setMethods(array('getClass'))->getMock();
30-
$validatorDefinition2 = $this->getMockBuilder('Symfony\Component\DependencyInjection\Definition')->setMethods(array('getClass'))->getMock();
31-
32-
$validatorDefinition1->expects($this->atLeastOnce())
33-
->method('getClass')
34-
->willReturn('My\Fully\Qualified\Class\Named\Validator1');
35-
$validatorDefinition2->expects($this->atLeastOnce())
36-
->method('getClass')
37-
->willReturn('My\Fully\Qualified\Class\Named\Validator2');
38-
39-
$container->expects($this->any())
40-
->method('getDefinition')
41-
->with($this->anything())
42-
->will($this->returnValueMap(array(
43-
array('my_constraint_validator_service1', $validatorDefinition1),
44-
array('my_constraint_validator_service2', $validatorDefinition2),
45-
array('validator.validator_factory', $validatorFactoryDefinition),
46-
)));
47-
48-
$container->expects($this->atLeastOnce())
49-
->method('findTaggedServiceIds')
50-
->will($this->returnValue($services));
51-
$container->expects($this->atLeastOnce())
52-
->method('hasDefinition')
53-
->with('validator.validator_factory')
54-
->will($this->returnValue(true));
55-
56-
$validatorFactoryDefinition->expects($this->once())
57-
->method('replaceArgument')
58-
->with(1, array(
59-
'My\Fully\Qualified\Class\Named\Validator1' => 'my_constraint_validator_service1',
60-
'my_constraint_validator_alias1' => 'my_constraint_validator_service1',
61-
'My\Fully\Qualified\Class\Named\Validator2' => 'my_constraint_validator_service2',
62-
));
24+
$container = new ContainerBuilder();
25+
$validatorFactory = $container->register('validator.validator_factory')
26+
->setArguments(array(new ServiceLocatorArgument()));
27+
28+
$container->register('my_constraint_validator_service1', Validator1::class)
29+
->addTag('validator.constraint_validator', array('alias' => 'my_constraint_validator_alias1'));
30+
$container->register('my_constraint_validator_service2', Validator2::class)
31+
->addTag('validator.constraint_validator');
32+
$container->register('my_abstract_constraint_validator')
33+
->setAbstract(true)
34+
->addTag('validator.constraint_validator');
6335

6436
$addConstraintValidatorsPass = new AddConstraintValidatorsPass();
6537
$addConstraintValidatorsPass->process($container);
38+
39+
$this->assertEquals(new ServiceLocatorArgument(array(
40+
Validator1::class => new Reference('my_constraint_validator_service1'),
41+
'my_constraint_validator_alias1' => new Reference('my_constraint_validator_service1'),
42+
Validator2::class => new Reference('my_constraint_validator_service2'),
43+
)), $validatorFactory->getArgument(0));
6644
}
6745

6846
public function testThatCompilerPassIsIgnoredIfThereIsNoConstraintValidatorFactoryDefinition()

src/Symfony/Bundle/FrameworkBundle/Validator/ConstraintValidatorFactory.php

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
namespace Symfony\Bundle\FrameworkBundle\Validator;
1313

14-
use Symfony\Component\DependencyInjection\ContainerInterface;
14+
use Psr\Container\ContainerInterface;
1515
use Symfony\Component\Validator\Constraint;
1616
use Symfony\Component\Validator\ConstraintValidatorFactoryInterface;
1717
use Symfony\Component\Validator\ConstraintValidatorInterface;
@@ -37,6 +37,8 @@
3737
* }
3838
*
3939
* @author Kris Wallsmith <kris@symfony.com>
40+
*
41+
* @final since version 3.3
4042
*/
4143
class ConstraintValidatorFactory implements ConstraintValidatorFactoryInterface
4244
{
@@ -70,11 +72,15 @@ public function getInstance(Constraint $constraint)
7072
$name = $constraint->validatedBy();
7173

7274
if (!isset($this->validators[$name])) {
73-
if (!class_exists($name)) {
74-
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)));
75-
}
75+
if ($this->container->has($name)) {
76+
$this->validators[$name] = $this->container->get($name);
77+
} else {
78+
if (!class_exists($name)) {
79+
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)));
80+
}
7681

77-
$this->validators[$name] = new $name();
82+
$this->validators[$name] = new $name();
83+
}
7884
} elseif (is_string($this->validators[$name])) {
7985
$this->validators[$name] = $this->container->get($this->validators[$name]);
8086
}

src/Symfony/Bundle/FrameworkBundle/composer.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
"symfony/serializer": "~3.3",
5050
"symfony/translation": "~2.8|~3.0",
5151
"symfony/templating": "~2.8|~3.0",
52-
"symfony/validator": "~3.2",
52+
"symfony/validator": "~3.3",
5353
"symfony/yaml": "~3.2",
5454
"symfony/property-info": "~3.3",
5555
"doctrine/annotations": "~1.0",
@@ -65,7 +65,8 @@
6565
"symfony/console": "<3.3",
6666
"symfony/serializer": "<3.3",
6767
"symfony/form": "<3.3",
68-
"symfony/property-info": "<3.3"
68+
"symfony/property-info": "<3.3",
69+
"symfony/validator": "<3.3"
6970
},
7071
"suggest": {
7172
"ext-apcu": "For best performance of the system caches",

0 commit comments

Comments
 (0)