14
14
use PhpParser \Node \Stmt ;
15
15
use PhpParser \NodeAbstract ;
16
16
use PhpParser \NodeVisitorAbstract ;
17
- use PHPStan \PhpDocParser \Ast \PhpDoc \PhpDocNode ;
18
- use PHPStan \PhpDocParser \Ast \Type \ArrayTypeNode ;
19
- use PHPStan \PhpDocParser \Ast \Type \GenericTypeNode ;
20
- use PHPStan \PhpDocParser \Ast \Type \TypeNode ;
21
17
22
18
/**
23
19
* This class is used to collect type information from dockblocks, in particular
@@ -68,51 +64,39 @@ public function enterNode(Node $node): void
68
64
69
65
$ this ->resolveFunctionTypes ($ node );
70
66
71
- $ this ->resolveParamTypes ($ node );
67
+ $ this ->resolvePropertyTypes ($ node );
72
68
}
73
69
74
- private function resolveParamTypes (Node $ node ): void
70
+ private function resolvePropertyTypes (Node $ node ): void
75
71
{
76
72
if (!($ node instanceof Stmt \Property)) {
77
73
return ;
78
74
}
79
75
80
- $ phpDocNode = $ this ->parseDocblock ($ node );
76
+ $ docblock = $ this ->parseDocblock ($ node );
81
77
82
- if (null === $ phpDocNode ) {
78
+ if (null === $ docblock ) {
83
79
return ;
84
80
}
85
81
86
- if ( $ this -> isNodeOfTypeArray ( $ node )) {
87
- $ arrayItemType = null ;
82
+ $ arrayItemType = $ docblock -> getVarTagTypes ();
83
+ $ arrayItemType = array_pop ( $ arrayItemType ) ;
88
84
89
- foreach ($ phpDocNode ->getVarTagValues () as $ tagValue ) {
90
- $ arrayItemType = $ this ->getArrayItemType ($ tagValue ->type );
91
- }
92
-
93
- if (null !== $ arrayItemType ) {
94
- $ node ->type = $ this ->resolveName (new Name ($ arrayItemType ), Stmt \Use_::TYPE_NORMAL );
95
-
96
- return ;
97
- }
98
- }
85
+ if (null !== $ arrayItemType ) {
86
+ $ node ->type = $ this ->resolveName (new Name ($ arrayItemType ), Stmt \Use_::TYPE_NORMAL );
99
87
100
- foreach ($ phpDocNode ->getVarTagValues () as $ tagValue ) {
101
- $ type = $ this ->resolveName (new Name ((string ) $ tagValue ->type ), Stmt \Use_::TYPE_NORMAL );
102
- $ node ->type = $ type ;
103
- break ;
88
+ return ;
104
89
}
105
90
106
91
if ($ this ->parseCustomAnnotations && !($ node ->type instanceof FullyQualified)) {
107
- foreach ($ phpDocNode ->getTags () as $ tagValue ) {
108
- if ('@ ' === $ tagValue ->name [0 ] && !str_contains ($ tagValue ->name , '@var ' )) {
109
- $ customTag = str_replace ('@ ' , '' , $ tagValue ->name );
110
- $ type = $ this ->resolveName (new Name ($ customTag ), Stmt \Use_::TYPE_NORMAL );
111
- $ node ->type = $ type ;
112
-
113
- break ;
114
- }
92
+ $ doctrineAnnotations = $ docblock ->getDoctrineLikeAnnotationTypes ();
93
+ $ doctrineAnnotations = array_shift ($ doctrineAnnotations );
94
+
95
+ if (null === $ doctrineAnnotations ) {
96
+ return ;
115
97
}
98
+
99
+ $ node ->type = $ this ->resolveName (new Name ($ doctrineAnnotations ), Stmt \Use_::TYPE_NORMAL );
116
100
}
117
101
}
118
102
@@ -127,38 +111,41 @@ private function resolveFunctionTypes(Node $node): void
127
111
return ;
128
112
}
129
113
130
- $ phpDocNode = $ this ->parseDocblock ($ node );
114
+ $ docblock = $ this ->parseDocblock ($ node );
131
115
132
- if (null === $ phpDocNode ) { // no docblock, nothing to do
116
+ if (null === $ docblock ) { // no docblock, nothing to do
133
117
return ;
134
118
}
135
119
120
+ // extract param types from param tags
136
121
foreach ($ node ->params as $ param ) {
137
- if (!$ this ->isNodeOfTypeArray ($ param )) { // not an array, nothing to do
122
+ if (!$ this ->isTypeArray ($ param ->type )) { // not an array, nothing to do
123
+ continue ;
124
+ }
125
+
126
+ if (!($ param ->var instanceof Expr \Variable) || !\is_string ($ param ->var ->name )) {
138
127
continue ;
139
128
}
140
129
141
- foreach ($ phpDocNode ->getParamTagValues () as $ phpDocParam ) {
142
- if ($ param ->var instanceof Expr \Variable && \is_string ($ param ->var ->name ) && $ phpDocParam ->parameterName === ('$ ' .$ param ->var ->name )) {
143
- $ arrayItemType = $ this ->getArrayItemType ($ phpDocParam ->type );
130
+ $ type = $ docblock ->getParamTagTypesByName ('$ ' .$ param ->var ->name );
144
131
145
- if (null !== $ arrayItemType ) {
146
- $ param ->type = $ this ->resolveName (new Name ($ arrayItemType ), Stmt \Use_::TYPE_NORMAL );
147
- }
148
- }
132
+ if (null === $ type ) {
133
+ continue ;
149
134
}
135
+
136
+ $ param ->type = $ this ->resolveName (new Name ($ type ), Stmt \Use_::TYPE_NORMAL );
150
137
}
151
138
152
- if ($ node ->returnType instanceof Node \Identifier && 'array ' === $ node ->returnType ->name ) {
153
- $ arrayItemType = null ;
139
+ // extract return type from return tag
140
+ if ($ this ->isTypeArray ($ node ->returnType )) {
141
+ $ type = $ docblock ->getReturnTagTypes ();
142
+ $ type = array_pop ($ type );
154
143
155
- foreach ( $ phpDocNode -> getReturnTagValues () as $ tagValue ) {
156
- $ arrayItemType = $ this -> getArrayItemType ( $ tagValue -> type ) ;
144
+ if ( null === $ type ) {
145
+ return ;
157
146
}
158
147
159
- if (null !== $ arrayItemType ) {
160
- $ node ->returnType = $ this ->resolveName (new Name ($ arrayItemType ), Stmt \Use_::TYPE_NORMAL );
161
- }
148
+ $ node ->returnType = $ this ->resolveName (new Name ($ type ), Stmt \Use_::TYPE_NORMAL );
162
149
}
163
150
}
164
151
@@ -178,14 +165,6 @@ private function resolveName(Name $name, int $type): Name
178
165
return $ resolvedName ;
179
166
}
180
167
181
- // unqualified names inside a namespace cannot be resolved at compile-time
182
- // add the namespaced version of the name as an attribute
183
- $ name ->setAttribute ('namespacedName ' , FullyQualified::concat (
184
- $ this ->nameContext ->getNamespace (),
185
- $ name ,
186
- $ name ->getAttributes ()
187
- ));
188
-
189
168
return $ name ;
190
169
}
191
170
@@ -218,7 +197,7 @@ private function addAlias(Node\UseItem $use, int $type, ?Name $prefix = null): v
218
197
);
219
198
}
220
199
221
- private function parseDocblock (NodeAbstract $ node ): ?PhpDocNode
200
+ private function parseDocblock (NodeAbstract $ node ): ?Docblock
222
201
{
223
202
if (null === $ node ->getDocComment ()) {
224
203
return null ;
@@ -231,38 +210,10 @@ private function parseDocblock(NodeAbstract $node): ?PhpDocNode
231
210
}
232
211
233
212
/**
234
- * @param Node\Param|Stmt\Property $node
213
+ * @param Node\Identifier|Name|Node\ComplexType|null $type
235
214
*/
236
- private function isNodeOfTypeArray ($ node ): bool
237
- {
238
- return null !== $ node ->type && isset ($ node ->type ->name ) && 'array ' === $ node ->type ->name ;
239
- }
240
-
241
- private function getArrayItemType (TypeNode $ typeNode ): ?string
215
+ private function isTypeArray ($ type ): bool
242
216
{
243
- $ arrayItemType = null ;
244
-
245
- if ($ typeNode instanceof GenericTypeNode) {
246
- if (1 === \count ($ typeNode ->genericTypes )) {
247
- // this handles list<ClassName>
248
- $ arrayItemType = (string ) $ typeNode ->genericTypes [0 ];
249
- } elseif (2 === \count ($ typeNode ->genericTypes )) {
250
- // this handles array<int, ClassName>
251
- $ arrayItemType = (string ) $ typeNode ->genericTypes [1 ];
252
- }
253
- }
254
-
255
- if ($ typeNode instanceof ArrayTypeNode) {
256
- // this handles ClassName[]
257
- $ arrayItemType = (string ) $ typeNode ->type ;
258
- }
259
-
260
- $ validFqcn = '/^[a-zA-Z_\x7f-\xff \\\\][a-zA-Z0-9_\x7f-\xff \\\\]*[a-zA-Z0-9_\x7f-\xff]$/ ' ;
261
-
262
- if (null !== $ arrayItemType && !(bool ) preg_match ($ validFqcn , $ arrayItemType )) {
263
- return null ;
264
- }
265
-
266
- return $ arrayItemType ;
217
+ return null !== $ type && isset ($ type ->name ) && 'array ' === $ type ->name ;
267
218
}
268
219
}
0 commit comments