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

Open in your IDE?
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <[email protected]>
  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 <[email protected]>
  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 $listExtractor, PropertyTypeExtractorInterface $typeExtractor, PropertyAccessExtractorInterface $accessExtractor, ?string $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_INT, PropertyInfoType::BUILTIN_TYPE_FLOAT, PropertyInfoType::BUILTIN_TYPE_STRING, PropertyInfoType::BUILTIN_TYPE_BOOL], true)) {
  97. $scalar = false;
  98. }
  99. if (!$nullable && $type->isNullable()) {
  100. $nullable = true;
  101. }
  102. }
  103. if (!$hasTypeConstraint) {
  104. if (1 === \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 $builtinType, PropertyInfoType $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 $allConstraint, PropertyInfoType $propertyInfoType, ClassMetadata $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. }