vendor/symfony/property-info/Util/PhpDocTypeHelper.php line 108

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\PropertyInfo\Util;
  11. use phpDocumentor\Reflection\PseudoType;
  12. use phpDocumentor\Reflection\PseudoTypes\ConstExpression;
  13. use phpDocumentor\Reflection\PseudoTypes\List_;
  14. use phpDocumentor\Reflection\Type as DocType;
  15. use phpDocumentor\Reflection\Types\Array_;
  16. use phpDocumentor\Reflection\Types\Collection;
  17. use phpDocumentor\Reflection\Types\Compound;
  18. use phpDocumentor\Reflection\Types\Integer;
  19. use phpDocumentor\Reflection\Types\Null_;
  20. use phpDocumentor\Reflection\Types\Nullable;
  21. use phpDocumentor\Reflection\Types\String_;
  22. use Symfony\Component\PropertyInfo\Type;
  23. // Workaround for phpdocumentor/type-resolver < 1.6
  24. // We trigger the autoloader here, so we don't need to trigger it inside the loop later.
  25. class_exists(List_::class);
  26. /**
  27.  * Transforms a php doc type to a {@link Type} instance.
  28.  *
  29.  * @author Kévin Dunglas <dunglas@gmail.com>
  30.  * @author Guilhem N. <egetick@gmail.com>
  31.  */
  32. final class PhpDocTypeHelper
  33. {
  34.     /**
  35.      * Creates a {@see Type} from a PHPDoc type.
  36.      *
  37.      * @return Type[]
  38.      */
  39.     public function getTypes(DocType $varType): array
  40.     {
  41.         if ($varType instanceof ConstExpression) {
  42.             // It's safer to fall back to other extractors here, as resolving const types correctly is not easy at the moment
  43.             return [];
  44.         }
  45.         $types = [];
  46.         $nullable false;
  47.         if ($varType instanceof Nullable) {
  48.             $nullable true;
  49.             $varType $varType->getActualType();
  50.         }
  51.         if (!$varType instanceof Compound) {
  52.             if ($varType instanceof Null_) {
  53.                 $nullable true;
  54.             }
  55.             $type $this->createType($varType$nullable);
  56.             if (null !== $type) {
  57.                 $types[] = $type;
  58.             }
  59.             return $types;
  60.         }
  61.         $varTypes = [];
  62.         for ($typeIndex 0$varType->has($typeIndex); ++$typeIndex) {
  63.             $type $varType->get($typeIndex);
  64.             if ($type instanceof ConstExpression) {
  65.                 // It's safer to fall back to other extractors here, as resolving const types correctly is not easy at the moment
  66.                 return [];
  67.             }
  68.             // If null is present, all types are nullable
  69.             if ($type instanceof Null_) {
  70.                 $nullable true;
  71.                 continue;
  72.             }
  73.             if ($type instanceof Nullable) {
  74.                 $nullable true;
  75.                 $type $type->getActualType();
  76.             }
  77.             $varTypes[] = $type;
  78.         }
  79.         foreach ($varTypes as $varType) {
  80.             $type $this->createType($varType$nullable);
  81.             if (null !== $type) {
  82.                 $types[] = $type;
  83.             }
  84.         }
  85.         return $types;
  86.     }
  87.     /**
  88.      * Creates a {@see Type} from a PHPDoc type.
  89.      */
  90.     private function createType(DocType $typebool $nullablestring $docType null): ?Type
  91.     {
  92.         $docType ??= (string) $type;
  93.         if ($type instanceof Collection) {
  94.             $fqsen $type->getFqsen();
  95.             if ($fqsen && 'list' === $fqsen->getName() && !class_exists(List_::class, false) && !class_exists((string) $fqsen)) {
  96.                 // Workaround for phpdocumentor/type-resolver < 1.6
  97.                 return new Type(Type::BUILTIN_TYPE_ARRAY$nullablenulltrue, new Type(Type::BUILTIN_TYPE_INT), $this->getTypes($type->getValueType()));
  98.             }
  99.             [$phpType$class] = $this->getPhpTypeAndClass((string) $fqsen);
  100.             $key $this->getTypes($type->getKeyType());
  101.             $value $this->getTypes($type->getValueType());
  102.             // More than 1 type returned means it is a Compound type, which is
  103.             // not handled by Type, so better use a null value.
  104.             $key === \count($key) ? $key[0] : null;
  105.             $value === \count($value) ? $value[0] : null;
  106.             return new Type($phpType$nullable$classtrue$key$value);
  107.         }
  108.         // Cannot guess
  109.         if (!$docType || 'mixed' === $docType) {
  110.             return null;
  111.         }
  112.         if (str_ends_with($docType'[]')) {
  113.             $collectionKeyType = new Type(Type::BUILTIN_TYPE_INT);
  114.             $collectionValueType $this->createType($typefalsesubstr($docType0, -2));
  115.             return new Type(Type::BUILTIN_TYPE_ARRAY$nullablenulltrue$collectionKeyType$collectionValueType);
  116.         }
  117.         if ((str_starts_with($docType'list<') || str_starts_with($docType'array<')) && $type instanceof Array_) {
  118.             // array<value> is converted to x[] which is handled above
  119.             // so it's only necessary to handle array<key, value> here
  120.             $collectionKeyType $this->getTypes($type->getKeyType())[0];
  121.             $collectionValueTypes $this->getTypes($type->getValueType());
  122.             if (!= \count($collectionValueTypes)) {
  123.                 // the Type class does not support union types yet, so assume that no type was defined
  124.                 $collectionValueType null;
  125.             } else {
  126.                 $collectionValueType $collectionValueTypes[0];
  127.             }
  128.             return new Type(Type::BUILTIN_TYPE_ARRAY$nullablenulltrue$collectionKeyType$collectionValueType);
  129.         }
  130.         if ($type instanceof PseudoType) {
  131.             if ($type->underlyingType() instanceof Integer) {
  132.                 return new Type(Type::BUILTIN_TYPE_INT$nullablenull);
  133.             } elseif ($type->underlyingType() instanceof String_) {
  134.                 return new Type(Type::BUILTIN_TYPE_STRING$nullablenull);
  135.             }
  136.         }
  137.         $docType $this->normalizeType($docType);
  138.         [$phpType$class] = $this->getPhpTypeAndClass($docType);
  139.         if ('array' === $docType) {
  140.             return new Type(Type::BUILTIN_TYPE_ARRAY$nullablenulltruenullnull);
  141.         }
  142.         return new Type($phpType$nullable$class);
  143.     }
  144.     private function normalizeType(string $docType): string
  145.     {
  146.         return match ($docType) {
  147.             'integer' => 'int',
  148.             'boolean' => 'bool',
  149.             // real is not part of the PHPDoc standard, so we ignore it
  150.             'double' => 'float',
  151.             'callback' => 'callable',
  152.             'void' => 'null',
  153.             default => $docType,
  154.         };
  155.     }
  156.     private function getPhpTypeAndClass(string $docType): array
  157.     {
  158.         if (\in_array($docTypeType::$builtinTypes)) {
  159.             return [$docTypenull];
  160.         }
  161.         if (\in_array($docType, ['parent''self''static'], true)) {
  162.             return ['object'$docType];
  163.         }
  164.         return ['object'ltrim($docType'\\')];
  165.     }
  166. }