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
- 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 \TypeWithClassName ;
26
20
use PHPStan \Type \VoidType ;
27
- use function count ;
28
21
29
22
final class QueryResultDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
30
23
{
@@ -39,32 +32,14 @@ final class QueryResultDynamicReturnTypeExtension implements DynamicMethodReturn
39
32
'getSingleResult ' => 0 ,
40
33
];
41
34
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
- /** @var ObjectMetadataResolver */
50
- private $ objectMetadataResolver ;
51
-
52
- public function __construct (
53
- ObjectMetadataResolver $ objectMetadataResolver
54
- )
55
- {
56
- $ this ->objectMetadataResolver = $ objectMetadataResolver ;
57
- }
58
-
59
35
public function getClass (): string
60
36
{
61
37
return AbstractQuery::class;
62
38
}
63
39
64
40
public function isMethodSupported (MethodReflection $ methodReflection ): bool
65
41
{
66
- return isset (self ::METHOD_HYDRATION_MODE_ARG [$ methodReflection ->getName ()])
67
- || isset (self ::METHOD_HYDRATION_MODE [$ methodReflection ->getName ()]);
42
+ return isset (self ::METHOD_HYDRATION_MODE_ARG [$ methodReflection ->getName ()]);
68
43
}
69
44
70
45
public function getTypeFromMethodCall (
@@ -75,23 +50,21 @@ public function getTypeFromMethodCall(
75
50
{
76
51
$ methodName = $ methodReflection ->getName ();
77
52
78
- if (isset (self ::METHOD_HYDRATION_MODE [$ methodName ])) {
79
- $ hydrationMode = new ConstantIntegerType (self ::METHOD_HYDRATION_MODE [$ methodName ]);
80
- } elseif (isset (self ::METHOD_HYDRATION_MODE_ARG [$ methodName ])) {
81
- $ argIndex = self ::METHOD_HYDRATION_MODE_ARG [$ methodName ];
82
- $ args = $ methodCall ->getArgs ();
53
+ if (!isset (self ::METHOD_HYDRATION_MODE_ARG [$ methodName ])) {
54
+ throw new ShouldNotHappenException ();
55
+ }
83
56
84
- if (isset ($ args [$ argIndex ])) {
85
- $ hydrationMode = $ scope ->getType ($ args [$ argIndex ]->value );
86
- } else {
87
- $ parametersAcceptor = ParametersAcceptorSelector::selectSingle (
88
- $ methodReflection ->getVariants ()
89
- );
90
- $ parameter = $ parametersAcceptor ->getParameters ()[$ argIndex ];
91
- $ hydrationMode = $ parameter ->getDefaultValue () ?? new NullType ();
92
- }
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 );
93
62
} else {
94
- throw new ShouldNotHappenException ();
63
+ $ parametersAcceptor = ParametersAcceptorSelector::selectSingle (
64
+ $ methodReflection ->getVariants ()
65
+ );
66
+ $ parameter = $ parametersAcceptor ->getParameters ()[$ argIndex ];
67
+ $ hydrationMode = $ parameter ->getDefaultValue () ?? new NullType ();
95
68
}
96
69
97
70
$ queryType = $ scope ->getType ($ methodCall ->var );
@@ -125,34 +98,12 @@ private function getMethodReturnTypeForHydrationMode(
125
98
return $ this ->originalReturnType ($ methodReflection );
126
99
}
127
100
128
- 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.
129
104
return $ this ->originalReturnType ($ methodReflection );
130
105
}
131
106
132
- $ singleResult = false ;
133
- switch ($ hydrationMode ->getValue ()) {
134
- case AbstractQuery::HYDRATE_OBJECT :
135
- break ;
136
- case AbstractQuery::HYDRATE_ARRAY :
137
- $ queryResultType = $ this ->getArrayHydratedReturnType ($ queryResultType );
138
- break ;
139
- case AbstractQuery::HYDRATE_SCALAR :
140
- $ queryResultType = $ this ->getScalarHydratedReturnType ($ queryResultType );
141
- break ;
142
- case AbstractQuery::HYDRATE_SINGLE_SCALAR :
143
- $ singleResult = true ;
144
- $ queryResultType = $ this ->getSingleScalarHydratedReturnType ($ queryResultType );
145
- break ;
146
- case AbstractQuery::HYDRATE_SIMPLEOBJECT :
147
- $ queryResultType = $ this ->getSimpleObjectHydratedReturnType ($ queryResultType );
148
- break ;
149
- case AbstractQuery::HYDRATE_SCALAR_COLUMN :
150
- $ queryResultType = $ this ->getScalarColumnHydratedReturnType ($ queryResultType );
151
- break ;
152
- default :
153
- return $ this ->originalReturnType ($ methodReflection );
154
- }
155
-
156
107
switch ($ methodReflection ->getName ()) {
157
108
case 'getSingleResult ' :
158
109
return $ queryResultType ;
@@ -164,10 +115,6 @@ private function getMethodReturnTypeForHydrationMode(
164
115
$ queryResultType
165
116
);
166
117
default :
167
- if ($ singleResult ) {
168
- return $ queryResultType ;
169
- }
170
-
171
118
if ($ queryKeyType ->isNull ()->yes ()) {
172
119
return AccessoryArrayListType::intersectWith (new ArrayType (
173
120
new IntegerType (),
@@ -181,86 +128,13 @@ private function getMethodReturnTypeForHydrationMode(
181
128
}
182
129
}
183
130
184
- private function getArrayHydratedReturnType (Type $ queryResultType ): Type
185
- {
186
- $ objectManager = $ this ->objectMetadataResolver ->getObjectManager ();
187
-
188
- return TypeTraverser::map (
189
- $ queryResultType ,
190
- static function (Type $ type , callable $ traverse ) use ($ objectManager ): Type {
191
- $ isObject = (new ObjectWithoutClassType ())->isSuperTypeOf ($ type );
192
- if ($ isObject ->no ()) {
193
- return $ traverse ($ type );
194
- }
195
- if (
196
- $ isObject ->maybe ()
197
- || !$ type instanceof TypeWithClassName
198
- || $ objectManager === null
199
- ) {
200
- return new MixedType ();
201
- }
202
-
203
- return $ objectManager ->getMetadataFactory ()->hasMetadataFor ($ type ->getClassName ())
204
- ? new ArrayType (new MixedType (), new MixedType ())
205
- : $ traverse ($ type );
206
- }
207
- );
208
- }
209
-
210
- private function getScalarHydratedReturnType (Type $ queryResultType ): Type
211
- {
212
- if (!$ queryResultType instanceof ArrayType) {
213
- return new ArrayType (new MixedType (), new MixedType ());
214
- }
215
-
216
- $ itemType = $ queryResultType ->getItemType ();
217
- $ hasNoObject = (new ObjectWithoutClassType ())->isSuperTypeOf ($ itemType )->no ();
218
- $ hasNoArray = $ itemType ->isArray ()->no ();
219
-
220
- if ($ hasNoArray && $ hasNoObject ) {
221
- return $ queryResultType ;
222
- }
223
-
224
- return new ArrayType (new MixedType (), new MixedType ());
225
- }
226
-
227
- private function getSimpleObjectHydratedReturnType (Type $ queryResultType ): Type
131
+ private function isObjectHydrationMode (Type $ type ): bool
228
132
{
229
- if ((new ObjectWithoutClassType ())->isSuperTypeOf ($ queryResultType )->yes ()) {
230
- return $ queryResultType ;
231
- }
232
-
233
- return new MixedType ();
234
- }
235
-
236
- private function getSingleScalarHydratedReturnType (Type $ queryResultType ): Type
237
- {
238
- $ queryResultType = $ this ->getScalarHydratedReturnType ($ queryResultType );
239
- if (!$ queryResultType instanceof ConstantArrayType) {
240
- return new MixedType ();
241
- }
242
-
243
- $ values = $ queryResultType ->getValueTypes ();
244
- if (count ($ values ) !== 1 ) {
245
- return new MixedType ();
246
- }
247
-
248
- return $ queryResultType ->getFirstIterableValueType ();
249
- }
250
-
251
- private function getScalarColumnHydratedReturnType (Type $ queryResultType ): Type
252
- {
253
- $ queryResultType = $ this ->getScalarHydratedReturnType ($ queryResultType );
254
- if (!$ queryResultType instanceof ConstantArrayType) {
255
- return new MixedType ();
256
- }
257
-
258
- $ values = $ queryResultType ->getValueTypes ();
259
- if (count ($ values ) !== 1 ) {
260
- return new MixedType ();
133
+ if (!$ type instanceof ConstantIntegerType) {
134
+ return false ;
261
135
}
262
136
263
- return $ queryResultType -> getFirstIterableValueType () ;
137
+ return $ type -> getValue () === AbstractQuery:: HYDRATE_OBJECT ;
264
138
}
265
139
266
140
private function originalReturnType (MethodReflection $ methodReflection ): Type
0 commit comments