Skip to main content
Drupal API
User account menu
  • Log in

Breadcrumb

  1. Drupal Core 11.1.x

PropertyInfoLoader.php

Namespace

Symfony\Component\Validator\Mapping\Loader

File

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

View source
<?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\TypeInfo\Type as TypeInfoType;
use Symfony\Component\TypeInfo\Type\BuiltinType;
use Symfony\Component\TypeInfo\Type\CollectionType;
use Symfony\Component\TypeInfo\Type\CompositeTypeInterface;
use Symfony\Component\TypeInfo\Type\IntersectionType;
use Symfony\Component\TypeInfo\Type\NullableType;
use Symfony\Component\TypeInfo\Type\ObjectType;
use Symfony\Component\TypeInfo\Type\UnionType;
use Symfony\Component\TypeInfo\Type\WrappingTypeInterface;
use Symfony\Component\TypeInfo\TypeIdentifier;
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;
    public function __construct(PropertyListExtractorInterface $listExtractor, PropertyTypeExtractorInterface $typeExtractor, PropertyAccessExtractorInterface $accessExtractor, ?string $classValidatorRegexp = null) {
    }
    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->getPropertyTypes($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;
            // BC layer for PropertyTypeExtractorInterface::getTypes().
            // Can be removed as soon as PropertyTypeExtractorInterface::getTypes() is removed (8.0).
            if (\is_array($types)) {
                $builtinTypes = [];
                $nullable = false;
                $scalar = true;
                foreach ($types as $type) {
                    $builtinTypes[] = $type->getBuiltinType();
                    if ($scalar && !\in_array($type->getBuiltinType(), [
                        'int',
                        'float',
                        'string',
                        'bool',
                    ], true)) {
                        $scalar = false;
                    }
                    if (!$nullable && $type->isNullable()) {
                        $nullable = true;
                    }
                }
                if (!$hasTypeConstraint) {
                    if (1 === \count($builtinTypes)) {
                        if ($types[0]->isCollection() && \count($collectionValueType = $types[0]->getCollectionValueTypes()) > 0) {
                            [
                                $collectionValueType,
                            ] = $collectionValueType;
                            $this->handleAllConstraintLegacy($property, $allConstraint, $collectionValueType, $metadata);
                        }
                        $metadata->addPropertyConstraint($property, $this->getTypeConstraintLegacy($builtinTypes[0], $types[0]));
                    }
                    elseif ($scalar) {
                        $metadata->addPropertyConstraint($property, new Type([
                            'type' => 'scalar',
                        ]));
                    }
                }
            }
            else {
                if ($hasTypeConstraint) {
                    continue;
                }
                $type = $types;
                // BC layer for type-info < 7.2
                if (!class_exists(NullableType::class)) {
                    $nullable = false;
                    if ($type instanceof UnionType && $type->isNullable()) {
                        $nullable = true;
                        $type = $type->asNonNullable();
                    }
                }
                else {
                    $nullable = $type->isNullable();
                    if ($type instanceof NullableType) {
                        $type = $type->getWrappedType();
                    }
                }
                if ($type instanceof NullableType) {
                    $type = $type->getWrappedType();
                }
                if ($type instanceof CollectionType) {
                    $this->handleAllConstraint($property, $allConstraint, $type->getCollectionValueType(), $metadata);
                }
                if (null !== ($typeConstraint = $this->getTypeConstraint($type))) {
                    $metadata->addPropertyConstraint($property, $typeConstraint);
                }
            }
            if (!$nullable && !$hasNotBlankConstraint && !$hasNotNullConstraint) {
                $metadata->addPropertyConstraint($property, new NotNull());
            }
        }
        return $loaded;
    }
    
    /**
     * BC layer for PropertyTypeExtractorInterface::getTypes().
     * Can be removed as soon as PropertyTypeExtractorInterface::getTypes() is removed (8.0).
     *
     * @return TypeInfoType|list<PropertyInfoType>|null
     */
    private function getPropertyTypes(string $className, string $property) : TypeInfoType|array|null {
        if (class_exists(TypeInfoType::class) && method_exists($this->typeExtractor, 'getType')) {
            return $this->typeExtractor
                ->getType($className, $property);
        }
        return $this->typeExtractor
            ->getTypes($className, $property);
    }
    
    /**
     * BC layer for PropertyTypeExtractorInterface::getTypes().
     * Can be removed as soon as PropertyTypeExtractorInterface::getTypes() is removed (8.0).
     */
    private function getTypeConstraintLegacy(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 getTypeConstraint(TypeInfoType $type) : ?Type {
        // BC layer for type-info < 7.2
        if (!interface_exists(CompositeTypeInterface::class)) {
            if ($type instanceof UnionType || $type instanceof IntersectionType) {
                return $type->isA(TypeIdentifier::INT) || $type->isA(TypeIdentifier::FLOAT) || $type->isA(TypeIdentifier::STRING) || $type->isA(TypeIdentifier::BOOL) ? new Type([
                    'type' => 'scalar',
                ]) : null;
            }
            $baseType = $type->getBaseType();
            if ($baseType instanceof ObjectType) {
                return new Type([
                    'type' => $baseType->getClassName(),
                ]);
            }
            if (TypeIdentifier::MIXED !== $baseType->getTypeIdentifier()) {
                return new Type([
                    'type' => $baseType->getTypeIdentifier()->value,
                ]);
            }
            return null;
        }
        if ($type instanceof CompositeTypeInterface) {
            return $type->isIdentifiedBy(TypeIdentifier::INT, TypeIdentifier::FLOAT, TypeIdentifier::STRING, TypeIdentifier::BOOL, TypeIdentifier::TRUE, TypeIdentifier::FALSE) ? new Type([
                'type' => 'scalar',
            ]) : null;
        }
        while ($type instanceof WrappingTypeInterface) {
            $type = $type->getWrappedType();
        }
        if ($type instanceof ObjectType) {
            return new Type([
                'type' => $type->getClassName(),
            ]);
        }
        if ($type instanceof BuiltinType && TypeIdentifier::MIXED !== $type->getTypeIdentifier()) {
            return new Type([
                'type' => $type->getTypeIdentifier()->value,
            ]);
        }
        return null;
    }
    private function handleAllConstraint(string $property, ?All $allConstraint, TypeInfoType $type, ClassMetadata $metadata) : void {
        $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 && !$type->isNullable()) {
            $constraints[] = new NotNull();
        }
        if (!$containsTypeConstraint && null !== ($typeConstraint = $this->getTypeConstraint($type))) {
            $constraints[] = $typeConstraint;
        }
        if (!$constraints) {
            return;
        }
        if (null === $allConstraint) {
            $metadata->addPropertyConstraint($property, new All([
                'constraints' => $constraints,
            ]));
        }
        else {
            $allConstraint->constraints = array_merge($allConstraint->constraints, $constraints);
        }
    }
    
    /**
     * BC layer for PropertyTypeExtractorInterface::getTypes().
     * Can be removed as soon as PropertyTypeExtractorInterface::getTypes() is removed (8.0).
     */
    private function handleAllConstraintLegacy(string $property, ?All $allConstraint, PropertyInfoType $propertyInfoType, ClassMetadata $metadata) : void {
        $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->getTypeConstraintLegacy($propertyInfoType->getBuiltinType(), $propertyInfoType);
        }
        if (null === $allConstraint) {
            $metadata->addPropertyConstraint($property, new All([
                'constraints' => $constraints,
            ]));
        }
        else {
            $allConstraint->constraints = array_merge($allConstraint->constraints, $constraints);
        }
    }

}

Classes

Title Deprecated Summary
PropertyInfoLoader Guesses and loads the appropriate constraints using PropertyInfo.

API Navigation

  • Drupal Core 11.1.x
  • Topics
  • Classes
  • Functions
  • Constants
  • Globals
  • Files
  • Namespaces
  • Deprecated
  • Services
RSS feed
Powered by Drupal