8000 minor #11485 [Validator] Constraint validators now use the 2.5 API (w… · symfony/symfony@a6a7f5d · GitHub
[go: up one dir, main page]

Skip to content

Commit a6a7f5d

Browse files
committed
minor #11485 [Validator] Constraint validators now use the 2.5 API (webmozart)
This PR was squashed before being merged into the 2.5 branch (closes #11485). Discussion ---------- [Validator] Constraint validators now use the 2.5 API | Q | A | ------------- | --- | Bug fix? | no | New feature? | no | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | - | License | MIT | Doc PR | - See the comments in #11049 for the origin of this PR. Currently, the 2.5 API needs to use `LegacyExecutionContextFactory` because the constraint validators rely on methods from the old `ExecutionContext` class (like `validate()`, `validateValue()`). Consequently it is impossible to switch to the pure 2.5 API and check whether all calls to deprecated methods were removed from application code. This is fixed now. This PR also introduces a complete test suite to test each constraint validator against all three APIs: 2.4, 2.5-BC and 2.5. Currently, some tests are not executed yet when running the complete test suite is run. I expect this to be fixed soon (ticket: sebastianbergmann/phpunit#529, pr: sebastianbergmann/phpunit#1327). Commits ------- 295e5bb [Validator] Fixed failing tests 3bd6d80 [Validator] CS fixes 870a41a [FrameworkBundle] Made ConstraintValidatorFactory aware of the legacy validators 7504448 [Validator] Added extensive test coverage for the constraint validators for the different APIs 8e461af [Validator] Constraint validators now use the 2.5 API. For incompatible validators, legacy validators were created
2 parents c43253e + 295e5bb commit a6a7f5d

File tree

159 files changed

+4607
-1662
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

159 files changed

+4607
-1662
lines changed

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -718,6 +718,7 @@ private function registerValidationConfiguration(array $config, ContainerBuilder
718718
switch ($config['api']) {
719719
case '2.4':
720720
$api = Validation::API_VERSION_2_4;
721+
$container->setParameter('validator.validator_factory.class', $container->getParameter('validator.legacy_validator_factory.class'));
721722
break;
722723
case '2.5':
723724
$api = Validation::API_VERSION_2_5;

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
<parameter key="validator.mapping.cache.apc.class">Symfony\Component\Validator\Mapping\Cache\ApcCache</parameter>
1212
<parameter key="validator.mapping.cache.prefix" />
1313
<parameter key="validator.validator_factory.class">Symfony\Bundle\FrameworkBundle\Validator\ConstraintValidatorFactory</parameter>
14+
<parameter key="validator.legacy_validator_factory.class">Symfony\Bundle\FrameworkBundle\Validator\LegacyConstraintValidatorFactory</parameter>
1415
<parameter key="validator.expression.class">Symfony\Component\Validator\Constraints\ExpressionValidator</parameter>
1516
<parameter key="validator.email.class">Symfony\Component\Validator\Constraints\EmailValidator</parameter>
1617
</parameters>
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\FrameworkBundle\Validator;
13+
14+
use Symfony\Component\DependencyInjection\ContainerInterface;
15+
use Symfony\Component\Validator\Constraint;
16+
use Symfony\Component\Validator\ConstraintValidatorFactoryInterface;
17+
use Symfony\Component\Validator\ConstraintValidatorInterface;
18+
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
19+
20+
/**
21+
* Like {@link ConstraintValidatorFactory}, but aware of services compatible
22+
* with the 2.4 API.
23+
*
24+
* @author Bernhard Schussek <bschussek@gmail.com>
25+
* @author Kris Wallsmith <kris@symfony.com>
26+
*
27+
* @see ConstraintValidatorFactory
28+
*/
29+
class LegacyConstraintValidatorFactory implements ConstraintValidatorFactoryInterface
30+
{
31+
const BASE_NAMESPACE = 'Symfony\\Component\\Validator\\Constraints';
32+
33+
protected $container;
34+
protected $validators;
35+
36+
/**
37+
* Constructor.
38+
*
39+
* @param ContainerInterface $container The service container
40+
* @param array $validators An array of validators
41+
*/
42+
public function __construct(ContainerInterface $container, array $validators = array())
43+
{
44+
$this->container = $container;
45+
$this->validators = $validators;
46+
}
47+
48+
/**
49+
* Returns the validator for the supplied constraint.
50+
*
51+
* @param Constraint $constraint A constraint
52+
*
53+
* @return ConstraintValidatorInterface A validator for the supplied constraint
54+
*
55+
* @throws UnexpectedTypeException When the validator is not an instance of ConstraintValidatorInterface
56+
*/
57+
public function getInstance(Constraint $constraint)
58+
{
59+
$name = $constraint->validatedBy();
60+
61+
if (!isset($this->validators[$name])) {
62+
switch (get_class($constraint)) {
63+
case self::BASE_NAMESPACE.'\\All':
64+
$name = self::BASE_NAMESPACE.'\\LegacyAllValidator';
65+
break;
66+
case self::BASE_NAMESPACE.'\\Choice':
67+
$name = self::BASE_NAMESPACE.'\\LegacyChoiceValidator';
68+
break;
69+
case self::BASE_NAMESPACE.'\\Collection':
70+
$name = self::BASE_NAMESPACE.'\\LegacyCollectionValidator';
71+
break;
72+
case self::BASE_NAMESPACE.'\\Count':
73+
$name = self::BASE_NAMESPACE.'\\LegacyCountValidator';
74+
break;
75+
case self::BASE_NAMESPACE.'\\Length':
76+
$name = self::BASE_NAMESPACE.'\\LegacyLengthValidator';
77+
break;
78+
}
79+
80+
$this->validators[$name] = new $name();
81+
} elseif (is_string($this->validators[$name])) {
82+
$this->validators[$name] = $this->container->get($this->validators[$name]);
83+
}
84+
85+
if (!$this->validators[$name] instanceof ConstraintValidatorInterface) {
86+
throw new UnexpectedTypeException($this->validators[$name], 'Symfony\Component\Validator\ConstraintValidatorInterface');
87+
}
88+
89+
return $this->validators[$name];
90+
}
91+
}

src/Symfony/Component/Validator/Constraints/AllValidator.php

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,10 @@ public function validate($value, Constraint $constraint)
4141

4242
$context = $this->context;
4343
$group = $context->getGroup();
44+
$validator = $context->getValidator()->inContext($context);
4445

4546
foreach ($value as $key => $element) {
46-
foreach ($constraint->constraints as $constr) {
47-
$context->validateValue($element, $constr, '['.$key.']', $group);
48-
}
47+
$validator->atPath('['.$key.']')->validate($element, $constraint->constraints, $group);
4948
}
5049
}
5150
}

src/Symfony/Component/Validator/Constraints/ChoiceValidator.php

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,25 +63,35 @@ public function validate($value, Constraint $constraint)
6363
if ($constraint->multiple) {
6464
foreach ($value as $_value) {
6565
if (!in_array($_value, $choices, $constraint->strict)) {
66-
$this->context->addViolation($constraint->multipleMessage, array('{{ value }}' => $_value));
66+
$this->context->buildViolation($constraint->multipleMessage)
67+
->setParameter('{{ value }}', $_value)
68+
->addViolation();
6769
}
6870
}
6971

7072
$count = count($value);
7173

7274
if ($constraint->min !== null && $count < $constraint->min) {
73-
$this->context->addViolation($constraint->minMessage, array('{{ limit }}' => $constraint->min), null, (int) $constraint->min);
75+
$this->context->buildViolation($constraint->minMessage)
76+
->setParameter('{{ limit }}', $constraint->min)
77+
->setPlural((int) $constraint->min)
78+
->addViolation();
7479

7580
return;
7681
}
7782

7883
if ($constraint->max !== null && $count > $constraint->max) {
79-
$this->context->addViolation($constraint->maxMessage, array('{{ limit }}' => $constraint->max), null, (int) $constraint->max);
84+
$this->context->buildViolation($constraint->maxMessage)
85+
->setParameter('{{ limit }}', $constraint->max)
86+
->setPlural((int) $constraint->max)
87+
->addViolation();
8088

8189
return;
8290
}
8391
} elseif (!in_array($value, $choices, $constraint->strict)) {
84-
$this->context->addViolation($constraint->message, array('{{ value }}' => $value));
92+
$this->context->buildViolation($constraint->message)
93+
->setParameter('{{ value }}', $value)
94+
->addViolation();
8595
}
8696
}
8797
}

