8000 [Validator] Add target guards for Composite nested constraints · symfony/symfony@a08ddf7 · GitHub
[go: up one dir, main page]

Skip to content

Commit a08ddf7

Browse files
committed
[Validator] Add target guards for Composite nested constraints
1 parent a8b5b15 commit a08ddf7

File tree

5 files changed

+126
-6
lines changed

5 files changed

+126
-6
lines changed

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,17 @@ public function addImplicitGroupName($group)
136136
*/
137137
abstract protected function getCompositeOption();
138138

139+
/**
140+
* @internal Used by metadata
141+
*
142+
* @return Constraint[]
143+
*/
144+
public function getNestedContraints()
145+
{
146+
/* @var Constraint[] $nestedConstraints */
147+
return $this->{$this->getCompositeOption()};
148+
}
149+
139150
/**
140151
* Initializes the nested constraints.
141152
*

src/Symfony/Component/Validator/Mapping/ClassMetadata.php

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\Validator\Mapping;
1313

1414
use Symfony\Component\Validator\Constraint;
15+
use Symfony\Component\Validator\Constraints\Composite;
1516
use Symfony\Component\Validator\Constraints\GroupSequence;
1617
use Symfony\Component\Validator\Constraints\Traverse;
1718
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
@@ -178,9 +179,7 @@ public function getDefaultGroup()
178179
*/
179180
public function addConstraint(Constraint $constraint)
180181
{
181-
if (!\in_array(Constraint::CLASS_CONSTRAINT, (array) $constraint->getTargets())) {
182-
throw new ConstraintDefinitionException(sprintf('The constraint "%s" cannot be put on classes.', \get_class($constraint)));
183-
}
182+
$this->checkConstraint($constraint);
184183

185184
if ($constraint instanceof Traverse) {
186185
if ($constraint->traverse) {
@@ -495,4 +494,17 @@ private function addPropertyMetadata(PropertyMetadataInterface $metadata)
495494

496495
$this->members[$property][] = $metadata;
497496
}
497+
498+
private function checkConstraint(Constraint $constraint)
499+
{
500+
if (!\in_array(Constraint::CLASS_CONSTRAINT, (array) $constraint->getTargets(), true)) {
501+
throw new ConstraintDefinitionException(sprintf('The constraint "%s" cannot be put on classes.', \get_class($constraint)));
502+
}
503+
504+
if ($constraint instanceof Composite) {
505+
foreach ($constraint->getNestedContraints() as $nestedContraint) {
506+
$this->checkConstraint($nestedContraint);
507+
}
508+
}
509+
}
498510
}

src/Symfony/Component/Validator/Mapping/MemberMetadata.php

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\Validator\Mapping;
1313

1414
use Symfony\Component\Validator\Constraint;
15+
use Symfony\Component\Validator\Constraints\Composite;
1516
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
1617

1718
/**
@@ -71,9 +72,7 @@ public function __construct($class, $name, $property)
7172
*/
7273
public function addConstraint(Constraint $constraint)
7374
{
74-
if (!\in_array(Constraint::PROPERTY_CONSTRAINT, (array) $constraint->getTargets())) {
75-
throw new ConstraintDefinitionException(sprintf('The constraint "%s" cannot be put on properties or getters.', \get_class($constraint)));
76-
}
75+
$this->checkConstraint($constraint);
7776

7877
parent::addConstraint($constraint);
7978

@@ -181,4 +180,17 @@ public function getReflectionMember($objectOrClassName)
181180
* @return \ReflectionMethod|\ReflectionProperty The reflection instance
182181
*/
183182
abstract protected function newReflectionMember($objectOrClassName);
183+
184+
private function checkConstraint(Constraint $constraint)
185+
{
186+
if (!\in_array(Constraint::PROPERTY_CONSTRAINT, (array) $constraint->getTargets(), true)) {
187+
throw new ConstraintDefinitionException(sprintf('The constraint "%s" cannot be put on properties or getters.', \get_class($constraint)));
188+
}
189+
190+
if ($constraint instanceof Composite) {
191+
foreach ($constraint->getNestedContraints() as $nestedContraint) {
192+
$this->checkConstraint($nestedContraint);
193+
}
194+
}
195+
}
184196
}

src/Symfony/Component/Validator/Tests/Mapping/ClassMetadataTest.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,11 @@
1313

1414
use PHPUnit\Framework\TestCase;
1515
use Symfony\Component\Validator\Constraint;
16+
use Symfony\Component\Validator\Constraints\Composite;
1617
use Symfony\Component\Validator\Constraints\Valid;
18+
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
1719
use Symfony\Component\Validator\Mapping\ClassMetadata;
20+
use Symfony\Component\Validator\Tests\Fixtures\ClassConstraint;
1821
use Symfony\Component\Validator\Tests\Fixtures\ConstraintA;
1922
use Symfony\Component\Validator\Tests\Fixtures\ConstraintB;
2023
use Symfony\Component\Validator\Tests\Fixtures\PropertyConstraint;
@@ -52,6 +55,20 @@ public function testAddConstraintRequiresClassConstraints()
5255
$this->metadata->addConstraint(new PropertyConstraint());
5356
}
5457

