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 ;
17
14
use phpDocumentor \Reflection \Types \ContextFactory ;
18
15
use PHPStan \PhpDocParser \Ast \PhpDoc \InvalidTagValueNode ;
19
16
use PHPStan \PhpDocParser \Ast \PhpDoc \ParamTagValueNode ;
24
21
use PHPStan \PhpDocParser \Parser \PhpDocParser ;
25
22
use PHPStan \PhpDocParser \Parser \TokenIterator ;
26
23
use PHPStan \PhpDocParser \Parser \TypeParser ;
27
- use Symfony \Component \PropertyInfo \PhpStan \NameScopeFactory ;
28
24
use Symfony \Component \PropertyInfo \PropertyTypeExtractorInterface ;
29
25
use Symfony \Component \PropertyInfo \Type ;
30
- use Symfony \Component \PropertyInfo \Util \PhpStanTypeHelper ;
26
+ use Symfony \Component \PropertyInfo \Util \BackwardCompatibilityHelper ;
27
+ use Symfony \Component \TypeInfo \Exception \LogicException ;
28
+ use Symfony \Component \TypeInfo \Exception \UnsupportedException ;
31
29
use Symfony \Component \TypeInfo \Type as TypeInfoType ;
30
+ use Symfony \Component \TypeInfo \TypeContext \TypeContextFactory ;
31
+ use Symfony \Component \TypeInfo \TypeResolver \StringTypeResolver ;
32
32
33
33
/**
34
34
* Extracts data using PHPStan parser.
@@ -43,14 +43,12 @@ final class PhpStanExtractor implements PropertyTypeExtractorInterface, Construc
43
43
44
44
private PhpDocParser $ phpDocParser ;
45
45
private Lexer $ lexer ;
46
- private NameScopeFactory $ nameScopeFactory ;
47
46
48
47
private StringTypeResolver $ stringTypeResolver ;
49
48
private TypeContextFactory $ typeContextFactory ;
50
49
51
50
/** @var array<string, array{PhpDocNode|null, int|null, string|null, string|null}> */
52
51
private array $ docBlocks = [];
53
- private PhpStanTypeHelper $ phpStanTypeHelper ;
54
52
private array $ mutatorPrefixes ;
55
53
private array $ accessorPrefixes ;
56
54
private array $ arrayMutatorPrefixes ;
@@ -70,52 +68,27 @@ public function __construct(array $mutatorPrefixes = null, array $accessorPrefix
70
68
throw new \LogicException (sprintf ('Unable to use the "%s" class as the "phpstan/phpdoc-parser" package is not installed. Try running composer require "phpstan/phpdoc-parser". ' , __CLASS__ ));
71
69
}
72
70
73
- $ this ->phpStanTypeHelper = new PhpStanTypeHelper ();
74
71
$ this ->mutatorPrefixes = $ mutatorPrefixes ?? ReflectionExtractor::$ defaultMutatorPrefixes ;
75
72
$ this ->accessorPrefixes = $ accessorPrefixes ?? ReflectionExtractor::$ defaultAccessorPrefixes ;
76
73
$ this ->arrayMutatorPrefixes = $ arrayMutatorPrefixes ?? ReflectionExtractor::$ defaultArrayMutatorPrefixes ;
77
74
78
75
$ this ->phpDocParser = new PhpDocParser (new TypeParser (new ConstExprParser ()), new ConstExprParser ());
79
76
$ this ->lexer = new Lexer ();
80
- $ this ->nameScopeFactory = new NameScopeFactory ();
81
77
$ this ->stringTypeResolver = new StringTypeResolver ();
82
78
$ this ->typeContextFactory = new TypeContextFactory ($ this ->stringTypeResolver );
83
79
}
84
80
85
- public function getTypes (string $ class , string $ property , array $ context = []): ?array
86
- {
87
- trigger_deprecation ('symfony/property-info ' , '7.1 ' , 'The "%s()" method is deprecated. Use "%s::getType()" instead. ' , __METHOD__ , self ::class);
88
-
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 ;
98
- }
99
-
100
- $ type = Type::convertFromTypeInfoType ($ type );
101
-
102
- if (!\is_array ($ type )) {
103
- $ type = [$ type ];
104
- }
105
-
106
- return $ type ;
107
- }
108
-
109
81
public function getType (string $ class , string $ property , array $ context = []): ?TypeInfoType
110
82
{
83
+ $ backwardCompatible = \func_get_args ()[3 ] ?? false ;
84
+
111
85
/** @var PhpDocNode|null $docNode */
112
86
[$ docNode , $ source , $ prefix , $ declaringClass ] = $ this ->getDocBlock ($ class , $ property );
113
87
114
88
if (null === $ docNode ) {
115
89
return null ;
116
90
}
117
91
118
- $ nameScope = $ this ->nameScopeFactory ->create ($ class , $ declaringClass );
119
92
$ typeContext = $ this ->typeContextFactory ->createFromClassName ($ class , $ declaringClass );
120
93
121
94
$ tag = match ($ source ) {
@@ -132,15 +105,25 @@ public function getType(string $class, string $property, array $context = []): ?
132
105
continue ;
133
106
}
134
107
135
- if (
136
- $ tagDocNode ->value instanceof ParamTagValueNode
137
- && null === $ prefix
138
- && $ tagDocNode ->value ->parameterName !== '$ ' .$ property
139
- ) {
108
+ if ($ tagDocNode ->value instanceof ParamTagValueNode && null === $ prefix && $ tagDocNode ->value ->parameterName !== '$ ' .$ property ) {
140
109
continue ;
141
110
}
142
111
143
- $ types [] = $ this ->stringTypeResolver ->resolve ((string ) $ tagDocNode ->value ->type , $ typeContext );
112
+ try {
113
+ $ types [] = $ this ->stringTypeResolver ->resolve ((string ) $ tagDocNode ->value ->type , $ typeContext , $ backwardCompatible );
114
+ } catch (UnsupportedException ) {
115
+ // BC layer to handle "void" type in "getTypes"
116
+ if ('void ' === (string ) $ tagDocNode ->value ->type && $ backwardCompatible ) {
117
+ return TypeInfoType::template ('void ' );
118
+ }
119
+ } catch (LogicException $ e ) {
120
+ // BC layer to handle "parent" type without existing parent
121
+ if ('parent ' === (string ) $ tagDocNode ->value ->type && $ backwardCompatible ) {
122
+ return TypeInfoType::object ('parent ' );
123
+ }
124
+
125
+ throw $ e ;
126
+ }
144
127
}
145
128
146
129
if (null === ($ type = $ types [0 ] ?? null )) {
@@ -154,26 +137,31 @@ public function getType(string $class, string $property, array $context = []): ?
154
137
return TypeInfoType::list ($ type );
155
138
}
156
139
157
- public function getTypeFromConstructor (string $ class , string $ property ): ?TypeInfoType
140
+ public function getTypes (string $ class , string $ property, array $ context = [] ): ?array
158
141
{
142
+ trigger_deprecation ('symfony/property-info ' , '7.1 ' , 'The "%s()" method is deprecated. Use "%s::getType()" instead. ' , __METHOD__ , self ::class);
143
+
144
+ return BackwardCompatibilityHelper::convertTypeToLegacyTypes ($ this ->getType ($ class , $ property , $ context , true ));
159
145
}
160
146
161
- public function getTypesFromConstructor (string $ class , string $ property ): ?array
147
+ public function getTypeFromConstructor (string $ class , string $ property ): ?TypeInfoType
162
148
{
149
+ $ backwardCompatible = \func_get_args ()[2 ] ?? false ;
150
+
163
151
if (null === $ tagDocNode = $ this ->getDocBlockFromConstructor ($ class , $ property )) {
164
152
return null ;
165
153
}
166
154
167
- $ types = [];
168
- foreach ($ this ->phpStanTypeHelper ->getTypes ($ tagDocNode , $ this ->nameScopeFactory ->create ($ class )) as $ type ) {
169
- $ types [] = $ type ;
170
- }
155
+ $ typeContext = $ this ->typeContextFactory ->createFromClassName ($ class );
171
156
172
- if (!isset ($ types [0 ])) {
173
- return null ;
174
- }
157
+ return $ this ->stringTypeResolver ->resolve ((string ) $ tagDocNode ->type , $ typeContext , $ backwardCompatible );
158
+ }
159
+
160
+ public function getTypesFromConstructor (string $ class , string $ property ): ?array
161
+ {
162
+ trigger_deprecation ('symfony/property-info ' , '7.1 ' , 'The "%s()" method is deprecated. Use "%s::getTypeFromConstructor()" instead. ' , __METHOD__ , self ::class);
175
163
176
- return $ types ;
164
+ return BackwardCompatibilityHelper:: convertTypeToLegacyTypes ( $ this -> getTypeFromConstructor ( $ class , $ property , true )) ;
177
165
}
178
166
179
167
private function getDocBlockFromConstructor (string $ class , string $ property ): ?ParamTagValueNode
0 commit comments