src/Symfony/Component/Validator/Constraints/CollectionValidator.php

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -50,29 +50,35 @@ public function validate($value, Constraint $constraint)
5050
// to validate() instead.
5151
$context = $this->context;
5252
$group = $context->getGroup();
53+
$validator = $context->getValidator()->inContext($context);
5354

5455
foreach ($constraint->fields as $field => $fieldConstraint) {
55-
if (
56-
// bug fix issue #2779
57-
(is_array($value) && array_key_exists($field, $value)) ||
58-
($value instanceof \ArrayAccess && $value->offsetExists($field))
59-
) {
60-
foreach ($fieldConstraint->constraints as $constr) {
61-
$context->validateValue($value[$field], $constr, '['.$field.']', $group);
56+
// bug fix issue #2779
57+
$existsInArray = is_array($value) && array_key_exists($field, $value);
58+
$existsInArrayAccess = $value instanceof \ArrayAccess && $value->offsetExists($field);
59+
60+
if ($existsInArray || $existsInArrayAccess) {
61+
if (count($fieldConstraint->constraints) > 0) {
62+
$validator->atPath('['.$field.']')
63+
->validate($value[$field], $fieldConstraint->constraints, $group);
6264
}
6365
} elseif (!$fieldConstraint instanceof Optional && !$constraint->allowMissingFields) {
64-
$context->addViolationAt('['.$field.']', $constraint->missingFieldsMessage, array(
65-
'{{ field }}' => $field
66-
), null);
66+
$context->buildViolation($constraint->missingFieldsMessage)
67+
->atPath('['.$field.']')
68+
->setParameter('{{ field }}', $field)
69+
->setInvalidValue(null)
70+
->addViolation();
6771
}
6872
}
6973

7074
if (!$constraint->allowExtraFields) {
7175
foreach ($value as $field => $fieldValue) {
7276
if (!isset($constraint->fields[$field])) {
73-
$context->addViolationAt('['.$field.']', $constraint->extraFieldsMessage, array(
74-
'{{ field }}' => $field
75-
), $fieldValue);
77+
$context->buildViolation($constraint->extraFieldsMessage)
78+
->atPath('['.$field.']')
79+
->setParameter('{{ field }}', $field)
80+
->setInvalidValue($fieldValue)
81+
->addViolation();
7682
}
7783
}
7884
}

src/Symfony/Component/Validator/Constraints/CountValidator.php

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -36,28 +36,34 @@ public function validate($value, Constraint $constraint)
3636
$count = count($value);
3737

3838
if ($constraint->min == $constraint->max && $count != $constraint->min) {
39-
$this->context->addViolation($constraint->exactMessage, array(
40-
'{{ count }}' => $count,
41-
'{{ limit }}' => $constraint->min,
42-
), $value, (int) $constraint->min);
39+
$this->context->buildViolation($constraint->exactMessage)
40+
->setParameter('{{ count }}', $count)
41+
->setParameter('{{ limit }}', $constraint->min)
42+
->setInvalidValue($value)
43+
->setPlural((int) $constraint->min)
44+
->addViolation();
4345

4446
return;
4547
}
4648

4749
if (null !== $constraint->max && $count > $constraint->max) {
48-
$this->context->addViolation($constraint->maxMessage, array(
49-
'{{ count }}' => $count,
50-
'{{ limit }}' => $constraint->max,
51-
), $value, (int) $constraint->max);
50+
$this->context->buildViolation($constraint->maxMessage)
51+
->setParameter('{{ count }}', $count)
52+
->setParameter('{{ limit }}', $constraint->max)
53+
->setInvalidValue($value)
54+
->setPlural((int) $constraint->max)
55+
->addViolation();
5256

5357
return;
5458
}
5559

5660
if (null !== $constraint->min && $count < $constraint->min) {
57-
$this->context->addViolation($constraint->minMessage, array(
58-
'{{ count }}' => $count,
59-
'{{ limit }}' => $constraint->min,
60-
), $value, (int) $constraint->min);
61+
$this->context->buildViolation($constraint->minMessage)
62+
->setParameter('{{ count }}', $count)
63+
->setParameter('{{ limit }}', $constraint->min)
64+
->setInvalidValue($value)
65+
->setPlural((int) $constraint->min)
66+
->addViolation();
6167
}
6268
}
6369
}
Lines changed: 49 additions & 0 deletions
< 10000 tr class="diff-line-row">
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Validator\Constraints;
13+
14+
use Symfony\Component\Validator\Constraint;
15+
use Symfony\Component\Validator\ConstraintValidator;
16+
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
17+
18+
/**
19+
* @author Bernhard Schussek <bschussek@gmail.com>
20+
*
21+
* @deprecated Deprecated since version 2.5.3, to be removed in 3.0.
22+
*/
23+
class LegacyAllValidator extends ConstraintValidator
24+
{
25+
/**
26+
* {@inheritdoc}
27+
*/
28+
public function validate($value, Constraint $constraint)
29+
{
30+
if (!$constraint instanceof All) {
31+
throw new UnexpectedTypeException($constraint, __NAMESPACE__.'\All');
32+
}
33+
34+
if (null === $value) {
35+
return;
36+
}
37+
38+
if (!is_array($value) && !$value instanceof \Traversable) {
39+
throw new UnexpectedTypeException($value, 'array or Traversable');
40+
}
41+
42+
$context = $this->context;
43+
$group = $context->getGroup();
44+
45+
foreach ($value as $key => $element) {
46+
$context->validateValue($element, $constraint->constraints, '['.$key.']', $group);
47+
}
48+
}
49+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Validator\Constraints;
13+
14+
use Symfony\Component\Validator\Constraint;
15+
use Symfony\Component\Validator\ConstraintValidator;
16+
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
17+
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
18+
19+
/**
20+
* ChoiceValidator validates that the value is one of the expected values.
21+
*
22+
* @author Fabien Potencier <fabien@symfony.com>
23+
* @author Florian Eckerstorfer <florian@eckerstorfer.org>
24+
* @author Bernhard Schussek <bschussek@gmail.com>
25+
*
26+
* @deprecated Deprecated since version 2.5.3, to be removed in 3.0.
27+
*/
28+
class LegacyChoiceValidator extends ConstraintValidator
29+
{
30+
/**
31+
* {@inheritdoc}
32+
*/
33+
public function validate($value, Constraint $constraint)
34+
{
35+
if (!$constraint instanceof Choice) {
36+
throw new UnexpectedTypeException($constraint, __NAMESPACE__.'\Choice');
37+
}
38+
39+
if (!$constraint->choices && !$constraint->callback) {
40+
throw new ConstraintDefinitionException('Either "choices" or "callback" must be specified on constraint Choice');
41+
}
42+
43+
if (null === $value) {
44+
return;
45+
}
46+
47+
if ($constraint->multiple && !is_array($value)) {
48+
throw new UnexpectedTypeException($value, 'array');
49+
}
50+
51+
if ($constraint->callback) {
52+
if (is_callable(array($this->context->getClassName(), $constraint->callback))) {
53+
$choices = call_user_func(array($this->context->getClassName(), $constraint->callback));
54+
} elseif (is_callable($constraint->callback)) {
55+
$choices = call_user_func($constraint->callback);
56+
} else {
57+
throw new ConstraintDefinitionException('The Choice constraint expects a valid callback');
58+
}
59+
} else {
60+
$choices = $constraint->choices;
61+
}
62+
63+
if ($constraint->multiple) {
64+
foreach ($value as $_value) {
65+
if (!in_array($_value, $choices, $constraint->strict)) {
66+
$this->context->addViolation($constraint->multipleMessage, array('{{ value }}' => $_value));
67+
}
68+
}
69+
70+
$count = count($value);
71+
72+
if ($constraint->min !== null && $count < $constraint->min) {
73+
$this->context->addViolation($constraint->minMessage, array('{{ limit }}' => $constraint->min), $value, (int) $constraint->min);
74+
75+
return;
76+
}
77+
78+
if ($constraint->max !== null && $count > $constraint->max) {
79+
$this->context->addViolation($constraint->maxMessage, array('{{ limit }}' => $constraint->max), $value, (int) $constraint->max);
80+
81+
return;
82+
}
83+
} elseif (!in_array($value, $choices, $constraint->strict)) {
84+
$this->context->addViolation($constraint->message, array('{{ value }}' => $value));
85+
}
86+
}
87+
}

0 commit comments

Comments
 (0)
0