123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178 |
- <?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\Loader;
- use Symfony\Component\PropertyInfo\PropertyAccessExtractorInterface;
- 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\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;
- /**
- * Guesses and loads the appropriate constraints using PropertyInfo.
- *
- * @author Kévin Dunglas <dunglas@gmail.com>
- */
- final class PropertyInfoLoader implements LoaderInterface
- {
- use AutoMappingTrait;
- private $listExtractor;
- private $typeExtractor;
- private $accessExtractor;
- private $classValidatorRegexp;
- public function __construct(PropertyListExtractorInterface $listExtractor, PropertyTypeExtractorInterface $typeExtractor, PropertyAccessExtractorInterface $accessExtractor, string $classValidatorRegexp = null)
- {
- $this->listExtractor = $listExtractor;
- $this->typeExtractor = $typeExtractor;
- $this->accessExtractor = $accessExtractor;
- $this->classValidatorRegexp = $classValidatorRegexp;
- }
- /**
- * {@inheritdoc}
- */
- public function loadClassMetadata(ClassMetadata $metadata): bool
- {
- $className = $metadata->getClassName();
- if (!$properties = $this->listExtractor->getProperties($className)) {
- return false;
- }
- $loaded = false;
- $enabledForClass = $this->isAutoMappingEnabledForClass($metadata, $this->classValidatorRegexp);
- foreach ($properties as $property) {
- if (false === $this->accessExtractor->isWritable($className, $property)) {
- continue;
- }
- if (!property_exists($className, $property)) {
- continue;
- }
- $types = $this->typeExtractor->getTypes($className, $property);
- if (null === $types) {
- continue;
- }
- $enabledForProperty = $enabledForClass;
- $hasTypeConstraint = false;
- $hasNotNullConstraint = false;
- $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) {
- if ($constraint instanceof Type) {
- $hasTypeConstraint = true;
- } elseif ($constraint instanceof NotNull) {
- $hasNotNullConstraint = true;
- } elseif ($constraint instanceof NotBlank) {
- $hasNotBlankConstraint = true;
- } elseif ($constraint instanceof All) {
- $allConstraint = $constraint;
- }
- }
- }
- if (!$enabledForProperty) {
- continue;
- }
- $loaded = true;
- $builtinTypes = [];
- $nullable = false;
- $scalar = true;
- foreach ($types as $type) {
- $builtinTypes[] = $type->getBuiltinType();
- if ($scalar && !\in_array($type->getBuiltinType(), [PropertyInfoType::BUILTIN_TYPE_INT, PropertyInfoType::BUILTIN_TYPE_FLOAT, PropertyInfoType::BUILTIN_TYPE_STRING, PropertyInfoType::BUILTIN_TYPE_BOOL], true)) {
- $scalar = false;
- }
- if (!$nullable && $type->isNullable()) {
- $nullable = true;
- }
- }
- if (!$hasTypeConstraint) {
- if (1 === \count($builtinTypes)) {
- if ($types[0]->isCollection() && (null !== $collectionValueType = $types[0]->getCollectionValueType())) {
- $this->handleAllConstraint($property, $allConstraint, $collectionValueType, $metadata);
- }
- $metadata->addPropertyConstraint($property, $this->getTypeConstraint($builtinTypes[0], $types[0]));
- } elseif ($scalar) {
- $metadata->addPropertyConstraint($property, new Type(['type' => 'scalar']));
- }
- }
- if (!$nullable && !$hasNotBlankConstraint && !$hasNotNullConstraint) {
- $metadata->addPropertyConstraint($property, new NotNull());
- }
- }
- return $loaded;
- }
- private function getTypeConstraint(string $builtinType, PropertyInfoType $type): Type
- {
- if (PropertyInfoType::BUILTIN_TYPE_OBJECT === $builtinType && null !== $className = $type->getClassName()) {
- return new Type(['type' => $className]);
- }
- return new Type(['type' => $builtinType]);
- }
- private function handleAllConstraint(string $property, ?All $allConstraint, PropertyInfoType $propertyInfoType, ClassMetadata $metadata)
- {
- $containsTypeConstraint = false;
- $containsNotNullConstraint = false;
- if (null !== $allConstraint) {
- foreach ($allConstraint->constraints as $constraint) {
- if ($constraint instanceof Type) {
- $containsTypeConstraint = true;
- } elseif ($constraint instanceof NotNull) {
- $containsNotNullConstraint = true;
- }
- }
- }
- $constraints = [];
- if (!$containsNotNullConstraint && !$propertyInfoType->isNullable()) {
- $constraints[] = new NotNull();
- }
- if (!$containsTypeConstraint) {
- $constraints[] = $this->getTypeConstraint($propertyInfoType->getBuiltinType(), $propertyInfoType);
- }
- if (null === $allConstraint) {
- $metadata->addPropertyConstraint($property, new All(['constraints' => $constraints]));
- } else {
- $allConstraint->constraints = array_merge($allConstraint->constraints, $constraints);
- }
- }
- }
|