[Validator] Fix auto-mapping constraints should not be validated

This commit is contained in:
Maxime Steinhausser 2019-11-28 16:20:09 +01:00 committed by Nicolas Grekas
parent 2a2d2bc0c0
commit bc53e4bca0
8 changed files with 116 additions and 41 deletions

View File

@ -21,11 +21,12 @@ use Symfony\Bridge\Doctrine\Tests\Fixtures\DoctrineLoaderNoAutoMappingEntity;
use Symfony\Bridge\Doctrine\Tests\Fixtures\DoctrineLoaderParentEntity;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Bridge\Doctrine\Validator\DoctrineLoader;
use Symfony\Component\Validator\Constraints\DisableAutoMapping;
use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Mapping\AutoMappingStrategy;
use Symfony\Component\Validator\Mapping\CascadingStrategy;
use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Mapping\Loader\AutoMappingTrait;
use Symfony\Component\Validator\Mapping\PropertyMetadata;
use Symfony\Component\Validator\Mapping\TraversalStrategy;
use Symfony\Component\Validator\Tests\Fixtures\Entity;
use Symfony\Component\Validator\Validation;
@ -141,11 +142,12 @@ class DoctrineLoaderTest extends TestCase
$this->assertInstanceOf(Length::class, $textFieldConstraints[0]);
$this->assertSame(1000, $textFieldConstraints[0]->max);
/** @var PropertyMetadata[] $noAutoMappingMetadata */
$noAutoMappingMetadata = $classMetadata->getPropertyMetadata('noAutoMapping');
$this->assertCount(1, $noAutoMappingMetadata);
$noAutoMappingConstraints = $noAutoMappingMetadata[0]->getConstraints();
$this->assertCount(1, $noAutoMappingConstraints);
$this->assertInstanceOf(DisableAutoMapping::class, $noAutoMappingConstraints[0]);
$this->assertCount(0, $noAutoMappingConstraints);
$this->assertSame(AutoMappingStrategy::DISABLED, $noAutoMappingMetadata[0]->getAutoMappingStrategy());
}
public function testFieldMappingsConfiguration()
@ -207,13 +209,15 @@ class DoctrineLoaderTest extends TestCase
$classMetadata = $validator->getMetadataFor(new DoctrineLoaderNoAutoMappingEntity());
$classConstraints = $classMetadata->getConstraints();
$this->assertCount(1, $classConstraints);
$this->assertInstanceOf(DisableAutoMapping::class, $classConstraints[0]);
$this->assertCount(0, $classConstraints);
$this->assertSame(AutoMappingStrategy::DISABLED, $classMetadata->getAutoMappingStrategy());
$maxLengthMetadata = $classMetadata->getPropertyMetadata('maxLength');
$this->assertEmpty($maxLengthMetadata);
/** @var PropertyMetadata[] $autoMappingExplicitlyEnabledMetadata */
$autoMappingExplicitlyEnabledMetadata = $classMetadata->getPropertyMetadata('autoMappingExplicitlyEnabled');
$this->assertCount(2, $autoMappingExplicitlyEnabledMetadata[0]->constraints);
$this->assertCount(1, $autoMappingExplicitlyEnabledMetadata[0]->constraints);
$this->assertSame(AutoMappingStrategy::ENABLED, $autoMappingExplicitlyEnabledMetadata[0]->getAutoMappingStrategy());
}
}

View File