58+
public function testAddCompositeConstraintRejectsNestedPropertyConstraints()
59+
{
60+
$this->expectException(ConstraintDefinitionException::class);
61+
$this->expectExceptionMessage('The constraint "Symfony\Component\Validator\Tests\Fixtures\PropertyConstraint" cannot be put on classes.');
62+
63+
$this->metadata->addConstraint(new ClassCompositeConstraint([new PropertyConstraint()]));
64+
}
65+
66+
public function testAddCompositeConstraintAcceptsNestedClassConstraints()
67+
{
68+
$this->metadata->addConstraint($constraint = new ClassCompositeConstraint([new ClassConstraint()]));
69+
$this->assertSame($this->metadata->getConstraints(), [$constraint]);
70+
}
71+
5572
public function testAddPropertyConstraints()
5673
{
5774
$this->metadata->addPropertyConstraint('firstName', new ConstraintA());
@@ -311,3 +328,23 @@ public function testGetPropertyMetadataReturnsEmptyArrayWithoutConfiguredMetadat
311328
$this->assertCount(0, $this->metadata->getPropertyMetadata('foo'), '->getPropertyMetadata() returns an empty collection if no metadata is configured for the given property');
312329
}
313330
}
331+
332+
class ClassCompositeConstraint extends Composite
333+
{
334+
public $nested;
335+
336+
public function getDefaultOption()
337+
{
338+
return $this->getCompositeOption();
339+
}
340+
341+
protected function getCompositeOption()
342+
{
343+
return 'nested';
344+
}
345+
346+
public function getTargets()
347+
{
348+
return [self::CLASS_CONSTRAINT];
349+
}
350+
}

src/Symfony/Component/Validator/Tests/Mapping/MemberMetadataTest.php

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,16 @@
1212
namespace Symfony\Component\Validator\Tests\Mapping;
1313

1414
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\Validator\Constraints\Collection;
16+
use Symfony\Component\Validator\Constraints\Composite;
17+
use Symfony\Component\Validator\Constraints\Required;
1518
use Symfony\Component\Validator\Constraints\Valid;
19+
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
1620
use Symfony\Component\Validator\Mapping\MemberMetadata;
1721
use Symfony\Component\Validator\Tests\Fixtures\ClassConstraint;
1822
use Symfony\Component\Validator\Tests\Fixtures\ConstraintA;
1923
use Symfony\Component\Validator\Tests\Fixtures\ConstraintB;
24+
use Symfony\Component\Validator\Tests\Fixtures\PropertyConstraint;
2025

2126
class MemberMetadataTest extends TestCase
2227
{
@@ -43,6 +48,34 @@ public function testAddConstraintRequiresClassConstraints()
4348
$this->metadata->addConstraint(new ClassConstraint());
4449
}
4550

51+
public function testAddCompositeConstraintRejectsNestedClassConstraints()
52+
{
53+
$this->expectException(ConstraintDefinitionException::class);
54+
$this->expectExceptionMessage('The constraint "Symfony\Component\Validator\Tests\Fixtures\ClassConstraint" cannot be put on properties or getters.');
55+
56+
$this->metadata->addConstraint(new PropertyCompositeConstraint([new ClassConstraint()]));
57+
}
58+
59+
public function testAddCompositeConstraintRejectsDeepNestedClassConstraints()
60+
{
61+
$this->expectException(ConstraintDefinitionException::class);
62+
$this->expectExceptionMessage('The constraint "Symfony\Component\Validator\Tests\Fixtures\ClassConstraint" cannot be put on properties or getters.');
63+
64+
$this->metadata->addConstraint(new Collection(['field1' => new Required([new ClassConstraint()])]));
65+
}
66+
67+
public function testAddCompositeConstraintAcceptsNestedPropertyConstraints()
68+
{
69+
$this->metadata->addConstraint($constraint = new PropertyCompositeConstraint([new PropertyConstraint()]));
70+
$this->assertSame($this->metadata->getConstraints(), [$constraint]);
71+
}
72+
73+
public function testAddCompositeConstraintAcceptsDeepNestedPropertyConstraints()
74+
{
75+
$this->metadata->addConstraint($constraint = new Collection(['field1' => new Required([new PropertyConstraint()])]));
76+
$this->assertSame($this->metadata->getConstraints(), [$constraint]);
77+
}
78+
4679
public function testSerialize()
4780
{
4881
$this->metadata->addConstraint(new ConstraintA(['property1' => 'A']));
@@ -82,3 +115,18 @@ protected function newReflectionMember($object)
82115
{
83116
}
84117
}
118+
119+
class PropertyCompositeConstraint extends Composite
120+
{
121+
public $nested;
122+
123+
public function getDefaultOption()
124+
{
125+
return $this->getCompositeOption();
126+
}
127+
128+
protected function getCompositeOption()
129+
{
130+
return 'nested';
131+
}
132+
}

0 commit comments

Comments
 (0)
0