8000 [Validator] Fixed calling getters before resolving groups · symfony/symfony@5741d3e · GitHub
[go: up one dir, main page]

Skip to content

Commit 5741d3e

Browse files
committed
[Validator] Fixed calling getters before resolving groups
1 parent a562ba2 commit 5741d3e

File tree

5 files changed

+109
-3
lines changed

5 files changed

+109
-3
lines changed

src/Symfony/Component/Validator/Context/ExecutionContext.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
use Symfony\Component\Validator\Mapping\MetadataInterface;
2121
use Symfony\Component\Validator\Mapping\PropertyMetadataInterface;
2222
use Symfony\Component\Validator\Util\PropertyPath;
23+
use Symfony\Component\Validator\Validator\PropertyCandidate;
2324
use Symfony\Component\Validator\Validator\ValidatorInterface;
2425
use Symfony\Component\Validator\Violation\ConstraintViolationBuilder;
2526

@@ -187,7 +188,7 @@ public function addViolation($message, array $parameters = [])
187188
$parameters,
188189
$this->root,
189190
$this->propertyPath,
190-
$this->value,
191+
$this->getValue(),
191192
null,
192193
null,
193194
$this->constraint
@@ -206,7 +207,7 @@ public function buildViolation($message, array $parameters = [])
206207
$parameters,
207208
$this->root,
208209
$this->propertyPath,
209-
$this->value,
210+
$this->getValue(),
210211
$this->translator,
211212
$this->translationDomain
212213
);
@@ -241,6 +242,10 @@ public function getRoot()
241242
*/
242243
public function getValue()
243244
{
245+
if ($this->value instanceof PropertyCandidate) {
246+
return $this->value->getProperty();
247+
}
248+
244249
return $this->value;
245250
}
246251

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
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\Tests\Fixtures;
13+
14+
use Symfony\Component\Validator\Constraints as Assert;
15+
16+
class EntityWithGroupedConstraintOnMethods
17+
{
18+
private $bar;
19+
20+
/**
21+
* @Assert\IsTrue(groups={"Foo"})
22+
*/
23+
public function isValidInFoo()
24+
{
25+
return false;
26+
}
27+
28+
/**
29+
* @Assert\NotNull(groups={"Bar"})
30+
*/
31+
public function getBar()
32+
{
33+
throw new \Exception('Should not be called');
34+
}
35+
}

src/Symfony/Component/Validator/Tests/Validator/RecursiveValidatorTest.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,18 @@
1414
use Symfony\Component\Translation\IdentityTranslator;
1515
use Symfony\Component\Validator\Constraints\All;
1616
use Symfony\Component\Validator\Constraints\Collection;
17+
use Symfony\Component\Validator\Constraints\IsTrue;
1718
use Symfony\Component\Validator\Constraints\Length;
1819
use Symfony\Component\Validator\Constraints\NotBlank;
20+
use Symfony\Component\Validator\Constraints\NotNull;
1921
use Symfony\Component\Validator\ConstraintValidatorFactory;
2022
use Symfony\Component\Validator\Context\ExecutionContextFactory;
23+
use Symfony\Component\Validator\Mapping\ClassMetadata;
2124
use Symfony\Component\Validator\Mapping\Factory\MetadataFactoryInterface;
2225
use Symfony\Component\Validator\Tests\Constraints\Fixtures\ChildA;
2326
use Symfony\Component\Validator\Tests\Constraints\Fixtures\ChildB;
2427
use Symfony\Component\Validator\Tests\Fixtures\Entity;
28+
use Symfony\Component\Validator\Tests\Fixtures\EntityWithGroupedConstraintOnMethods;
2529
use Symfony\Component\Validator\Validator\RecursiveValidator;
2630

2731
class RecursiveValidatorTest extends AbstractTest
@@ -117,6 +121,24 @@ public function testCollectionConstraintValidateAllGroupsForNestedConstraints()
117121
$this->assertInstanceOf(NotBlank::class, $violations->get(1)->getConstraint());
118122
}
119123

124+
public function testGroupedMethodConstraintValidateInSequence()
125+
{
126+
$metadata = new ClassMetadata('Symfony\Component\Validator\Tests\Fixtures\EntityWithGroupedConstraintOnMethods');
127+
$metadata->addGetterMethodConstraint('validInFoo', 'isValidInFoo', new IsTrue(['groups' => 'Foo']));
128+
$metadata->addGetterMethodConstraint('bar', 'getBar', new NotNull(['groups' => 'Bar']));
129+
130+
$this->metadataFactory->addMetadata($metadata);
131+
132+
$entity = new EntityWithGroupedConstraintOnMethods();
133+
134+
$metadata->setGroupSequence(['EntityWithGroupedConstraintOnMethods', 'Foo', 'Bar']);
135+
136+
$violations = $this->validator->validate($entity);
137+
138+
$this->assertCount(1, $violations);
139+
$this->assertInstanceOf('Symfony\Component\Validator\Constraints\IsTrue', $violations->get(0)->getConstraint());
140+
}
141+
120142
public function testAllConstraintValidateAllGroupsForNestedConstraints()
121143
{
122144
$this->metadata->addPropertyConstraint('data', new All(['constraints' => [
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
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\Validator;
13+
14+
/**
15+
* A wrapper for a callable initializing a property from a getter.
16+
*
17+
* @internal
18+
*/
19+
class PropertyCandidate
20+
{
21+
private $propertyCallback;
22+
23+
public function __construct(\Closure $propertyCallback)
24+
{
25+
$this->propertyCallback = $propertyCallback;
26+
}
27+
28+
public function getProperty()
29+
{
30+
return \call_user_func($this->propertyCallback);
31+
}
32+
}

src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
use Symfony\Component\Validator\Mapping\ClassMetadataInterface;
2727
use Symfony\Component\Validator\Mapping\Factory\MetadataFactoryInterface;
2828
use Symfony\Component\Validator\Mapping\GenericMetadata;
29+
use Symfony\Component\Validator\Mapping\GetterMetadata;
2930
use Symfony\Component\Validator\Mapping\MetadataInterface;
3031
use Symfony\Component\Validator\Mapping\PropertyMetadataInterface;
3132
use Symfony\Component\Validator\Mapping\TraversalStrategy;
@@ -534,7 +535,13 @@ private function validateClassNode($object, $cacheKey, ClassMetadataInterface $m
534535
throw new UnsupportedMetadataException(sprintf('The property metadata instances should implement "Symfony\Component\Validator\Mapping\PropertyMetadataInterface", got: "%s".', \is_object($propertyMetadata) ? \get_class($propertyMetadata) : \gettype($propertyMetadata)));
535536
}
536537

537-
$propertyValue = $propertyMetadata->getPropertyValue($object);
538+
if ($propertyMetadata instanceof GetterMetadata) {
539+
$propertyValue = new PropertyCandidate(function () use ($propertyMetadata, $object) {
540+
return $propertyMetadata->getPropertyValue($object);
541+
});
542+
} else {
543+
$propertyValue = $propertyMetadata->getPropertyValue($object);
544+
}
538545

539546
$this->validateGenericNode(
540547
$propertyValue,
@@ -798,6 +805,11 @@ private function validateInGroup($value, $cacheKey, MetadataInterface $metadata,
798805

799806
$validator = $this->validatorFactory->getInstance($constraint);
800807
$validator->initialize($context);
808+
809+
if ($value instanceof PropertyCandidate) {
810+
$value = $value->getProperty();
811+
}
812+
801813
$validator->validate($value, $constraint);
802814
}
803815
}

0 commit comments

Comments
 (0)
0