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

  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.             $keys $this->getTypes($type->getKeyType());
  101.             $values $this->getTypes($type->getValueType());
  102.             return new Type($phpType$nullable$classtrue$keys$values);
  103.         }
  104.         // Cannot guess
  105.         if (!$docType || 'mixed' === $docType) {
  106.             return null;
  107.         }
  108.         if (str_ends_with($docType'[]') && $type instanceof Array_) {
  109.             $collectionKeyTypes = new Type(Type::BUILTIN_TYPE_INT);
  110.             $collectionValueTypes $this->getTypes($type->getValueType());
  111.             return new Type(Type::BUILTIN_TYPE_ARRAY$nullablenulltrue$collectionKeyTypes$collectionValueTypes);
  112.         }
  113.         if ((str_starts_with($docType'list<') || str_starts_with($docType'array<')) && $type instanceof Array_) {
  114.             // array<value> is converted to x[] which is handled above
  115.             // so it's only necessary to handle array<key, value> here
  116.             $collectionKeyTypes $this->getTypes($type->getKeyType());
  117.             $collectionValueTypes $this->getTypes($type->getValueType());
  118.             return new Type(Type::BUILTIN_TYPE_ARRAY$nullablenulltrue$collectionKeyTypes$collectionValueTypes);
  119.         }
  120.         if ($type instanceof PseudoType) {
  121.             if ($type->underlyingType() instanceof Integer) {
  122.                 return new Type(Type::BUILTIN_TYPE_INT$nullablenull);
  123.             } elseif ($type->underlyingType() instanceof String_) {
  124.                 return new Type(Type::BUILTIN_TYPE_STRING$nullablenull);
  125.             }
  126.         }
  127.         $docType $this->normalizeType($docType);
  128.         [$phpType$class] = $this->getPhpTypeAndClass($docType);
  129.         if ('array' === $docType) {
  130.             return new Type(Type::BUILTIN_TYPE_ARRAY$nullablenulltruenullnull);
  131.         }
  132.         return new Type($phpType$nullable$class);
  133.     }
  134.     private function normalizeType(string $docType): string
  135.     {
  136.         return match ($docType) {
  137.             'integer' => 'int',
  138.             'boolean' => 'bool',
  139.             // real is not part of the PHPDoc standard, so we ignore it
  140.             'double' => 'float',
  141.             'callback' => 'callable',
  142.             'void' => 'null',
  143.             default => $docType,
  144.         };
  145.     }
  146.     private function getPhpTypeAndClass(string $docType): array
  147.     {
  148.         if (\in_array($docTypeType::$builtinTypes)) {
  149.             return [$docTypenull];
  150.         }
  151.         if (\in_array($docType, ['parent''self''static'], true)) {
  152.             return ['object'$docType];
  153.         }
  154.         return ['object'ltrim($docType'\\')];
  155.     }
  156. }