10
10
use PHPStan \ShouldNotHappenException ;
11
11
use PHPStan \Type \Accessory \AccessoryArrayListType ;
12
12
use PHPStan \Type \ArrayType ;
13
- use PHPStan \Type \Constant \ConstantArrayType ;
14
13
use PHPStan \Type \Constant \ConstantIntegerType ;
15
14
use PHPStan \Type \DynamicMethodReturnTypeExtension ;
16
15
use PHPStan \Type \GenericTypeVariableResolver ;
17
16
use PHPStan \Type \IntegerType ;
18
17
use PHPStan \Type \IterableType ;
19
18
use PHPStan \Type \MixedType ;
20
19
use PHPStan \Type \NullType ;
21
- use PHPStan \Type \ObjectWithoutClassType ;
22
20
use PHPStan \Type \Type ;
23
21
use PHPStan \Type \TypeCombinator ;
24
- use PHPStan \Type \TypeTraverser ;
25
22
use PHPStan \Type \TypeWithClassName ;
26
23
use PHPStan \Type \VoidType ;
27
- use function count ;
28
24
29
25
final class QueryResultDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
30
26
{
@@ -39,22 +35,14 @@ final class QueryResultDynamicReturnTypeExtension implements DynamicMethodReturn
39
35
'getSingleResult ' => 0 ,
40
36
];
41
37
42
- private const METHOD_HYDRATION_MODE = [
43
- 'getArrayResult ' => AbstractQuery::HYDRATE_ARRAY ,
44
- 'getScalarResult ' => AbstractQuery::HYDRATE_SCALAR ,
45
- 'getSingleColumnResult ' => AbstractQuery::HYDRATE_SCALAR_COLUMN ,
46
- 'getSingleScalarResult ' => AbstractQuery::HYDRATE_SINGLE_SCALAR ,
47
- ];
48
-
49
38
public function getClass (): string
50
39
{
51
40
return AbstractQuery::class;
52
41
}
53
42
54
43
public function isMethodSupported (MethodReflection $ methodReflection ): bool
55
44
{
56
- return isset (self ::METHOD_HYDRATION_MODE_ARG [$ methodReflection ->getName ()])
57
- || isset (self ::METHOD_HYDRATION_MODE [$ methodReflection ->getName ()]);
45
+ return isset (self ::METHOD_HYDRATION_MODE_ARG [$ methodReflection ->getName ()]);
58
46
}
59
47
60
48
public function getTypeFromMethodCall (
@@ -65,23 +53,21 @@ public function getTypeFromMethodCall(
65
53
{
66
54
$ methodName = $ methodReflection ->getName ();
67
55
68
- if (isset (self ::METHOD_HYDRATION_MODE [$ methodName ])) {
69
- $ hydrationMode = new ConstantIntegerType (self ::METHOD_HYDRATION_MODE [$ methodName ]);
70
- } elseif (isset (self ::METHOD_HYDRATION_MODE_ARG [$ methodName ])) {
71
- $ argIndex = self ::METHOD_HYDRATION_MODE_ARG [$ methodName ];
72
- $ args = $ methodCall ->getArgs ();
56
+ if (!isset (self ::METHOD_HYDRATION_MODE_ARG [$ methodName ])) {
57
+ throw new ShouldNotHappenException ();
58
+ }
59
+
60
+ $ argIndex = self ::METHOD_HYDRATION_MODE_ARG [$ methodName ];
61
+ $ args = $ methodCall ->getArgs ();
73
62
74
- if (isset ($ args [$ argIndex ])) {
75
- $ hydrationMode = $ scope ->getType ($ args [$ argIndex ]->value );
76
- } else {
77
- $ parametersAcceptor = ParametersAcceptorSelector::selectSingle (
78
- $ methodReflection ->getVariants ()
79
- );
80
- $ parameter = $ parametersAcceptor ->getParameters ()[$ argIndex ];
81
- $ hydrationMode = $ parameter ->getDefaultValue () ?? new NullType ();
82
- }
63
+ if (isset ($ args [$ argIndex ])) {
64
+ $ hydrationMode = $ scope ->getType ($ args [$ argIndex ]->value );
83
65
} else {
84
- throw new ShouldNotHappenException ();
66
+ $ parametersAcceptor = ParametersAcceptorSelector::selectSingle (
67
+ $ methodReflection ->getVariants ()
68
+ );
69
+ $ parameter = $ parametersAcceptor ->getParameters ()[$ argIndex ];
70
+ $ hydrationMode = $ parameter ->getDefaultValue () ?? new NullType ();
85
71
}
86
72
87
73
$ queryType = $ scope ->getType ($ methodCall ->var );
@@ -145,32 +131,12 @@ private function getMethodReturnTypeForHydrationMode(
145
131
return $ this ->originalReturnType ($ methodReflection );
146
132
}
147
133
148
- if (!$ hydrationMode instanceof ConstantIntegerType) {
134
+ if (!$ this ->isObjectHydrationMode ($ hydrationMode )) {
135
+ // We support only HYDRATE_OBJECT. For other hydration modes, we
136
+ // return the declared return type of the method.
149
137
return $ this ->originalReturnType ($ methodReflection );
150
138
}
151
139
152
- switch ($ hydrationMode ->getValue ()) {
153
- case AbstractQuery::HYDRATE_OBJECT :
154
- break ;
155
- case AbstractQuery::HYDRATE_ARRAY :
156
- $ queryResultType = $ this ->getArrayHydratedReturnType ($ queryResultType );
157
- break ;
158
- case AbstractQuery::HYDRATE_SCALAR :
159
- $ queryResultType = $ this ->getScalarHydratedReturnType ($ queryResultType );
160
- break ;
161
- case AbstractQuery::HYDRATE_SINGLE_SCALAR :
162
- $ queryResultType = $ this ->getSingleScalarHydratedReturnType ($ queryResultType );
163
- break ;
164
- case AbstractQuery::HYDRATE_SIMPLEOBJECT :
165
- $ queryResultType = $ this ->getSimpleObjectHydratedReturnType ($ queryResultType );
166
- break ;
167
- case AbstractQuery::HYDRATE_SCALAR_COLUMN :
168
- $ queryResultType = $ this ->getScalarColumnHydratedReturnType ($ queryResultType );
169
- break ;
170
- default :
171
- return $ this ->originalReturnType ($ methodReflection );
172
- }
173
-
174
140
switch ($ methodReflection ->getName ()) {
175
141
case 'getSingleResult ' :
176
142
return $ queryResultType ;
@@ -195,78 +161,13 @@ private function getMethodReturnTypeForHydrationMode(
195
161
}
196
162
}
197
163
198
- private function getArrayHydratedReturnType (Type $ queryResultType ): Type
164
+ private function isObjectHydrationMode (Type $ type ): bool
199
165
{
200
- return TypeTraverser::map (
201
- $ queryResultType ,
202
- static function (Type $ type , callable $ traverse ): Type {
203
- $ isObject = (new ObjectWithoutClassType ())->isSuperTypeOf ($ type );
204
- if ($ isObject ->yes ()) {
205
- return new ArrayType (new MixedType (), new MixedType ());
206
- }
207
- if ($ isObject ->maybe ()) {
208
- return new MixedType ();
209
- }
210
-
211
- return $ traverse ($ type );
212
- }
213
- );
214
- }
215
-
216
- private function getScalarHydratedReturnType (Type $ queryResultType ): Type
217
- {
218
- if (!$ queryResultType instanceof ArrayType) {
219
- return new ArrayType (new MixedType (), new MixedType ());
220
- }
221
-
222
- $ itemType = $ queryResultType ->getItemType ();
223
- $ hasNoObject = (new ObjectWithoutClassType ())->isSuperTypeOf ($ itemType )->no ();
224
- $ hasNoArray = $ itemType ->isArray ()->no ();
225
-
226
- if ($ hasNoArray && $ hasNoObject ) {
227
- return $ queryResultType ;
228
- }
229
-
230
- return new ArrayType (new MixedType (), new MixedType ());
231
- }
232
-
233
- private function getSimpleObjectHydratedReturnType (Type $ queryResultType ): Type
234
- {
235
- if ((new ObjectWithoutClassType ())->isSuperTypeOf ($ queryResultType )->yes ()) {
236
- return $ queryResultType ;
237
- }
238
-
239
- return new MixedType ();
240
- }
241
-
242
- private function getSingleScalarHydratedReturnType (Type $ queryResultType ): Type
243
- {
244
- $ queryResultType = $ this ->getScalarHydratedReturnType ($ queryResultType );
245
- if (!$ queryResultType instanceof ConstantArrayType) {
246
- return new ArrayType (new MixedType (), new MixedType ());
247
- }
248
-
249
- $ values = $ queryResultType ->getValueTypes ();
250
- if (count ($ values ) !== 1 ) {
251
- return new ArrayType (new MixedType (), new MixedType ());
252
- }
253
-
254
- return $ queryResultType ;
255
- }
256
-
257
- private function getScalarColumnHydratedReturnType (Type $ queryResultType ): Type
258
- {
259
- $ queryResultType = $ this ->getScalarHydratedReturnType ($ queryResultType );
260
- if (!$ queryResultType instanceof ConstantArrayType) {
261
- return new MixedType ();
262
- }
263
-
264
- $ values = $ queryResultType ->getValueTypes ();
265
- if (count ($ values ) !== 1 ) {
266
- return new MixedType ();
166
+ if (!$ type instanceof ConstantIntegerType) {
167
+ return false ;
267
168
}
268
169
269
- return $ queryResultType -> getFirstIterableValueType () ;
170
+ return $ type -> getValue () === AbstractQuery:: HYDRATE_OBJECT ;
270
171
}
271
172
272
173
private function originalReturnType (MethodReflection $ methodReflection ): Type
0 commit comments