11
11
12
12
namespace Symfony \Component \PropertyInfo \Extractor ;
13
13
14
+ use Symfony \Component \TypeInfo \Exception \UnsupportedException ;
15
+ use Symfony \Component \TypeInfo \TypeContext \TypeContextFactory ;
16
+ use Symfony \Component \TypeInfo \TypeResolver \StringTypeResolver ;
14
17
use phpDocumentor \Reflection \Types \ContextFactory ;
15
18
use PHPStan \PhpDocParser \Ast \PhpDoc \InvalidTagValueNode ;
16
19
use PHPStan \PhpDocParser \Ast \PhpDoc \ParamTagValueNode ;
17
20
use PHPStan \PhpDocParser \Ast \PhpDoc \PhpDocNode ;
18
21
use PHPStan \PhpDocParser \Ast \PhpDoc \PhpDocTagNode ;
19
22
use PHPStan \PhpDocParser \Lexer \Lexer ;
23
+ use PHPStan \PhpDocParser \Parser \ConstExprParser ;
20
24
use PHPStan \PhpDocParser \Parser \PhpDocParser ;
21
25
use PHPStan \PhpDocParser \Parser \TokenIterator ;
26
+ use PHPStan \PhpDocParser \Parser \TypeParser ;
27
+ use Symfony \Component \PropertyInfo \PhpStan \NameScopeFactory ;
22
28
use Symfony \Component \PropertyInfo \PropertyTypeExtractorInterface ;
23
29
use Symfony \Component \PropertyInfo \Type ;
24
30
use Symfony \Component \PropertyInfo \Util \PhpStanTypeHelper ;
25
31
use Symfony \Component \TypeInfo \Type as TypeInfoType ;
26
- use Symfony \Component \TypeInfo \TypeResolver \StringTypeResolver ;
27
- use Symfony \Component \TypeInfo \TypeResolver \TypeResolverInterface ;
28
32
29
33
/**
30
34
* Extracts data using PHPStan parser.
@@ -37,7 +41,12 @@ final class PhpStanExtractor implements PropertyTypeExtractorInterface, Construc
37
41
private const ACCESSOR = 1 ;
38
42
private const MUTATOR = 2 ;
39
43
40
- private TypeResolverInterface $ stringTypeResolver ;
44
+ private PhpDocParser $ phpDocParser ;
45
+ private Lexer $ lexer ;
46
+ private NameScopeFactory $ nameScopeFactory ;
47
+
48
+ private StringTypeResolver $ stringTypeResolver ;
49
+ private TypeContextFactory $ typeContextFactory ;
41
50
42
51
/** @var array<string, array{PhpDocNode|null, int|null, string|null, string|null}> */
43
52
private array $ docBlocks = [];
@@ -66,15 +75,26 @@ public function __construct(array $mutatorPrefixes = null, array $accessorPrefix
66
75
$ this ->accessorPrefixes = $ accessorPrefixes ?? ReflectionExtractor::$ defaultAccessorPrefixes ;
67
76
$ this ->arrayMutatorPrefixes = $ arrayMutatorPrefixes ?? ReflectionExtractor::$ defaultArrayMutatorPrefixes ;
68
77
78
+ $ this ->phpDocParser = new PhpDocParser (new TypeParser (new ConstExprParser ()), new ConstExprParser ());
79
+ $ this ->lexer = new Lexer ();
80
+ $ this ->nameScopeFactory = new NameScopeFactory ();
69
81
$ this ->stringTypeResolver = new StringTypeResolver ();
82
+ $ this ->typeContextFactory = new TypeContextFactory ($ this ->stringTypeResolver );
70
83
}
71
84
72
85
public function getTypes (string $ class , string $ property , array $ context = []): ?array
73
86
{
74
87
trigger_deprecation ('symfony/property-info ' , '7.1 ' , 'The "%s()" method is deprecated. Use "%s::getType()" instead. ' , __METHOD__ , self ::class);
75
88
76
- if (null === $ type = $ this ->getType ($ class , $ property , $ context )) {
77
- return $ type ;
89
+ try {
90
+ $ type = $ this ->getType ($ class , $ property , $ context );
91
+ } catch (UnsupportedException ) {
92
+ // workaround to handle void
93
+ return [new Type (Type::BUILTIN_TYPE_NULL )];
94
+ }
95
+
96
+ if (null === $ type || 'mixed ' === (string ) $ type ) {
97
+ return null ;
78
98
}
79
99
80
100
$ type = Type::convertFromTypeInfoType ($ type );
@@ -90,27 +110,23 @@ public function getType(string $class, string $property, array $context = []): ?
90
110
{
91
111
/** @var PhpDocNode|null $docNode */
92
112
[$ docNode , $ source , $ prefix , $ declaringClass ] = $ this ->getDocBlock ($ class , $ property );
93
- $ nameScope = $ this -> nameScopeFactory -> create ( $ class , $ declaringClass );
113
+
94
114
if (null === $ docNode ) {
95
115
return null ;
96
116
}
97
117
98
- switch ($ source ) {
99
- case self ::PROPERTY :
100
- $ tag = '@var ' ;
101
- break ;
102
-
103
- case self ::ACCESSOR :
104
- $ tag = '@return ' ;
105
- break ;
118
+ $ nameScope = $ this ->nameScopeFactory ->create ($ class , $ declaringClass );
119
+ $ typeContext = $ this ->typeContextFactory ->createFromClassName ($ class , $ declaringClass );
106
120
107
- case self ::MUTATOR :
108
- $ tag = '@param ' ;
109
- break ;
110
- }
121
+ $ tag = match ($ source ) {
122
+ self ::PROPERTY => '@var ' ,
123
+ self ::ACCESSOR => '@return ' ,
124
+ self ::MUTATOR => '@param ' ,
125
+ default => null ,
126
+ };
111
127
112
- $ parentClass = null ;
113
128
$ types = [];
129
+
114
130
foreach ($ docNode ->getTagsByName ($ tag ) as $ tagDocNode ) {
115
131
if ($ tagDocNode ->value instanceof InvalidTagValueNode) {
116
132
continue ;
@@ -124,37 +140,22 @@ public function getType(string $class, string $property, array $context = []): ?
124
140
continue ;
125
141
}
126
142
127
- foreach ($ this ->phpStanTypeHelper ->getTypes ($ tagDocNode ->value , $ nameScope ) as $ type ) {
128
- switch ($ type ->getClassName ()) {
129
- case 'self ' :
130
- case 'static ' :
131
- $ resolvedClass = $ class ;
132
- break ;
133
-
134
- case 'parent ' :
135
- if (false !== $ resolvedClass = $ parentClass ??= get_parent_class ($ class )) {
136
- break ;
137
- }
138
- // no break
139
-
140
- default :
141
- $ types [] = $ type ;
142
- continue 2 ;
143
- }
144
-
145
- $ types [] = new Type (Type::BUILTIN_TYPE_OBJECT , $ type ->isNullable (), $ resolvedClass , $ type ->isCollection (), $ type ->getCollectionKeyTypes (), $ type ->getCollectionValueTypes ());
146
- }
143
+ $ types [] = $ this ->stringTypeResolver ->resolve ((string ) $ tagDocNode ->value ->type , $ typeContext );
147
144
}
148
145
149
- if (! isset ( $ types [0 ])) {
146
+ if (null === ( $ type = $ types [0 ] ?? null )) {
150
147
return null ;
151
148
}
152
149
153
- if (!\in_array ($ prefix , $ this ->arrayMutatorPrefixes , true )) {
154
- return $ types ;
150
+ if (!\in_array ($ prefix , $ this ->arrayMutatorPrefixes )) {
151
+ return $ type ;
155
152
}
156
153
157
- return [new Type (Type::BUILTIN_TYPE_ARRAY , false , null , true , new Type (Type::BUILTIN_TYPE_INT ), $ types [0 ])];
154
+ return TypeInfoType::list ($ type );
155
+ }
156
+
157
+ public function getTypeFromConstructor (string $ class , string $ property ): ?TypeInfoType
158
+ {
158
159
}
159
160
160
161
public function getTypesFromConstructor (string $ class , string $ property ): ?array
0 commit comments