10
10
use PHPStan \ShouldNotHappenException ;
11
11
use PHPStan \Type \Accessory \AccessoryArrayListType ;
12
12
use PHPStan \Type \ArrayType ;
13
- use PHPStan \Type \BenevolentUnionType ;
14
13
use PHPStan \Type \Constant \ConstantIntegerType ;
15
- use PHPStan \Type \Doctrine \ObjectMetadataResolver ;
16
14
use PHPStan \Type \DynamicMethodReturnTypeExtension ;
17
15
use PHPStan \Type \IntegerType ;
18
16
use PHPStan \Type \IterableType ;
19
- use PHPStan \Type \MixedType ;
20
17
use PHPStan \Type \NullType ;
21
- use PHPStan \Type \ObjectWithoutClassType ;
22
18
use PHPStan \Type \Type ;
23
19
use PHPStan \Type \TypeCombinator ;
24
- use PHPStan \Type \TypeTraverser ;
25
- use PHPStan \Type \TypeUtils ;
26
- use PHPStan \Type \TypeWithClassName ;
27
20
use PHPStan \Type \VoidType ;
28
- use function count ;
29
21
30
22
final class QueryResultDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
31
23
{
@@ -40,32 +32,14 @@ final class QueryResultDynamicReturnTypeExtension implements DynamicMethodReturn
40
32
'getSingleResult ' => 0 ,
41
33
];
42
34
43
- private const METHOD_HYDRATION_MODE = [
44
- 'getArrayResult ' => AbstractQuery::HYDRATE_ARRAY ,
45
- 'getScalarResult ' => AbstractQuery::HYDRATE_SCALAR ,
46
- 'getSingleColumnResult ' => AbstractQuery::HYDRATE_SCALAR_COLUMN ,
47
- 'getSingleScalarResult ' => AbstractQuery::HYDRATE_SINGLE_SCALAR ,
48
- ];
49
-
50
- /** @var ObjectMetadataResolver */
51
- private $ objectMetadataResolver ;
52
-
53
- public function __construct (
54
- ObjectMetadataResolver $ objectMetadataResolver
55
- )
56
- {
57
- $ this ->objectMetadataResolver = $ objectMetadataResolver ;
58
- }
59
-
60
35
public function getClass (): string
61
36
{
62
37
return AbstractQuery::class;
63
38
}
64
39
65
40
public function isMethodSupported (MethodReflection $ methodReflection ): bool
66
41
{
67
- return isset (self ::METHOD_HYDRATION_MODE_ARG [$ methodReflection ->getName ()])
68
- || isset (self ::METHOD_HYDRATION_MODE [$ methodReflection ->getName ()]);
42
+ return isset (self ::METHOD_HYDRATION_MODE_ARG [$ methodReflection ->getName ()]);
69
43
}
70
44
71
45
public function getTypeFromMethodCall (
@@ -76,23 +50,21 @@ public function getTypeFromMethodCall(
76
50
{
77
51
$ methodName = $ methodReflection ->getName ();
78
52
79
- if (isset (self ::METHOD_HYDRATION_MODE [$ methodName ])) {
80
- $ hydrationMode = new ConstantIntegerType (self ::METHOD_HYDRATION_MODE [$ methodName ]);
81
- } elseif (isset (self ::METHOD_HYDRATION_MODE_ARG [$ methodName ])) {
82
- $ argIndex = self ::METHOD_HYDRATION_MODE_ARG [$ methodName ];
83
- $ args = $ methodCall ->getArgs ();
53
+ if (!isset (self ::METHOD_HYDRATION_MODE_ARG [$ methodName ])) {
54
+ throw new ShouldNotHappenException ();
55
+ }
84
56
85
- if (isset ($ args [$ argIndex ])) {
86
- $ hydrationMode = $ scope ->getType ($ args [$ argIndex ]->value );
87
- } else {
88
- $ parametersAcceptor = ParametersAcceptorSelector::selectSingle (
89
- $ methodReflection ->getVariants ()
90
- );
91
- $ parameter = $ parametersAcceptor ->getParameters ()[$ argIndex ];
92
- $ hydrationMode = $ parameter ->getDefaultValue () ?? new NullType ();
93
- }
57
+ $ argIndex = self ::METHOD_HYDRATION_MODE_ARG [$ methodName ];
58
+ $ args = $ methodCall ->getArgs ();
59
+
60
+ if (isset ($ args [$ argIndex ])) {
61
+ $ hydrationMode = $ scope ->getType ($ args [$ argIndex ]->value );
94
62
} else {
95
- throw new ShouldNotHappenException ();
63
+ $ parametersAcceptor = ParametersAcceptorSelector::selectSingle (
64
+ $ methodReflection ->getVariants ()
65
+ );
66
+ $ parameter = $ parametersAcceptor ->getParameters ()[$ argIndex ];
67
+ $ hydrationMode = $ parameter ->getDefaultValue () ?? new NullType ();
96
68
}
97
69
98
70
$ queryType = $ scope ->getType ($ methodCall ->var );
@@ -126,54 +98,23 @@ private function getMethodReturnTypeForHydrationMode(
126
98
return $ this ->originalReturnType ($ methodReflection );
127
99
}
128
100
129
- if (!$ hydrationMode instanceof ConstantIntegerType) {
101
+ if (!$ this ->isObjectHydrationMode ($ hydrationMode )) {
102
+ // We support only HYDRATE_OBJECT. For other hydration modes, we
103
+ // return the declared return type of the method.
130
104
return $ this ->originalReturnType ($ methodReflection );
131
105
}
132
106
133
- $ singleResult = false ;
134
- switch ($ hydrationMode ->getValue ()) {
135
- case AbstractQuery::HYDRATE_OBJECT :
136
- break ;
137
- case AbstractQuery::HYDRATE_ARRAY :
138
- $ queryResultType = $ this ->getArrayHydratedReturnType ($ queryResultType );
139
- break ;
140
- case AbstractQuery::HYDRATE_SCALAR :
141
- $ queryResultType = $ this ->getScalarHydratedReturnType ($ queryResultType );
142
- break ;
143
- case AbstractQuery::HYDRATE_SINGLE_SCALAR :
144
- $ singleResult = true ;
145
- $ queryResultType = $ this ->getSingleScalarHydratedReturnType ($ queryResultType );
146
- break ;
147
- case AbstractQuery::HYDRATE_SIMPLEOBJECT :
148
- $ queryResultType = $ this ->getSimpleObjectHydratedReturnType ($ queryResultType );
149
- break ;
150
- case AbstractQuery::HYDRATE_SCALAR_COLUMN :
151
- $ queryResultType = $ this ->getScalarColumnHydratedReturnType ($ queryResultType );
152
- break ;
153
- default :
154
- return $ this ->originalReturnType ($ methodReflection );
155
- }
156
-
157
107
switch ($ methodReflection ->getName ()) {
158
108
case 'getSingleResult ' :
159
109
return $ queryResultType ;
160
110
case 'getOneOrNullResult ' :
161
- $ nullableQueryResultType = TypeCombinator::addNull ($ queryResultType );
162
- if ($ queryResultType instanceof BenevolentUnionType) {
163
- $ nullableQueryResultType = TypeUtils::toBenevolentUnion ($ nullableQueryResultType );
164
- }
165
-
166
- return $ nullableQueryResultType ;
111
+ return TypeCombinator::addNull ($ queryResultType );
167
112
case 'toIterable ' :
168
113
return new IterableType (
169
114
$ queryKeyType ->isNull ()->yes () ? new IntegerType () : $ queryKeyType ,
170
115
$ queryResultType
171
116
);
172
117
default :
173
- if ($ singleResult ) {
174
- return $ queryResultType ;
175
- }
176
-
177
118
if ($ queryKeyType ->isNull ()->yes ()) {
178
119
return AccessoryArrayListType::intersectWith (new ArrayType (
179
120
new IntegerType (),
@@ -187,104 +128,13 @@ private function getMethodReturnTypeForHydrationMode(
187
128
}
188
129
}
189
130
190
- private function getArrayHydratedReturnType (Type $ queryResultType ): Type
191
- {
192
- $ objectManager = $ this ->objectMetadataResolver ->getObjectManager ();
193
-
194
- return TypeTraverser::map (
195
- $ queryResultType ,
196
- static function (Type $ type , callable $ traverse ) use ($ objectManager ): Type {
197
- $ isObject = (new ObjectWithoutClassType ())->isSuperTypeOf ($ type );
198
- if ($ isObject ->no ()) {
199
- return $ traverse ($ type );
200
- }
201
- if (
202
- $ isObject ->maybe ()
203
- || !$ type instanceof TypeWithClassName
204
- || $ objectManager === null
205
- ) {
206
- return new MixedType ();
207
- }
208
-
209
- if (!$ objectManager ->getMetadataFactory ()->hasMetadataFor ($ type ->getClassName ())) {
210
- return $ traverse ($ type );
211
- }
212
-
213
- // We could return `new ArrayTyp(new MixedType(), new MixedType())`
214
- // but the lack of precision in the array keys/values would give false positive
215
- // @see https://github.com/phpstan/phpstan-doctrine/pull/412#issuecomment-1497092934
216
- return new MixedType ();
217
- }
218
- );
219
- }
220
-
221
- private function getScalarHydratedReturnType (Type $ queryResultType ): Type
222
- {
223
- if (!$ queryResultType ->isArray ()->yes ()) {
224
- return new ArrayType (new MixedType (), new MixedType ());
225
- }
226
-
227
- foreach ($ queryResultType ->getArrays () as $ arrayType ) {
228
- $ itemType = $ arrayType ->getItemType ();
229
-
230
- if (
231
- !(new ObjectWithoutClassType ())->isSuperTypeOf ($ itemType )->no ()
232
- || !$ itemType ->isArray ()->no ()
233
- ) {
234
- return new ArrayType (new MixedType (), new MixedType ());
235
- }
236
- }
237
-
238
- return $ queryResultType ;
239
- }
240
-
241
- private function getSimpleObjectHydratedReturnType (Type $ queryResultType ): Type
242
- {
243
- if ((new ObjectWithoutClassType ())->isSuperTypeOf ($ queryResultType )->yes ()) {
244
- return $ queryResultType ;
245
- }
246
-
247
- return new MixedType ();
248
- }
249
-
250
- private function getSingleScalarHydratedReturnType (Type $ queryResultType ): Type
131
+ private function isObjectHydrationMode (Type $ type ): bool
251
132
{
252
- $ queryResultType = $ this ->getScalarHydratedReturnType ($ queryResultType );
253
- if (!$ queryResultType ->isConstantArray ()->yes ()) {
254
- return new MixedType ();
255
- }
256
-
257
- $ types = [];
258
- foreach ($ queryResultType ->getConstantArrays () as $ constantArrayType ) {
259
- $ values = $ constantArrayType ->getValueTypes ();
260
- if (count ($ values ) !== 1 ) {
261
- return new MixedType ();
262
- }
263
-
264
- $ types [] = $ constantArrayType ->getFirstIterableValueType ();
265
- }
266
-
267
- return TypeCombinator::union (...$ types );
268
- }
269
-
270
- private function getScalarColumnHydratedReturnType (Type $ queryResultType ): Type
271
- {
272
- $ queryResultType = $ this ->getScalarHydratedReturnType ($ queryResultType );
273
- if (!$ queryResultType ->isConstantArray ()->yes ()) {
274
- return new MixedType ();
275
- }
276
-
277
- $ types = [];
278
- foreach ($ queryResultType ->getConstantArrays () as $ constantArrayType ) {
279
- $ values = $ constantArrayType ->getValueTypes ();
280
- if (count ($ values ) !== 1 ) {
281
- return new MixedType ();
282
- }
283
-
284
- $ types [] = $ constantArrayType ->getFirstIterableValueType ();
133
+ if (!$ type instanceof ConstantIntegerType) {
134
+ return false ;
285
135
}
286
136
287
- return TypeCombinator:: union (... $ types ) ;
137
+ return $ type -> getValue () === AbstractQuery:: HYDRATE_OBJECT ;
288
138
}
289
139
290
140
private function originalReturnType (MethodReflection $ methodReflection ): Type
0 commit comments