vendor/symfony/validator/Mapping/Loader/PropertyInfoLoader.php line 39

  1. <?php
  2. /*
  3.  * This file is part of the Symfony package.
  4.  *
  5.  * (c) Fabien Potencier <fabien@symfony.com>
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  */
  10. namespace Symfony\Component\Validator\Mapping\Loader;
  11. use Symfony\Component\PropertyInfo\PropertyAccessExtractorInterface;
  12. use Symfony\Component\PropertyInfo\PropertyListExtractorInterface;
  13. use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
  14. use Symfony\Component\PropertyInfo\Type as PropertyInfoType;
  15. use Symfony\Component\Validator\Constraints\All;
  16. use Symfony\Component\Validator\Constraints\NotBlank;
  17. use Symfony\Component\Validator\Constraints\NotNull;
  18. use Symfony\Component\Validator\Constraints\Type;
  19. use Symfony\Component\Validator\Mapping\AutoMappingStrategy;
  20. use Symfony\Component\Validator\Mapping\ClassMetadata;
  21. /**
  22.  * Guesses and loads the appropriate constraints using PropertyInfo.
  23.  *
  24.  * @author Kévin Dunglas <dunglas@gmail.com>
  25.  */
  26. final class PropertyInfoLoader implements LoaderInterface
  27. {
  28.     use AutoMappingTrait;
  29.     private PropertyListExtractorInterface $listExtractor;
  30.     private PropertyTypeExtractorInterface $typeExtractor;
  31.     private PropertyAccessExtractorInterface $accessExtractor;
  32.     private ?string $classValidatorRegexp;
  33.     public function __construct(PropertyListExtractorInterface $listExtractorPropertyTypeExtractorInterface $typeExtractorPropertyAccessExtractorInterface $accessExtractorstring $classValidatorRegexp null)
  34.     {
  35.         $this->listExtractor $listExtractor;
  36.         $this->typeExtractor $typeExtractor;
  37.         $this->accessExtractor $accessExtractor;
  38.         $this->classValidatorRegexp $classValidatorRegexp;
  39.     }
  40.     public function loadClassMetadata(ClassMetadata $metadata): bool
  41.     {
  42.         $className $metadata->getClassName();
  43.         if (!$properties $this->listExtractor->getProperties($className)) {
  44.             return false;
  45.         }
  46.         $loaded false;
  47.         $enabledForClass $this->isAutoMappingEnabledForClass($metadata$this->classValidatorRegexp);
  48.         foreach ($properties as $property) {
  49.             if (false === $this->accessExtractor->isWritable($className$property)) {
  50.                 continue;
  51.             }
  52.             if (!property_exists($className$property)) {
  53.                 continue;
  54.             }
  55.             $types $this->typeExtractor->getTypes($className$property);
  56.             if (null === $types) {
  57.                 continue;
  58.             }
  59.             $enabledForProperty $enabledForClass;
  60.             $hasTypeConstraint false;
  61.             $hasNotNullConstraint false;
  62.             $hasNotBlankConstraint false;
  63.             $allConstraint null;
  64.             foreach ($metadata->getPropertyMetadata($property) as $propertyMetadata) {
  65.                 // Enabling or disabling auto-mapping explicitly always takes precedence
  66.                 if (AutoMappingStrategy::DISABLED === $propertyMetadata->getAutoMappingStrategy()) {
  67.                     continue 2;
  68.                 }
  69.                 if (AutoMappingStrategy::ENABLED === $propertyMetadata->getAutoMappingStrategy()) {
  70.                     $enabledForProperty true;
  71.                 }
  72.                 foreach ($propertyMetadata->getConstraints() as $constraint) {
  73.                     if ($constraint instanceof Type) {
  74.                         $hasTypeConstraint true;
  75.                     } elseif ($constraint instanceof NotNull) {
  76.                         $hasNotNullConstraint true;
  77.                     } elseif ($constraint instanceof NotBlank) {
  78.                         $hasNotBlankConstraint true;
  79.                     } elseif ($constraint instanceof All) {
  80.                         $allConstraint $constraint;
  81.                     }
  82.                 }
  83.             }
  84.             if (!$enabledForProperty) {
  85.                 continue;
  86.             }
  87.             $loaded true;
  88.             $builtinTypes = [];
  89.             $nullable false;
  90.             $scalar true;
  91.             foreach ($types as $type) {
  92.                 $builtinTypes[] = $type->getBuiltinType();
  93.                 if ($scalar && !\in_array($type->getBuiltinType(), [PropertyInfoType::BUILTIN_TYPE_INTPropertyInfoType::BUILTIN_TYPE_FLOATPropertyInfoType::BUILTIN_TYPE_STRINGPropertyInfoType::BUILTIN_TYPE_BOOL], true)) {
  94.                     $scalar false;
  95.                 }
  96.                 if (!$nullable && $type->isNullable()) {
  97.                     $nullable true;
  98.                 }
  99.             }
  100.             if (!$hasTypeConstraint) {
  101.                 if (=== \count($builtinTypes)) {
  102.                     if ($types[0]->isCollection() && \count($collectionValueType $types[0]->getCollectionValueTypes()) > 0) {
  103.                         [$collectionValueType] = $collectionValueType;
  104.                         $this->handleAllConstraint($property$allConstraint$collectionValueType$metadata);
  105.                     }
  106.                     $metadata->addPropertyConstraint($property$this->getTypeConstraint($builtinTypes[0], $types[0]));
  107.                 } elseif ($scalar) {
  108.                     $metadata->addPropertyConstraint($property, new Type(['type' => 'scalar']));
  109.                 }
  110.             }
  111.             if (!$nullable && !$hasNotBlankConstraint && !$hasNotNullConstraint) {
  112.                 $metadata->addPropertyConstraint($property, new NotNull());
  113.             }
  114.         }
  115.         return $loaded;
  116.     }
  117.     private function getTypeConstraint(string $builtinTypePropertyInfoType $type): Type
  118.     {
  119.         if (PropertyInfoType::BUILTIN_TYPE_OBJECT === $builtinType && null !== $className $type->getClassName()) {
  120.             return new Type(['type' => $className]);
  121.         }
  122.         return new Type(['type' => $builtinType]);
  123.     }
  124.     private function handleAllConstraint(string $property, ?All $allConstraintPropertyInfoType $propertyInfoTypeClassMetadata $metadata)
  125.     {
  126.         $containsTypeConstraint false;
  127.         $containsNotNullConstraint false;
  128.         if (null !== $allConstraint) {
  129.             foreach ($allConstraint->constraints as $constraint) {
  130.                 if ($constraint instanceof Type) {
  131.                     $containsTypeConstraint true;
  132.                 } elseif ($constraint instanceof NotNull) {
  133.                     $containsNotNullConstraint true;
  134.                 }
  135.             }
  136.         }
  137.         $constraints = [];
  138.         if (!$containsNotNullConstraint && !$propertyInfoType->isNullable()) {
  139.             $constraints[] = new NotNull();
  140.         }
  141.         if (!$containsTypeConstraint) {
  142.             $constraints[] = $this->getTypeConstraint($propertyInfoType->getBuiltinType(), $propertyInfoType);
  143.         }
  144.         if (null === $allConstraint) {
  145.             $metadata->addPropertyConstraint($property, new All(['constraints' => $constraints]));
  146.         } else {
  147.             $allConstraint->constraints array_merge($allConstraint->constraints$constraints);
  148.         }
  149.     }
  150. }