11
11
12
12
namespace Symfony \Component \TypeInfo \TypeContext ;
13
13
14
+ use PHPStan \PhpDocParser \Ast \PhpDoc \PhpDocNode ;
14
15
use PHPStan \PhpDocParser \Ast \PhpDoc \TemplateTagValueNode ;
16
+ use PHPStan \PhpDocParser \Ast \PhpDoc \TypeAliasImportTagValueNode ;
17
+ use PHPStan \PhpDocParser \Ast \PhpDoc \TypeAliasTagValueNode ;
15
18
use PHPStan \PhpDocParser \Lexer \Lexer ;
16
19
use PHPStan \PhpDocParser \Parser \ConstExprParser ;
17
20
use PHPStan \PhpDocParser \Parser \PhpDocParser ;
18
21
use PHPStan \PhpDocParser \Parser \TokenIterator ;
19
22
use PHPStan \PhpDocParser \Parser \TypeParser ;
20
23
use PHPStan \PhpDocParser \ParserConfig ;
24
+ use Symfony \Component \TypeInfo \Exception \LogicException ;
21
25
use Symfony \Component \TypeInfo \Exception \RuntimeException ;
22
26
use Symfony \Component \TypeInfo \Exception \UnsupportedException ;
23
27
use Symfony \Component \TypeInfo \Type ;
28
+ use Symfony \Component \TypeInfo \Type \ObjectType ;
24
29
use Symfony \Component \TypeInfo \TypeResolver \StringTypeResolver ;
25
30
26
31
/**
@@ -66,6 +71,7 @@ public function createFromClassName(string $calledClassName, ?string $declaringC
66
71
$ typeContext ->namespace ,
67
72
$ typeContext ->uses ,
68
73
$ this ->collectTemplates ($ declaringClassReflection , $ typeContext ),
74
+ $ this ->collectTypeAliases ($ declaringClassReflection , $ typeContext ),
69
75
);
70
76
}
71
77
@@ -103,6 +109,7 @@ public function createFromReflection(\Reflector $reflection): ?TypeContext
103
109
$ typeContext ->namespace ,
104
110
$ typeContext ->uses ,
105
111
$ templates ,
112
+ $ this ->collectTypeAliases ($ declaringClassReflection , $ typeContext ),
106
113
);
107
114
}
108
115
@@ -156,19 +163,8 @@ private function collectTemplates(\ReflectionClass|\ReflectionFunctionAbstract $
156
163
return [];
157
164
}
158
165
159
- if (class_exists (ParserConfig::class)) {
160
- $ config = new ParserConfig ([]);
161
- $ this ->phpstanLexer ??= new Lexer ($ config );
162
- $ this ->phpstanParser ??= new PhpDocParser ($ config , new TypeParser ($ config , new ConstExprParser ($ config )), new ConstExprParser ($ config ));
163
- } else {
164
- $ this ->phpstanLexer ??= new Lexer ();
165
- $ this ->phpstanParser ??= new PhpDocParser (new TypeParser (new ConstExprParser ()), new ConstExprParser ());
166
- }
167
-
168
- $ tokens = new TokenIterator ($ this ->phpstanLexer ->tokenize ($ rawDocNode ));
169
-
170
166
$ templates = [];
171
- foreach ($ this ->phpstanParser -> parse ( $ tokens )->getTagsByName ('@template ' ) as $ tag ) {
167
+ foreach ($ this ->getPhpDocNode ( $ rawDocNode )->getTagsByName ('@template ' ) as $ tag ) {
172
168
if (!$ tag ->value instanceof TemplateTagValueNode) {
173
169
continue ;
174
170
}
@@ -188,4 +184,59 @@ private function collectTemplates(\ReflectionClass|\ReflectionFunctionAbstract $
188
184
189
185
return $ templates ;
190
186
}
187
+
188
+ /**
189
+ * @return array<string, Type>
190
+ */
191
+ private function collectTypeAliases (\ReflectionClass $ reflection , TypeContext $ typeContext ): array
192
+ {
193
+ if (!$ this ->stringTypeResolver || !class_exists (PhpDocParser::class)) {
194
+ return [];
195
+ }
196
+
197
+ if (!$ rawDocNode = $ reflection ->getDocComment ()) {
198
+ return [];
199
+ }
200
+
201
+ $ aliases = [];
202
+ foreach ($ this ->getPhpDocNode ($ rawDocNode )->getTagsByName ('@phpstan-type ' ) as $ tag ) {
203
+ if (!$ tag ->value instanceof TypeAliasTagValueNode) {
204
+ continue ;
205
+ }
206
+
207
+ $ aliases [$ tag ->value ->alias ] = $ this ->stringTypeResolver ->resolve ((string ) $ tag ->value ->type , $ typeContext );
208
+ }
209
+
210
+ foreach ($ this ->getPhpDocNode ($ rawDocNode )->getTagsByName ('@phpstan-import-type ' ) as $ tag ) {
211
+ if (!$ tag ->value instanceof TypeAliasImportTagValueNode) {
212
+ continue ;
213
+ }
214
+
215
+ /** @var ObjectType $importedType */
216
+ $ importedType = $ this ->stringTypeResolver ->resolve ((string ) $ tag ->value ->importedFrom , $ typeContext );
217
+ $ importedTypeContext = $ this ->createFromClassName ($ importedType ->getClassName ());
218
+
219
+ $ typeAlias = $ importedTypeContext ->typeAliases [$ tag ->value ->importedAlias ] ?? null ;
220
+ if (!$ typeAlias ) {
221
+ throw new LogicException (sprintf ('Cannot find any "%s" type alias in "%s". ' , $ tag ->value ->importedAlias , $ importedType ->getClassName ()));
222
+ }
223
+
224
+ $ aliases [$ tag ->value ->importedAs ?? $ tag ->value ->importedAlias ] = $ typeAlias ;
225
+ }
226
+
227
+ return $ aliases ;
228
+ }
229
+
230
+ private function getPhpDocNode (string $ rawDocNode ): PhpDocNode
231
+ {
232
+ if (class_exists (ParserConfig::class)) {
233
+ $ this ->phpstanLexer ??= new Lexer ($ config = new ParserConfig ([]));
234
+ $ this ->phpstanParser ??= new PhpDocParser ($ config , new TypeParser ($ config , new ConstExprParser ($ config )), new ConstExprParser ($ config ));
235
+ } else {
236
+ $ this ->phpstanLexer ??= new Lexer ();
237
+ $ this ->phpstanParser ??= new PhpDocParser (new TypeParser (new ConstExprParser ()), new ConstExprParser ());
238
+ }
239
+
240
+ return $ this ->phpstanParser ->parse (new TokenIterator ($ this ->phpstanLexer ->tokenize ($ rawDocNode )));
241
+ }
191
242
}
0 commit comments