diff --git a/src/Symfony/Bridge/Doctrine/Tests/Validator/DoctrineLoaderTest.php b/src/Symfony/Bridge/Doctrine/Tests/Validator/DoctrineLoaderTest.php index 2c87b870a8e77..5ca0ea3bf61ed 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Validator/DoctrineLoaderTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Validator/DoctrineLoaderTest.php @@ -21,11 +21,12 @@ 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 @@ public function testLoadClassMetadata() $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 @@ public function testClassNoAutoMapping() $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()); } } diff --git a/src/Symfony/Bridge/Doctrine/Validator/DoctrineLoader.php b/src/Symfony/Bridge/Doctrine/Validator/DoctrineLoader.php index 7cfde6515ccc8..8a153c551676b 100644 --- a/src/Symfony/Bridge/Doctrine/Validator/DoctrineLoader.php +++ b/src/Symfony/Bridge/Doctrine/Validator/DoctrineLoader.php @@ -16,10 +16,9 @@ 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 @@ public function loadClassMetadata(ClassMetadata $metadata): bool $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; } } diff --git a/src/Symfony/Bridge/Doctrine/composer.json b/src/Symfony/Bridge/Doctrine/composer.json index 8c2d65c72801d..dbb51aca3c6a9 100644 --- a/src/Symfony/Bridge/Doctrine/composer.json +++ b/src/Symfony/Bridge/Doctrine/composer.json @@ -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": "", diff --git a/src/Symfony/Component/Validator/Mapping/AutoMappingStrategy.php b/src/Symfony/Component/Validator/Mapping/AutoMappingStrategy.php new file mode 100644 index 0000000000000..4012ddcff9c05 --- /dev/null +++ b/src/Symfony/Component/Validator/Mapping/AutoMappingStrategy.php @@ -0,0 +1,42 @@ + + * + * 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 + */ +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() + { + } +} diff --git a/src/Symfony/Component/Validator/Mapping/GenericMetadata.php b/src/Symfony/Component/Validator/Mapping/GenericMetadata.php index 43db82ecca0c9..40e447a6f4dde 100644 --- a/src/Symfony/Component/Validator/Mapping/GenericMetadata.php +++ b/src/Symfony/Component/Validator/Mapping/GenericMetadata.php @@ -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 @@ public function __sleep() 'constraintsByGroup', 'cascadingStrategy', 'traversalStrategy', + 'autoMappingStrategy', ]; } @@ -139,6 +155,13 @@ public function addConstraint(Constraint $constraint) 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 @@ public function getTraversalStrategy() return $this->traversalStrategy; } + /** + * @see AutoMappingStrategy + */ + public function getAutoMappingStrategy(): int + { + return $this->autoMappingStrategy; + } + private function configureLengthConstraints(array $constraints): void { $allowEmptyString = true; diff --git a/src/Symfony/Component/Validator/Mapping/Loader/AutoMappingTrait.php b/src/Symfony/Component/Validator/Mapping/Loader/AutoMappingTrait.php index 1c21810763557..66508823234e1 100644 --- a/src/Symfony/Component/Validator/Mapping/Loader/AutoMappingTrait.php +++ b/src/Symfony/Component/Validator/Mapping/Loader/AutoMappingTrait.php @@ -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 diff --git a/src/Symfony/Component/Validator/Mapping/Loader/PropertyInfoLoader.php b/src/Symfony/Component/Validator/Mapping/Loader/PropertyInfoLoader.php index e2ad9cb9ec9e5..530348c638448 100644 --- a/src/Symfony/Component/Validator/Mapping/Loader/PropertyInfoLoader.php +++ b/src/Symfony/Component/Validator/Mapping/Loader/PropertyInfoLoader.php @@ -16,11 +16,10 @@ 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 @@ public function loadClassMetadata(ClassMetadata $metadata): bool $hasNotBlankConstraint = false; $allConstraint = null; foreach ($metadata->getPropertyMetadata($property) as $propertyMetadata) { - foreach ($propertyMetadata->getConstraints() as $constraint) { - // Enabling or disabling auto-mapping explicitly always takes precedence - if ($constraint instanceof DisableAutoMapping) { - continue 3; - } + // Enabling or disabling auto-mapping explicitly always takes precedence + if (AutoMappingStrategy::DISABLED === $propertyMetadata->getAutoMappingStrategy()) { + continue 2; + } - if ($constraint instanceof EnableAutoMapping) { - $enabledForProperty = true; - } + if (AutoMappingStrategy::ENABLED === $propertyMetadata->getAutoMappingStrategy()) { + $enabledForProperty = true; + } + foreach ($propertyMetadata->getConstraints() as $constraint) { if ($constraint instanceof Type) { $hasTypeConstraint = true; } elseif ($constraint instanceof NotNull) { diff --git a/src/Symfony/Component/Validator/Tests/Mapping/Loader/PropertyInfoLoaderTest.php b/src/Symfony/Component/Validator/Tests/Mapping/Loader/PropertyInfoLoaderTest.php index 8e122dcdd872e..755797c21ef63 100644 --- a/src/Symfony/Component/Validator/Tests/Mapping/Loader/PropertyInfoLoaderTest.php +++ b/src/Symfony/Component/Validator/Tests/Mapping/Loader/PropertyInfoLoaderTest.php @@ -15,13 +15,14 @@ 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 @@ public function testLoadClassMetadata() $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 @@ public function testClassNoAutoMapping() ->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()); } }