@ -16,10 +16,9 @@ use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
use Doctrine\ORM\Mapping\MappingException as OrmMappingException;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Validator\Constraints\DisableAutoMapping;
use Symfony\Component\Validator\Constraints\EnableAutoMapping;
use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Constraints\Valid;
use Symfony\Component\Validator\Mapping\AutoMappingStrategy;
use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Mapping\Loader\AutoMappingTrait;
use Symfony\Component\Validator\Mapping\Loader\LoaderInterface;
@ -76,13 +75,16 @@ final class DoctrineLoader implements LoaderInterface
$enabledForProperty = $enabledForClass;
$lengthConstraint = null;
foreach ($metadata->getPropertyMetadata($mapping['fieldName']) as $propertyMetadata) {
// Enabling or disabling auto-mapping explicitly always takes precedence
if (AutoMappingStrategy::DISABLED === $propertyMetadata->getAutoMappingStrategy()) {
continue 2;
}
if (AutoMappingStrategy::ENABLED === $propertyMetadata->getAutoMappingStrategy()) {
$enabledForProperty = true;
}
foreach ($propertyMetadata->getConstraints() as $constraint) {
// Enabling or disabling auto-mapping explicitly always takes precedence
if ($constraint instanceof DisableAutoMapping) {
continue 3;
} elseif ($constraint instanceof EnableAutoMapping) {
$enabledForProperty = true;
} elseif ($constraint instanceof Length) {
if ($constraint instanceof Length) {
$lengthConstraint = $constraint;
}
}

View File

@ -35,7 +35,7 @@
"symfony/proxy-manager-bridge": "^3.4|^4.0|^5.0",
"symfony/security-core": "^4.4|^5.0",
"symfony/expression-language": "^3.4|^4.0|^5.0",
"symfony/validator": "^4.4|^5.0",
"symfony/validator": "^4.4.1|^5.0.1",
"symfony/var-dumper": "^3.4|^4.0|^5.0",
"symfony/translation": "^3.4|^4.0|^5.0",
"doctrine/annotations": "~1.7",
@ -53,7 +53,7 @@
"symfony/http-kernel": "<4.3.7",
"symfony/messenger": "<4.3",
"symfony/security-core": "<4.4",
"symfony/validator": "<4.4"
"symfony/validator": "<4.4.1|<5.0.1,>=5.0"
},
"suggest": {
"symfony/form": "",

View File

@ -0,0 +1,42 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Validator\Mapping;
/**
* Specifies how the auto-mapping feature should behave.
*
* @author Maxime Steinhausser <maxime.steinhausser@gmail.com>
*/
final class AutoMappingStrategy
{
/**
* Nothing explicitly set, rely on auto-mapping configured regex.
*/
public const NONE = 0;
/**
* Explicitly enabled.
*/
public const ENABLED = 1;
/**
* Explicitly disabled.
*/
public const DISABLED = 2;
/**
* Not instantiable.
*/
private function __construct()
{
}
}

View File

@ -12,6 +12,8 @@
namespace Symfony\Component\Validator\Mapping;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Constraints\DisableAutoMapping;
use Symfony\Component\Validator\Constraints\EnableAutoMapping;
use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints\Traverse;
@ -75,6 +77,19 @@ class GenericMetadata implements MetadataInterface
*/
public $traversalStrategy = TraversalStrategy::NONE;
/**
* Is auto-mapping enabled?
*
* @var int
*
* @see AutoMappingStrategy
*
* @internal This property is public in order to reduce the size of the
* class' serialized representation. Do not access it. Use
* {@link getAutoMappingStrategy()} instead.
*/
public $autoMappingStrategy = AutoMappingStrategy::NONE;
/**
* Returns the names of the properties that should be serialized.
*
@ -87,6 +102,7 @@ class GenericMetadata implements MetadataInterface
'constraintsByGroup',
'cascadingStrategy',
'traversalStrategy',
'autoMappingStrategy',
];
}
@ -139,6 +155,13 @@ class GenericMetadata implements MetadataInterface
return $this;
}
if ($constraint instanceof DisableAutoMapping || $constraint instanceof EnableAutoMapping) {
$this->autoMappingStrategy = $constraint instanceof EnableAutoMapping ? AutoMappingStrategy::ENABLED : AutoMappingStrategy::DISABLED;
// The constraint is not added
return $this;
}
$this->constraints[] = $constraint;
foreach ($constraint->groups as $group) {
@ -213,6 +236,14 @@ class GenericMetadata implements MetadataInterface
return $this->traversalStrategy;
}
/**
* @see AutoMappingStrategy
*/
public function getAutoMappingStrategy(): int
{
return $this->autoMappingStrategy;
}
private function configureLengthConstraints(array $constraints): void
{
$allowEmptyString = true;

View File

@ -11,8 +11,7 @@
namespace Symfony\Component\Validator\Mapping\Loader;
use Symfony\Component\Validator\Constraints\DisableAutoMapping;
use Symfony\Component\Validator\Constraints\EnableAutoMapping;
use Symfony\Component\Validator\Mapping\AutoMappingStrategy;
use Symfony\Component\Validator\Mapping\ClassMetadata;
/**
@ -25,14 +24,8 @@ trait AutoMappingTrait
private function isAutoMappingEnabledForClass(ClassMetadata $metadata, string $classValidatorRegexp = null): bool
{
// Check if AutoMapping constraint is set first
foreach ($metadata->getConstraints() as $constraint) {
if ($constraint instanceof DisableAutoMapping) {
return false;
}
if ($constraint instanceof EnableAutoMapping) {
return true;
}
if (AutoMappingStrategy::NONE !== $strategy = $metadata->getAutoMappingStrategy()) {
return AutoMappingStrategy::ENABLED === $strategy;
}
// Fallback on the config

View File

@ -16,11 +16,10 @@ use Symfony\Component\PropertyInfo\PropertyListExtractorInterface;
use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
use Symfony\Component\PropertyInfo\Type as PropertyInfoType;
use Symfony\Component\Validator\Constraints\All;
use Symfony\Component\Validator\Constraints\DisableAutoMapping;
use Symfony\Component\Validator\Constraints\EnableAutoMapping;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints\NotNull;
use Symfony\Component\Validator\Constraints\Type;
use Symfony\Component\Validator\Mapping\AutoMappingStrategy;
use Symfony\Component\Validator\Mapping\ClassMetadata;
/**
@ -77,16 +76,16 @@ final class PropertyInfoLoader implements LoaderInterface
$hasNotBlankConstraint = false;
$allConstraint = null;
foreach ($metadata->getPropertyMetadata($property) as $propertyMetadata) {
// Enabling or disabling auto-mapping explicitly always takes precedence
if (AutoMappingStrategy::DISABLED === $propertyMetadata->getAutoMappingStrategy()) {
continue 2;
}
if (AutoMappingStrategy::ENABLED === $propertyMetadata->getAutoMappingStrategy()) {
$enabledForProperty = true;
}
foreach ($propertyMetadata->getConstraints() as $constraint) {
// Enabling or disabling auto-mapping explicitly always takes precedence
if ($constraint instanceof DisableAutoMapping) {
continue 3;
}
if ($constraint instanceof EnableAutoMapping) {
$enabledForProperty = true;
}
if ($constraint instanceof Type) {
$hasTypeConstraint = true;
} elseif ($constraint instanceof NotNull) {

View File

@ -15,13 +15,14 @@ use PHPUnit\Framework\TestCase;
use Symfony\Component\PropertyInfo\PropertyInfoExtractorInterface;
use Symfony\Component\PropertyInfo\Type;
use Symfony\Component\Validator\Constraints\All;
use Symfony\Component\Validator\Constraints\DisableAutoMapping;
use Symfony\Component\Validator\Constraints\Iban;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints\NotNull;
use Symfony\Component\Validator\Constraints\Type as TypeConstraint;
use Symfony\Component\Validator\Mapping\AutoMappingStrategy;
use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Mapping\Loader\PropertyInfoLoader;
use Symfony\Component\Validator\Mapping\PropertyMetadata;
use Symfony\Component\Validator\Tests\Fixtures\Entity;
use Symfony\Component\Validator\Tests\Fixtures\PropertyInfoLoaderEntity;
use Symfony\Component\Validator\Tests\Fixtures\PropertyInfoLoaderNoAutoMappingEntity;
@ -164,11 +165,12 @@ class PropertyInfoLoaderTest extends TestCase
$readOnlyMetadata = $classMetadata->getPropertyMetadata('readOnly');
$this->assertEmpty($readOnlyMetadata);
/** @var PropertyMetadata[] $noAutoMappingMetadata */
$noAutoMappingMetadata = $classMetadata->getPropertyMetadata('noAutoMapping');
$this->assertCount(1, $noAutoMappingMetadata);
$this->assertSame(AutoMappingStrategy::DISABLED, $noAutoMappingMetadata[0]->getAutoMappingStrategy());
$noAutoMappingConstraints = $noAutoMappingMetadata[0]->getConstraints();
$this->assertCount(1, $noAutoMappingConstraints);
$this->assertInstanceOf(DisableAutoMapping::class, $noAutoMappingConstraints[0]);
$this->assertCount(0, $noAutoMappingConstraints, 'DisableAutoMapping constraint is not added in the list');
}
/**
@ -222,8 +224,10 @@ class PropertyInfoLoaderTest extends TestCase
->getValidator()
;
/** @var ClassMetadata $classMetadata */
$classMetadata = $validator->getMetadataFor(new PropertyInfoLoaderNoAutoMappingEntity());
$this->assertEmpty($classMetadata->getPropertyMetadata('string'));
$this->assertCount(3, $classMetadata->getPropertyMetadata('autoMappingExplicitlyEnabled')[0]->constraints);
$this->assertCount(2, $classMetadata->getPropertyMetadata('autoMappingExplicitlyEnabled')[0]->constraints);
$this->assertSame(AutoMappingStrategy::ENABLED, $classMetadata->getPropertyMetadata('autoMappingExplicitlyEnabled')[0]->getAutoMappingStrategy());
}
}