1010use PHPStan \ShouldNotHappenException ;
1111use PHPStan \Type \Accessory \AccessoryArrayListType ;
1212use PHPStan \Type \ArrayType ;
13- use PHPStan \Type \Constant \ConstantArrayType ;
1413use PHPStan \Type \Constant \ConstantIntegerType ;
15- use PHPStan \Type \Doctrine \ObjectMetadataResolver ;
1614use PHPStan \Type \DynamicMethodReturnTypeExtension ;
1715use PHPStan \Type \IntegerType ;
1816use PHPStan \Type \IterableType ;
19- use PHPStan \Type \MixedType ;
2017use PHPStan \Type \NullType ;
21- use PHPStan \Type \ObjectWithoutClassType ;
2218use PHPStan \Type \Type ;
2319use PHPStan \Type \TypeCombinator ;
24- use PHPStan \Type \TypeTraverser ;
25- use PHPStan \Type \TypeWithClassName ;
2620use PHPStan \Type \VoidType ;
27- use function count ;
2821
2922final class QueryResultDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
3023{
@@ -39,32 +32,14 @@ final class QueryResultDynamicReturnTypeExtension implements DynamicMethodReturn
3932 'getSingleResult ' => 0 ,
4033 ];
4134
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-
5935 public function getClass (): string
6036 {
6137 return AbstractQuery::class;
6238 }
6339
6440 public function isMethodSupported (MethodReflection $ methodReflection ): bool
6541 {
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 ()]);
6843 }
6944
7045 public function getTypeFromMethodCall (
@@ -75,23 +50,21 @@ public function getTypeFromMethodCall(
7550 {
7651 $ methodName = $ methodReflection ->getName ();
7752
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+ }
8356
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 );
9362 } else {
94- throw new ShouldNotHappenException ();
63+ $ parametersAcceptor = ParametersAcceptorSelector::selectSingle (
64+ $ methodReflection ->getVariants ()
65+ );
66+ $ parameter = $ parametersAcceptor ->getParameters ()[$ argIndex ];
67+ $ hydrationMode = $ parameter ->getDefaultValue () ?? new NullType ();
9568 }
9669
9770 $ queryType = $ scope ->getType ($ methodCall ->var );
@@ -125,34 +98,12 @@ private function getMethodReturnTypeForHydrationMode(
12598 return $ this ->originalReturnType ($ methodReflection );
12699 }
127100
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.
129104 return $ this ->originalReturnType ($ methodReflection );
130105 }
131106
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-
156107 switch ($ methodReflection ->getName ()) {
157108 case 'getSingleResult ' :
158109 return $ queryResultType ;
@@ -164,10 +115,6 @@ private function getMethodReturnTypeForHydrationMode(
164115 $ queryResultType
165116 );
166117 default :
167- if ($ singleResult ) {
168- return $ queryResultType ;
169- }
170-
171118 if ($ queryKeyType ->isNull ()->yes ()) {
172119 return AccessoryArrayListType::intersectWith (new ArrayType (
173120 new IntegerType (),
@@ -181,86 +128,13 @@ private function getMethodReturnTypeForHydrationMode(
181128 }
182129 }
183130
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
228132 {
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 ;
261135 }
262136
263- return $ queryResultType -> getFirstIterableValueType () ;
137+ return $ type -> getValue () === AbstractQuery:: HYDRATE_OBJECT ;
264138 }
265139
266140 private function originalReturnType (MethodReflection $ methodReflection ): Type
0 commit comments