1010use PHPStan \ShouldNotHappenException ;
1111use PHPStan \Type \Accessory \AccessoryArrayListType ;
1212use PHPStan \Type \ArrayType ;
13- use PHPStan \Type \Constant \ConstantArrayType ;
1413use PHPStan \Type \Constant \ConstantIntegerType ;
1514use PHPStan \Type \DynamicMethodReturnTypeExtension ;
1615use PHPStan \Type \GenericTypeVariableResolver ;
1716use PHPStan \Type \IntegerType ;
1817use PHPStan \Type \IterableType ;
1918use PHPStan \Type \MixedType ;
2019use PHPStan \Type \NullType ;
21- use PHPStan \Type \ObjectWithoutClassType ;
2220use PHPStan \Type \Type ;
2321use PHPStan \Type \TypeCombinator ;
24- use PHPStan \Type \TypeTraverser ;
2522use PHPStan \Type \TypeWithClassName ;
2623use PHPStan \Type \VoidType ;
27- use function count ;
2824
2925final class QueryResultDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
3026{
@@ -39,22 +35,14 @@ final class QueryResultDynamicReturnTypeExtension implements DynamicMethodReturn
3935 'getSingleResult ' => 0 ,
4036 ];
4137
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-
4938 public function getClass (): string
5039 {
5140 return AbstractQuery::class;
5241 }
5342
5443 public function isMethodSupported (MethodReflection $ methodReflection ): bool
5544 {
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 ()]);
5846 }
5947
6048 public function getTypeFromMethodCall (
@@ -65,23 +53,21 @@ public function getTypeFromMethodCall(
6553 {
6654 $ methodName = $ methodReflection ->getName ();
6755
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 ();
7362
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 );
8365 } else {
84- throw new ShouldNotHappenException ();
66+ $ parametersAcceptor = ParametersAcceptorSelector::selectSingle (
67+ $ methodReflection ->getVariants ()
68+ );
69+ $ parameter = $ parametersAcceptor ->getParameters ()[$ argIndex ];
70+ $ hydrationMode = $ parameter ->getDefaultValue () ?? new NullType ();
8571 }
8672
8773 $ queryType = $ scope ->getType ($ methodCall ->var );
@@ -145,32 +131,12 @@ private function getMethodReturnTypeForHydrationMode(
145131 return $ this ->originalReturnType ($ methodReflection );
146132 }
147133
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.
149137 return $ this ->originalReturnType ($ methodReflection );
150138 }
151139
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-
174140 switch ($ methodReflection ->getName ()) {
175141 case 'getSingleResult ' :
176142 return $ queryResultType ;
@@ -195,78 +161,13 @@ private function getMethodReturnTypeForHydrationMode(
195161 }
196162 }
197163
198- private function getArrayHydratedReturnType (Type $ queryResultType ): Type
164+ private function isObjectHydrationMode (Type $ type ): bool
199165 {
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 ;
267168 }
268169
269- return $ queryResultType -> getFirstIterableValueType () ;
170+ return $ type -> getValue () === AbstractQuery:: HYDRATE_OBJECT ;
270171 }
271172
272173 private function originalReturnType (MethodReflection $ methodReflection ): Type
0 commit comments