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

Open in your IDE?
  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 $listExtractor;
  30.     private $typeExtractor;
  31.     private $accessExtractor;
  32.     private $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.     /**
  41.      * {@inheritdoc}
  42.      */
  43.     public function loadClassMetadata(ClassMetadata $metadata): bool
  44.     {
  45.         $className $metadata->getClassName();
  46.         if (!$properties $this->listExtractor->getProperties($className)) {
  47.             return false;
  48.         }
  49.         $loaded false;
  50.         $enabledForClass $this->isAutoMappingEnabledForClass($metadata$this->classValidatorRegexp);
  51.         foreach ($properties as $property) {
  52.             if (false === $this->accessExtractor->isWritable($className$property)) {
  53.                 continue;
  54.             }
  55.             if (!property_exists($className$property)) {
  56.                 continue;
  57.             }
  58.             $types $this->typeExtractor->getTypes($className$property);
  59.             if (null === $types) {
  60.                 continue;
  61.             }
  62.             $enabledForProperty $enabledForClass;
  63.             $hasTypeConstraint false;
  64.             $hasNotNullConstraint false;
  65.             $hasNotBlankConstraint false;
  66.             $allConstraint null;
  67.             foreach ($metadata->getPropertyMetadata($property) as $propertyMetadata) {
  68.                 // Enabling or disabling auto-mapping explicitly always takes precedence
  69.                 if (AutoMappingStrategy::DISABLED === $propertyMetadata->getAutoMappingStrategy()) {
  70.                     continue 2;
  71.                 }
  72.                 if (AutoMappingStrategy::ENABLED === $propertyMetadata->getAutoMappingStrategy()) {
  73.                     $enabledForProperty true;
  74.                 }
  75.                 foreach ($propertyMetadata->getConstraints() as $constraint) {
  76.                     if ($constraint instanceof Type) {
  77.                         $hasTypeConstraint true;
  78.                     } elseif ($constraint instanceof NotNull) {
  79.                         $hasNotNullConstraint true;
  80.                     } elseif ($constraint instanceof NotBlank) {
  81.                         $hasNotBlankConstraint true;
  82.                     } elseif ($constraint instanceof All) {
  83.                         $allConstraint $constraint;
  84.                     }
  85.                 }
  86.             }
  87.             if (!$enabledForProperty) {
  88.                 continue;
  89.             }
  90.             $loaded true;
  91.             $builtinTypes = [];
  92.             $nullable false;
  93.             $scalar true;
  94.             foreach ($types as $type) {
  95.                 $builtinTypes[] = $type->getBuiltinType();
  96.                 if ($scalar && !\in_array($type->getBuiltinType(), [PropertyInfoType::BUILTIN_TYPE_INTPropertyInfoType::BUILTIN_TYPE_FLOATPropertyInfoType::BUILTIN_TYPE_STRINGPropertyInfoType::BUILTIN_TYPE_BOOL], true)) {
  97.                     $scalar false;
  98.                 }
  99.                 if (!$nullable && $type->isNullable()) {
  100.                     $nullable true;
  101.                 }
  102.             }
  103.             if (!$hasTypeConstraint) {
  104.                 if (=== \count($builtinTypes)) {
  105.                     if ($types[0]->isCollection() && \count($collectionValueType $types[0]->getCollectionValueTypes()) > 0) {
  106.                         [$collectionValueType] = $collectionValueType;
  107.                         $this->handleAllConstraint($property$allConstraint$collectionValueType$metadata);
  108.                     }
  109.                     $metadata->addPropertyConstraint($property$this->getTypeConstraint($builtinTypes[0], $types[0]));
  110.                 } elseif ($scalar) {
  111.                     $metadata->addPropertyConstraint($property, new Type(['type' => 'scalar']));
  112.                 }
  113.             }
  114.             if (!$nullable && !$hasNotBlankConstraint && !$hasNotNullConstraint) {
  115.                 $metadata->addPropertyConstraint($property, new NotNull());
  116.             }
  117.         }
  118.         return $loaded;
  119.     }
  120.     private function getTypeConstraint(string $builtinTypePropertyInfoType $type): Type
  121.     {
  122.         if (PropertyInfoType::BUILTIN_TYPE_OBJECT === $builtinType && null !== $className $type->getClassName()) {
  123.             return new Type(['type' => $className]);
  124.         }
  125.         return new Type(['type' => $builtinType]);
  126.     }
  127.     private function handleAllConstraint(string $property, ?All $allConstraintPropertyInfoType $propertyInfoTypeClassMetadata $metadata)
  128.     {
  129.         $containsTypeConstraint false;
  130.         $containsNotNullConstraint false;
  131.         if (null !== $allConstraint) {
  132.             foreach ($allConstraint->constraints as $constraint) {
  133.                 if ($constraint instanceof Type) {
  134.                     $containsTypeConstraint true;
  135.                 } elseif ($constraint instanceof NotNull) {
  136.                     $containsNotNullConstraint true;
  137.                 }
  138.             }
  139.         }
  140.         $constraints = [];
  141.         if (!$containsNotNullConstraint && !$propertyInfoType->isNullable()) {
  142.             $constraints[] = new NotNull();
  143.         }
  144.         if (!$containsTypeConstraint) {
  145.             $constraints[] = $this->getTypeConstraint($propertyInfoType->getBuiltinType(), $propertyInfoType);
  146.         }
  147.         if (null === $allConstraint) {
  148.             $metadata->addPropertyConstraint($property, new All(['constraints' => $constraints]));
  149.         } else {
  150.             $allConstraint->constraints array_merge($allConstraint->constraints$constraints);
  151.         }
  152.     }
  153. }