ca6a4e1d1e4eabbde9bfd77465a9bdba6bf06508
[GitHub/WoltLab/WCF.git] /
1 <?php
2
3 declare(strict_types=1);
4
5 namespace CuyZ\Valinor\Definition\Repository\Reflection;
6
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\MixedType;
12 use CuyZ\Valinor\Type\Types\UnresolvableType;
13 use CuyZ\Valinor\Utility\Reflection\Reflection;
14 use ReflectionFunctionAbstract;
15 use ReflectionParameter;
16 use ReflectionProperty;
17
18 /** @internal */
19 final class ReflectionTypeResolver
20 {
21 public function __construct(
22 private TypeParser $nativeParser,
23 private TypeParser $advancedParser
24 ) {}
25
26 public function resolveType(\ReflectionProperty|\ReflectionParameter|\ReflectionFunctionAbstract $reflection): Type
27 {
28 $nativeType = $this->nativeType($reflection);
29 $typeFromDocBlock = $this->typeFromDocBlock($reflection);
30
31 if (! $nativeType && ! $typeFromDocBlock) {
32 return MixedType::get();
33 }
34
35 if (! $nativeType) {
36 /** @var Type $typeFromDocBlock */
37 return $typeFromDocBlock;
38 }
39
40 if (! $typeFromDocBlock) {
41 return $nativeType;
42 }
43
44 if (! $typeFromDocBlock instanceof UnresolvableType
45 && ! $nativeType instanceof UnresolvableType
46 && ! $typeFromDocBlock->matches($nativeType)
47 ) {
48 throw new TypesDoNotMatch($reflection, $typeFromDocBlock, $nativeType);
49 }
50
51 return $typeFromDocBlock;
52 }
53
54 private function typeFromDocBlock(\ReflectionProperty|\ReflectionParameter|\ReflectionFunctionAbstract $reflection): ?Type
55 {
56 $type = $reflection instanceof ReflectionFunctionAbstract
57 ? Reflection::docBlockReturnType($reflection)
58 : Reflection::docBlockType($reflection);
59
60 if ($type === null) {
61 return null;
62 }
63
64 return $this->parseType($type, $reflection, $this->advancedParser);
65 }
66
67 private function nativeType(\ReflectionProperty|\ReflectionParameter|\ReflectionFunctionAbstract $reflection): ?Type
68 {
69 $reflectionType = $reflection instanceof ReflectionFunctionAbstract
70 ? $reflection->getReturnType()
71 : $reflection->getType();
72
73 if (! $reflectionType) {
74 return null;
75 }
76
77 $type = Reflection::flattenType($reflectionType);
78
79 if ($reflection instanceof ReflectionParameter && $reflection->isVariadic()) {
80 $type .= '[]';
81 }
82
83 return $this->parseType($type, $reflection, $this->nativeParser);
84 }
85
86 private function parseType(string $raw, \ReflectionProperty|\ReflectionParameter|\ReflectionFunctionAbstract $reflection, TypeParser $parser): Type
87 {
88 try {
89 return $parser->parse($raw);
90 } catch (InvalidType $exception) {
91 $raw = trim($raw);
92 $signature = Reflection::signature($reflection);
93
94 if ($reflection instanceof ReflectionProperty) {
95 return UnresolvableType::forProperty($raw, $signature, $exception);
96 }
97
98 if ($reflection instanceof ReflectionParameter) {
99 return UnresolvableType::forParameter($raw, $signature, $exception);
100 }
101
102 return UnresolvableType::forMethodReturnType($raw, $signature, $exception);
103 }
104 }
105 }