3 declare(strict_types=1);
5 namespace CuyZ\Valinor\Definition\Repository\Reflection;
7 use CuyZ\Valinor\Definition\Exception\TypesDoNotMatch;
8 use CuyZ\Valinor\Type\Parser\Exception\InvalidType;
9 use CuyZ\Valinor\Type\Parser\TypeParser;
10 use CuyZ\Valinor\Type\Type;
11 use CuyZ\Valinor\Type\Types\ArrayKeyType;
12 use CuyZ\Valinor\Type\Types\ArrayType;
13 use CuyZ\Valinor\Type\Types\MixedType;
14 use CuyZ\Valinor\Type\Types\UnresolvableType;
15 use CuyZ\Valinor\Utility\Reflection\DocParser;
16 use CuyZ\Valinor\Utility\Reflection\Reflection;
17 use ReflectionFunctionAbstract;
18 use ReflectionParameter;
19 use ReflectionProperty;
22 final class ReflectionTypeResolver
24 public function __construct(
25 private TypeParser $nativeParser,
26 private TypeParser $advancedParser
29 public function resolveType(ReflectionProperty|ReflectionParameter|ReflectionFunctionAbstract $reflection): Type
31 $nativeType = $this->nativeType($reflection);
32 $typeFromDocBlock = $this->typeFromDocBlock($reflection);
34 if (! $nativeType && ! $typeFromDocBlock) {
35 return MixedType::get();
39 /** @var Type $typeFromDocBlock */
40 return $typeFromDocBlock;
43 if (! $typeFromDocBlock) {
47 if (! $typeFromDocBlock instanceof UnresolvableType
48 && ! $nativeType instanceof UnresolvableType
49 && ! $typeFromDocBlock->matches($nativeType)
51 throw new TypesDoNotMatch($reflection, $typeFromDocBlock, $nativeType);
54 return $typeFromDocBlock;
57 private function typeFromDocBlock(ReflectionProperty|ReflectionParameter|ReflectionFunctionAbstract $reflection): ?Type
59 if ($reflection instanceof ReflectionFunctionAbstract) {
60 $type = DocParser::functionReturnType($reflection);
61 } elseif ($reflection instanceof ReflectionProperty) {
62 $type = DocParser::propertyType($reflection);
66 if ($reflection->isPromoted()) {
67 // @phpstan-ignore-next-line / parameter is promoted so class exists for sure
68 $type = DocParser::propertyType($reflection->getDeclaringClass()->getProperty($reflection->name));
72 $type = DocParser::parameterType($reflection);
80 $type = $this->parseType($type, $reflection, $this->advancedParser);
82 return $this->handleVariadicType($reflection, $type);
85 private function nativeType(ReflectionProperty|ReflectionParameter|ReflectionFunctionAbstract $reflection): ?Type
87 $reflectionType = $reflection instanceof ReflectionFunctionAbstract
88 ? $reflection->getReturnType()
89 : $reflection->getType();
91 if (! $reflectionType) {
95 $type = Reflection::flattenType($reflectionType);
96 $type = $this->parseType($type, $reflection, $this->nativeParser);
98 return $this->handleVariadicType($reflection, $type);
101 private function parseType(string $raw, ReflectionProperty|ReflectionParameter|ReflectionFunctionAbstract $reflection, TypeParser $parser): Type
104 return $parser->parse($raw);
105 } catch (InvalidType $exception) {
107 $signature = Reflection::signature($reflection);
109 if ($reflection instanceof ReflectionProperty) {
110 return UnresolvableType::forProperty($raw, $signature, $exception);
113 if ($reflection instanceof ReflectionParameter) {
114 return UnresolvableType::forParameter($raw, $signature, $exception);
117 return UnresolvableType::forMethodReturnType($raw, $signature, $exception);
121 private function handleVariadicType(ReflectionProperty|ReflectionParameter|ReflectionFunctionAbstract $reflection, Type $type): Type
123 if (! $reflection instanceof ReflectionParameter || ! $reflection->isVariadic()) {
127 return new ArrayType(ArrayKeyType::default(), $type);