bug #35843 [Validator] Add target guards for Composite nested constraints (ogizanagi)
This PR was merged into the 3.4 branch.
Discussion
----------
[Validator] Add target guards for Composite nested constraints
| Q | A
| ------------- | ---
| Branch? | 3.4 <!-- see below -->
| Bug fix? | yes
| New feature? | no <!-- please update src/**/CHANGELOG.md files -->
| Deprecations? | no <!-- please update UPGRADE-*.md and src/**/CHANGELOG.md files -->
| Tickets | Fix https://github.com/symfony/symfony/pull/35815#pullrequestreview-362719298 <!-- prefix each issue number with "Fix #", if any -->
| License | MIT
| Doc PR | N/A
Commits
-------
a08ddf7636
[Validator] Add target guards for Composite nested constraints
This commit is contained in:
commit
92eae57fdb
@ -136,6 +136,17 @@ abstract class Composite extends Constraint
|
|||||||
*/
|
*/
|
||||||
abstract protected function getCompositeOption();
|
abstract protected function getCompositeOption();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal Used by metadata
|
||||||
|
*
|
||||||
|
* @return Constraint[]
|
||||||
|
*/
|
||||||
|
public function getNestedContraints()
|
||||||
|
{
|
||||||
|
/* @var Constraint[] $nestedConstraints */
|
||||||
|
return $this->{$this->getCompositeOption()};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the nested constraints.
|
* Initializes the nested constraints.
|
||||||
*
|
*
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
namespace Symfony\Component\Validator\Mapping;
|
namespace Symfony\Component\Validator\Mapping;
|
||||||
|
|
||||||
use Symfony\Component\Validator\Constraint;
|
use Symfony\Component\Validator\Constraint;
|
||||||
|
use Symfony\Component\Validator\Constraints\Composite;
|
||||||
use Symfony\Component\Validator\Constraints\GroupSequence;
|
use Symfony\Component\Validator\Constraints\GroupSequence;
|
||||||
use Symfony\Component\Validator\Constraints\Traverse;
|
use Symfony\Component\Validator\Constraints\Traverse;
|
||||||
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
|
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
|
||||||
@ -178,9 +179,7 @@ class ClassMetadata extends GenericMetadata implements ClassMetadataInterface
|
|||||||
*/
|
*/
|
||||||
public function addConstraint(Constraint $constraint)
|
public function addConstraint(Constraint $constraint)
|
||||||
{
|
{
|
||||||
if (!\in_array(Constraint::CLASS_CONSTRAINT, (array) $constraint->getTargets())) {
|
$this->checkConstraint($constraint);
|
||||||
throw new ConstraintDefinitionException(sprintf('The constraint "%s" cannot be put on classes.', \get_class($constraint)));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($constraint instanceof Traverse) {
|
if ($constraint instanceof Traverse) {
|
||||||
if ($constraint->traverse) {
|
if ($constraint->traverse) {
|
||||||
@ -495,4 +494,17 @@ class ClassMetadata extends GenericMetadata implements ClassMetadataInterface
|
|||||||
|
|
||||||
$this->members[$property][] = $metadata;
|
$this->members[$property][] = $metadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function checkConstraint(Constraint $constraint)
|
||||||
|
{
|
||||||
|
if (!\in_array(Constraint::CLASS_CONSTRAINT, (array) $constraint->getTargets(), true)) {
|
||||||
|
throw new ConstraintDefinitionException(sprintf('The constraint "%s" cannot be put on classes.', \get_class($constraint)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($constraint instanceof Composite) {
|
||||||
|
foreach ($constraint->getNestedContraints() as $nestedContraint) {
|
||||||
|
$this->checkConstraint($nestedContraint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
namespace Symfony\Component\Validator\Mapping;
|
namespace Symfony\Component\Validator\Mapping;
|
||||||
|
|
||||||
use Symfony\Component\Validator\Constraint;
|
use Symfony\Component\Validator\Constraint;
|
||||||
|
use Symfony\Component\Validator\Constraints\Composite;
|
||||||
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
|
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -71,9 +72,7 @@ abstract class MemberMetadata extends GenericMetadata implements PropertyMetadat
|
|||||||
*/
|
*/
|
||||||
public function addConstraint(Constraint $constraint)
|
public function addConstraint(Constraint $constraint)
|
||||||
{
|
{
|
||||||
if (!\in_array(Constraint::PROPERTY_CONSTRAINT, (array) $constraint->getTargets())) {
|
$this->checkConstraint($constraint);
|
||||||
throw new ConstraintDefinitionException(sprintf('The constraint "%s" cannot be put on properties or getters.', \get_class($constraint)));
|
|
||||||
}
|
|
||||||
|
|
||||||
parent::addConstraint($constraint);
|
parent::addConstraint($constraint);
|
||||||
|
|
||||||
@ -181,4 +180,17 @@ abstract class MemberMetadata extends GenericMetadata implements PropertyMetadat
|
|||||||
* @return \ReflectionMethod|\ReflectionProperty The reflection instance
|
* @return \ReflectionMethod|\ReflectionProperty The reflection instance
|
||||||
*/
|
*/
|
||||||
abstract protected function newReflectionMember($objectOrClassName);
|
abstract protected function newReflectionMember($objectOrClassName);
|
||||||
|
|
||||||
|
private function checkConstraint(Constraint $constraint)
|
||||||
|
{
|
||||||
|
if (!\in_array(Constraint::PROPERTY_CONSTRAINT, (array) $constraint->getTargets(), true)) {
|
||||||
|
throw new ConstraintDefinitionException(sprintf('The constraint "%s" cannot be put on properties or getters.', \get_class($constraint)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($constraint instanceof Composite) {
|
||||||
|
foreach ($constraint->getNestedContraints() as $nestedContraint) {
|
||||||
|
$this->checkConstraint($nestedContraint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,8 +13,11 @@ namespace Symfony\Component\Validator\Tests\Mapping;
|
|||||||
|
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Symfony\Component\Validator\Constraint;
|
use Symfony\Component\Validator\Constraint;
|
||||||
|
use Symfony\Component\Validator\Constraints\Composite;
|
||||||
use Symfony\Component\Validator\Constraints\Valid;
|
use Symfony\Component\Validator\Constraints\Valid;
|
||||||
|
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
|
||||||
use Symfony\Component\Validator\Mapping\ClassMetadata;
|
use Symfony\Component\Validator\Mapping\ClassMetadata;
|
||||||
|
use Symfony\Component\Validator\Tests\Fixtures\ClassConstraint;
|
||||||
use Symfony\Component\Validator\Tests\Fixtures\ConstraintA;
|
use Symfony\Component\Validator\Tests\Fixtures\ConstraintA;
|
||||||
use Symfony\Component\Validator\Tests\Fixtures\ConstraintB;
|
use Symfony\Component\Validator\Tests\Fixtures\ConstraintB;
|
||||||
use Symfony\Component\Validator\Tests\Fixtures\PropertyConstraint;
|
use Symfony\Component\Validator\Tests\Fixtures\PropertyConstraint;
|
||||||
@ -52,6 +55,20 @@ class ClassMetadataTest extends TestCase
|
|||||||
$this->metadata->addConstraint(new PropertyConstraint());
|
$this->metadata->addConstraint(new PropertyConstraint());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testAddCompositeConstraintRejectsNestedPropertyConstraints()
|
||||||
|
{
|
||||||
|
$this->expectException(ConstraintDefinitionException::class);
|
||||||
|
$this->expectExceptionMessage('The constraint "Symfony\Component\Validator\Tests\Fixtures\PropertyConstraint" cannot be put on classes.');
|
||||||
|
|
||||||
|
$this->metadata->addConstraint(new ClassCompositeConstraint([new PropertyConstraint()]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAddCompositeConstraintAcceptsNestedClassConstraints()
|
||||||
|
{
|
||||||
|
$this->metadata->addConstraint($constraint = new ClassCompositeConstraint([new ClassConstraint()]));
|
||||||
|
$this->assertSame($this->metadata->getConstraints(), [$constraint]);
|
||||||
|
}
|
||||||
|
|
||||||
public function testAddPropertyConstraints()
|
public function testAddPropertyConstraints()
|
||||||
{
|
{
|
||||||
$this->metadata->addPropertyConstraint('firstName', new ConstraintA());
|
$this->metadata->addPropertyConstraint('firstName', new ConstraintA());
|
||||||
@ -311,3 +328,23 @@ class ClassMetadataTest extends TestCase
|
|||||||
$this->assertCount(0, $this->metadata->getPropertyMetadata('foo'), '->getPropertyMetadata() returns an empty collection if no metadata is configured for the given property');
|
$this->assertCount(0, $this->metadata->getPropertyMetadata('foo'), '->getPropertyMetadata() returns an empty collection if no metadata is configured for the given property');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ClassCompositeConstraint extends Composite
|
||||||
|
{
|
||||||
|
public $nested;
|
||||||
|
|
||||||
|
public function getDefaultOption()
|
||||||
|
{
|
||||||
|
return $this->getCompositeOption();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getCompositeOption()
|
||||||
|
{
|
||||||
|
return 'nested';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTargets()
|
||||||
|
{
|
||||||
|
return [self::CLASS_CONSTRAINT];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -12,11 +12,16 @@
|
|||||||
namespace Symfony\Component\Validator\Tests\Mapping;
|
namespace Symfony\Component\Validator\Tests\Mapping;
|
||||||
|
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Symfony\Component\Validator\Constraints\Collection;
|
||||||
|
use Symfony\Component\Validator\Constraints\Composite;
|
||||||
|
use Symfony\Component\Validator\Constraints\Required;
|
||||||
use Symfony\Component\Validator\Constraints\Valid;
|
use Symfony\Component\Validator\Constraints\Valid;
|
||||||
|
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
|
||||||
use Symfony\Component\Validator\Mapping\MemberMetadata;
|
use Symfony\Component\Validator\Mapping\MemberMetadata;
|
||||||
use Symfony\Component\Validator\Tests\Fixtures\ClassConstraint;
|
use Symfony\Component\Validator\Tests\Fixtures\ClassConstraint;
|
||||||
use Symfony\Component\Validator\Tests\Fixtures\ConstraintA;
|
use Symfony\Component\Validator\Tests\Fixtures\ConstraintA;
|
||||||
use Symfony\Component\Validator\Tests\Fixtures\ConstraintB;
|
use Symfony\Component\Validator\Tests\Fixtures\ConstraintB;
|
||||||
|
use Symfony\Component\Validator\Tests\Fixtures\PropertyConstraint;
|
||||||
|
|
||||||
class MemberMetadataTest extends TestCase
|
class MemberMetadataTest extends TestCase
|
||||||
{
|
{
|
||||||
@ -43,6 +48,34 @@ class MemberMetadataTest extends TestCase
|
|||||||
$this->metadata->addConstraint(new ClassConstraint());
|
$this->metadata->addConstraint(new ClassConstraint());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testAddCompositeConstraintRejectsNestedClassConstraints()
|
||||||
|
{
|
||||||
|
$this->expectException(ConstraintDefinitionException::class);
|
||||||
|
$this->expectExceptionMessage('The constraint "Symfony\Component\Validator\Tests\Fixtures\ClassConstraint" cannot be put on properties or getters.');
|
||||||
|
|
||||||
|
$this->metadata->addConstraint(new PropertyCompositeConstraint([new ClassConstraint()]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAddCompositeConstraintRejectsDeepNestedClassConstraints()
|
||||||
|
{
|
||||||
|
$this->expectException(ConstraintDefinitionException::class);
|
||||||
|
$this->expectExceptionMessage('The constraint "Symfony\Component\Validator\Tests\Fixtures\ClassConstraint" cannot be put on properties or getters.');
|
||||||
|
|
||||||
|
$this->metadata->addConstraint(new Collection(['field1' => new Required([new ClassConstraint()])]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAddCompositeConstraintAcceptsNestedPropertyConstraints()
|
||||||
|
{
|
||||||
|
$this->metadata->addConstraint($constraint = new PropertyCompositeConstraint([new PropertyConstraint()]));
|
||||||
|
$this->assertSame($this->metadata->getConstraints(), [$constraint]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAddCompositeConstraintAcceptsDeepNestedPropertyConstraints()
|
||||||
|
{
|
||||||
|
$this->metadata->addConstraint($constraint = new Collection(['field1' => new Required([new PropertyConstraint()])]));
|
||||||
|
$this->assertSame($this->metadata->getConstraints(), [$constraint]);
|
||||||
|
}
|
||||||
|
|
||||||
public function testSerialize()
|
public function testSerialize()
|
||||||
{
|
{
|
||||||
$this->metadata->addConstraint(new ConstraintA(['property1' => 'A']));
|
$this->metadata->addConstraint(new ConstraintA(['property1' => 'A']));
|
||||||
@ -82,3 +115,18 @@ class TestMemberMetadata extends MemberMetadata
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class PropertyCompositeConstraint extends Composite
|
||||||
|
{
|
||||||
|
public $nested;
|
||||||
|
|
||||||
|
public function getDefaultOption()
|
||||||
|
{
|
||||||
|
return $this->getCompositeOption();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getCompositeOption()
|
||||||
|
{
|
||||||
|
return 'nested';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user