Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit cefaeec

Browse files
committedDec 26, 2022
Improve QueryResultDynamicReturnTypeExtension
1 parent f769796 commit cefaeec

File tree

3 files changed

+478
-33
lines changed

3 files changed

+478
-33
lines changed
 

‎composer.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
],
88
"require": {
99
"php": "^7.2 || ^8.0",
10-
"phpstan/phpstan": "^1.8.11"
10+
"phpstan/phpstan": "^1.9.0"
1111
},
1212
"conflict": {
1313
"doctrine/collections": "<1.0",

‎src/Type/Doctrine/Query/QueryResultDynamicReturnTypeExtension.php

+121-22
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,20 @@
99
use PHPStan\Reflection\ParametersAcceptorSelector;
1010
use PHPStan\ShouldNotHappenException;
1111
use PHPStan\Type\ArrayType;
12+
use PHPStan\Type\Constant\ConstantArrayType;
1213
use PHPStan\Type\Constant\ConstantIntegerType;
1314
use PHPStan\Type\DynamicMethodReturnTypeExtension;
1415
use PHPStan\Type\Generic\GenericObjectType;
1516
use PHPStan\Type\IntegerType;
1617
use PHPStan\Type\IterableType;
1718
use PHPStan\Type\MixedType;
1819
use PHPStan\Type\NullType;
20+
use PHPStan\Type\ObjectWithoutClassType;
1921
use PHPStan\Type\Type;
2022
use PHPStan\Type\TypeCombinator;
23+
use PHPStan\Type\TypeTraverser;
2124
use PHPStan\Type\VoidType;
25+
use function count;
2226

2327
final class QueryResultDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
2428
{
@@ -33,14 +37,22 @@ final class QueryResultDynamicReturnTypeExtension implements DynamicMethodReturn
3337
'getSingleResult' => 0,
3438
];
3539

40+
private const METHOD_HYDRATION_MODE = [
41+
'getArrayResult' => AbstractQuery::HYDRATE_ARRAY,
42+
'getScalarResult' => AbstractQuery::HYDRATE_SCALAR,
43+
'getSingleColumnResult' => AbstractQuery::HYDRATE_SCALAR_COLUMN,
44+
'getSingleScalarResult' => AbstractQuery::HYDRATE_SINGLE_SCALAR,
45+
];
46+
3647
public function getClass(): string
3748
{
3849
return AbstractQuery::class;
3950
}
4051

4152
public function isMethodSupported(MethodReflection $methodReflection): bool
4253
{
43-
return isset(self::METHOD_HYDRATION_MODE_ARG[$methodReflection->getName()]);
54+
return isset(self::METHOD_HYDRATION_MODE_ARG[$methodReflection->getName()])
55+
|| isset(self::METHOD_HYDRATION_MODE[$methodReflection->getName()]);
4456
}
4557

4658
public function getTypeFromMethodCall(
@@ -51,21 +63,23 @@ public function getTypeFromMethodCall(
5163
{
5264
$methodName = $methodReflection->getName();
5365

54-
if (!isset(self::METHOD_HYDRATION_MODE_ARG[$methodName])) {
55-
throw new ShouldNotHappenException();
56-
}
57-
58-
$argIndex = self::METHOD_HYDRATION_MODE_ARG[$methodName];
59-
$args = $methodCall->getArgs();
60-
61-
if (isset($args[$argIndex])) {
62-
$hydrationMode = $scope->getType($args[$argIndex]->value);
66+
if (isset(self::METHOD_HYDRATION_MODE[$methodName])) {
67+
$hydrationMode = new ConstantIntegerType(self::METHOD_HYDRATION_MODE[$methodName]);
68+
} elseif (isset(self::METHOD_HYDRATION_MODE_ARG[$methodName])) {
69+
$argIndex = self::METHOD_HYDRATION_MODE_ARG[$methodName];
70+
$args = $methodCall->getArgs();
71+
72+
if (isset($args[$argIndex])) {
73+
$hydrationMode = $scope->getType($args[$argIndex]->value);
74+
} else {
75+
$parametersAcceptor = ParametersAcceptorSelector::selectSingle(
76+
$methodReflection->getVariants()
77+
);
78+
$parameter = $parametersAcceptor->getParameters()[$argIndex];
79+
$hydrationMode = $parameter->getDefaultValue() ?? new NullType();
80+
}
6381
} else {
64-
$parametersAcceptor = ParametersAcceptorSelector::selectSingle(
65-
$methodReflection->getVariants()
66-
);
67-
$parameter = $parametersAcceptor->getParameters()[$argIndex];
68-
$hydrationMode = $parameter->getDefaultValue() ?? new NullType();
82+
throw new ShouldNotHappenException();
6983
}
7084

7185
$queryType = $scope->getType($methodCall->var);
@@ -109,12 +123,32 @@ private function getMethodReturnTypeForHydrationMode(
109123
return $this->originalReturnType($methodReflection);
110124
}
111125

112-
if (!$this->isObjectHydrationMode($hydrationMode)) {
113-
// We support only HYDRATE_OBJECT. For other hydration modes, we
114-
// return the declared return type of the method.
126+
if (!$hydrationMode instanceof ConstantIntegerType) {
115127
return $this->originalReturnType($methodReflection);
116128
}
117129

130+
switch ($hydrationMode->getValue()) {
131+
case AbstractQuery::HYDRATE_OBJECT:
132+
break;
133+
case AbstractQuery::HYDRATE_ARRAY:
134+
$queryResultType = $this->getArrayHydratedReturnType($queryResultType);
135+
break;
136+
case AbstractQuery::HYDRATE_SCALAR:
137+
$queryResultType = $this->getScalarHydratedReturnType($queryResultType);
138+
break;
139+
case AbstractQuery::HYDRATE_SINGLE_SCALAR:
140+
$queryResultType = $this->getSingleScalarHydratedReturnType($queryResultType);
141+
break;
142+
case AbstractQuery::HYDRATE_SIMPLEOBJECT:
143+
$queryResultType = $this->getSimpleObjectHydratedReturnType($queryResultType);
144+
break;
145+
case AbstractQuery::HYDRATE_SCALAR_COLUMN:
146+
$queryResultType = $this->getScalarColumnHydratedReturnType($queryResultType);
147+
break;
148+
default:
149+
return $this->originalReturnType($methodReflection);
150+
}
151+
118152
switch ($methodReflection->getName()) {
119153
case 'getSingleResult':
120154
return $queryResultType;
@@ -133,13 +167,78 @@ private function getMethodReturnTypeForHydrationMode(
133167
}
134168
}
135169

136-
private function isObjectHydrationMode(Type $type): bool
170+
private function getArrayHydratedReturnType(Type $queryResultType): Type
171+
{
172+
return TypeTraverser::map(
173+
$queryResultType,
174+
static function (Type $type, callable $traverse): Type {
175+
$isObject = (new ObjectWithoutClassType())->isSuperTypeOf($type);
176+
if ($isObject->yes()) {
177+
return new ArrayType(new MixedType(), new MixedType());
178+
}
179+
if ($isObject->maybe()) {
180+
return new MixedType();
181+
}
182+
183+
return $traverse($type);
184+
}
185+
);
186+
}
187+
188+
private function getScalarHydratedReturnType(Type $queryResultType): Type
137189
{
138-
if (!$type instanceof ConstantIntegerType) {
139-
return false;
190+
if (!$queryResultType instanceof ArrayType) {
191+
return new ArrayType(new MixedType(), new MixedType());
192+
}
193+
194+
$itemType = $queryResultType->getItemType();
195+
$hasNoObject = (new ObjectWithoutClassType())->isSuperTypeOf($itemType)->no();
196+
$hasNoArray = $itemType->isArray()->no();
197+
198+
if ($hasNoArray && $hasNoObject) {
199+
return $queryResultType;
200+
}
201+
202+
return new ArrayType(new MixedType(), new MixedType());
203+
}
204+
205+
private function getSimpleObjectHydratedReturnType(Type $queryResultType): Type
206+
{
207+
if ((new ObjectWithoutClassType())->isSuperTypeOf($queryResultType)->yes()) {
208+
return $queryResultType;
209+
}
210+
211+
return new MixedType();
212+
}
213+
214+
private function getSingleScalarHydratedReturnType(Type $queryResultType): Type
215+
{
216+
$queryResultType = $this->getScalarHydratedReturnType($queryResultType);
217+
if (!$queryResultType instanceof ConstantArrayType) {
218+
return new ArrayType(new MixedType(), new MixedType());
219+
}
220+
221+
$values = $queryResultType->getValueTypes();
222+
if (count($values) !== 1) {
223+
return new ArrayType(new MixedType(), new MixedType());
224+
}
225+
226+
return $queryResultType;
227+
}
228+
229+
private function getScalarColumnHydratedReturnType(Type $queryResultType): Type
230+
{
231+
$queryResultType = $this->getScalarHydratedReturnType($queryResultType);
232+
if (!$queryResultType instanceof ConstantArrayType) {
233+
return new MixedType();
234+
}
235+
236+
$values = $queryResultType->getValueTypes();
237+
if (count($values) !== 1) {
238+
return new MixedType();
140239
}
141240

142-
return $type->getValue() === AbstractQuery::HYDRATE_OBJECT;
241+
return $queryResultType->getFirstIterableValueType();
143242
}
144243

145244
private function originalReturnType(MethodReflection $methodReflection): Type

‎tests/Type/Doctrine/data/QueryResult/queryResult.php

+356-10
Original file line numberDiff line numberDiff line change
@@ -143,45 +143,391 @@ public function testReturnTypeOfQueryMethodsWithExplicitObjectHydrationMode(Enti
143143
}
144144

145145
/**
146-
* Test that we properly infer the return type of Query methods with explicit hydration mode that is not HYDRATE_OBJECT
146+
* Test that we properly infer the return type of Query methods with explicit hydration mode of HYDRATE_ARRAY
147147
*
148-
* We are never able to infer the return type here
148+
* We can infer the return type by changing every object by an array
149149
*/
150-
public function testReturnTypeOfQueryMethodsWithExplicitNonObjectHydrationMode(EntityManagerInterface $em): void
150+
public function testReturnTypeOfQueryMethodsWithExplicitArrayHydrationMode(EntityManagerInterface $em): void
151151
{
152152
$query = $em->createQuery('
153153
SELECT m
154154
FROM QueryResult\Entities\Many m
155155
');
156156

157157
assertType(
158-
'mixed',
158+
'array<array>',
159159
$query->getResult(AbstractQuery::HYDRATE_ARRAY)
160160
);
161161
assertType(
162-
'iterable',
162+
'array<array>',
163+
$query->getArrayResult()
164+
);
165+
assertType(
166+
'iterable<array>',
163167
$query->toIterable([], AbstractQuery::HYDRATE_ARRAY)
164168
);
165169
assertType(
166-
'mixed',
170+
'array<array>',
167171
$query->execute(null, AbstractQuery::HYDRATE_ARRAY)
168172
);
169173
assertType(
170-
'mixed',
174+
'array<array>',
171175
$query->executeIgnoreQueryCache(null, AbstractQuery::HYDRATE_ARRAY)
172176
);
173177
assertType(
174-
'mixed',
178+
'array<array>',
175179
$query->executeUsingQueryCache(null, AbstractQuery::HYDRATE_ARRAY)
176180
);
177181
assertType(
178-
'mixed',
182+
'array',
179183
$query->getSingleResult(AbstractQuery::HYDRATE_ARRAY)
180184
);
181185
assertType(
182-
'mixed',
186+
'array|null',
183187
$query->getOneOrNullResult(AbstractQuery::HYDRATE_ARRAY)
184188
);
189+
190+
$query = $em->createQuery('
191+
SELECT m.intColumn, m.stringNullColumn
192+
FROM QueryResult\Entities\Many m
193+
');
194+
195+
assertType(
196+
'array<array{intColumn: int, stringNullColumn: string|null}>',
197+
$query->getResult(AbstractQuery::HYDRATE_ARRAY)
198+
);
199+
assertType(
200+
'array<array{intColumn: int, stringNullColumn: string|null}>',
201+
$query->getArrayResult()
202+
);
203+
assertType(
204+
'array<array{intColumn: int, stringNullColumn: string|null}>',
205+
$query->execute(null, AbstractQuery::HYDRATE_ARRAY)
206+
);
207+
assertType(
208+
'array<array{intColumn: int, stringNullColumn: string|null}>',
209+
$query->executeIgnoreQueryCache(null, AbstractQuery::HYDRATE_ARRAY)
210+
);
211+
assertType(
212+
'array<array{intColumn: int, stringNullColumn: string|null}>',
213+
$query->executeUsingQueryCache(null, AbstractQuery::HYDRATE_ARRAY)
214+
);
215+
assertType(
216+
'array{intColumn: int, stringNullColumn: string|null}',
217+
$query->getSingleResult(AbstractQuery::HYDRATE_ARRAY)
218+
);
219+
assertType(
220+
'array{intColumn: int, stringNullColumn: string|null}|null',
221+
$query->getOneOrNullResult(AbstractQuery::HYDRATE_ARRAY)
222+
);
223+
}
224+
225+
/**
226+
* Test that we properly infer the return type of Query methods with explicit hydration mode of HYDRATE_SCALAR
227+
*/
228+
public function testReturnTypeOfQueryMethodsWithExplicitScalarHydrationMode(EntityManagerInterface $em): void
229+
{
230+
$query = $em->createQuery('
231+
SELECT m
232+
FROM QueryResult\Entities\Many m
233+
');
234+
235+
assertType(
236+
'array<array>',
237+
$query->getResult(AbstractQuery::HYDRATE_SCALAR)
238+
);
239+
assertType(
240+
'array<array>',
241+
$query->getScalarResult()
242+
);
243+
assertType(
244+
'iterable<array>',
245+
$query->toIterable([], AbstractQuery::HYDRATE_SCALAR)
246+
);
247+
assertType(
248+
'array<array>',
249+
$query->execute(null, AbstractQuery::HYDRATE_SCALAR)
250+
);
251+
assertType(
252+
'array<array>',
253+
$query->executeIgnoreQueryCache(null, AbstractQuery::HYDRATE_SCALAR)
254+
);
255+
assertType(
256+
'array<array>',
257+
$query->executeUsingQueryCache(null, AbstractQuery::HYDRATE_SCALAR)
258+
);
259+
assertType(
260+
'array',
261+
$query->getSingleResult(AbstractQuery::HYDRATE_SCALAR)
262+
);
263+
assertType(
264+
'array|null',
265+
$query->getOneOrNullResult(AbstractQuery::HYDRATE_SCALAR)
266+
);
267+
268+
$query = $em->createQuery('
269+
SELECT m.intColumn, m.stringNullColumn
270+
FROM QueryResult\Entities\Many m
271+
');
272+
273+
assertType(
274+
'array<array{intColumn: int, stringNullColumn: string|null}>',
275+
$query->getResult(AbstractQuery::HYDRATE_SCALAR)
276+
);
277+
assertType(
278+
'array<array{intColumn: int, stringNullColumn: string|null}>',
279+
$query->getScalarResult()
280+
);
281+
assertType(
282+
'array<array{intColumn: int, stringNullColumn: string|null}>',
283+
$query->execute(null, AbstractQuery::HYDRATE_SCALAR)
284+
);
285+
assertType(
286+
'array<array{intColumn: int, stringNullColumn: string|null}>',
287+
$query->executeIgnoreQueryCache(null, AbstractQuery::HYDRATE_SCALAR)
288+
);
289+
assertType(
290+
'array<array{intColumn: int, stringNullColumn: string|null}>',
291+
$query->executeUsingQueryCache(null, AbstractQuery::HYDRATE_SCALAR)
292+
);
293+
assertType(
294+
'array{intColumn: int, stringNullColumn: string|null}',
295+
$query->getSingleResult(AbstractQuery::HYDRATE_SCALAR)
296+
);
297+
assertType(
298+
'array{intColumn: int, stringNullColumn: string|null}|null',
299+
$query->getOneOrNullResult(AbstractQuery::HYDRATE_SCALAR)
300+
);
301+
}
302+
303+
/**
304+
* Test that we properly infer the return type of Query methods with explicit hydration mode of HYDRATE_SCALAR
305+
*/
306+
public function testReturnTypeOfQueryMethodsWithExplicitSingleScalarHydrationMode(EntityManagerInterface $em): void
307+
{
308+
$query = $em->createQuery('
309+
SELECT m
310+
FROM QueryResult\Entities\Many m
311+
');
312+
313+
assertType(
314+
'array<array>',
315+
$query->getResult(AbstractQuery::HYDRATE_SINGLE_SCALAR)
316+
);
317+
assertType(
318+
'array<array>',
319+
$query->getSingleScalarResult()
320+
);
321+
assertType(
322+
'iterable<array>',
323+
$query->toIterable([], AbstractQuery::HYDRATE_SINGLE_SCALAR)
324+
);
325+
assertType(
326+
'array<array>',
327+
$query->execute(null, AbstractQuery::HYDRATE_SINGLE_SCALAR)
328+
);
329+
assertType(
330+
'array<array>',
331+
$query->executeIgnoreQueryCache(null, AbstractQuery::HYDRATE_SINGLE_SCALAR)
332+
);
333+
assertType(
334+
'array<array>',
335+
$query->executeUsingQueryCache(null, AbstractQuery::HYDRATE_SINGLE_SCALAR)
336+
);
337+
assertType(
338+
'array',
339+
$query->getSingleResult(AbstractQuery::HYDRATE_SINGLE_SCALAR)
340+
);
341+
assertType(
342+
'array|null',
343+
$query->getOneOrNullResult(AbstractQuery::HYDRATE_SINGLE_SCALAR)
344+
);
345+
346+
$query = $em->createQuery('
347+
SELECT m.intColumn
348+
FROM QueryResult\Entities\Many m
349+
');
350+
351+
assertType(
352+
'array<array{intColumn: int}>',
353+
$query->getResult(AbstractQuery::HYDRATE_SINGLE_SCALAR)
354+
);
355+
assertType(
356+
'array<array{intColumn: int}>',
357+
$query->getSingleScalarResult()
358+
);
359+
assertType(
360+
'array<array{intColumn: int}>',
361+
$query->execute(null, AbstractQuery::HYDRATE_SINGLE_SCALAR)
362+
);
363+
assertType(
364+
'array<array{intColumn: int}>',
365+
$query->executeIgnoreQueryCache(null, AbstractQuery::HYDRATE_SINGLE_SCALAR)
366+
);
367+
assertType(
368+
'array<array{intColumn: int}>',
369+
$query->executeUsingQueryCache(null, AbstractQuery::HYDRATE_SINGLE_SCALAR)
370+
);
371+
assertType(
372+
'array{intColumn: int}',
373+
$query->getSingleResult(AbstractQuery::HYDRATE_SINGLE_SCALAR)
374+
);
375+
assertType(
376+
'array{intColumn: int}|null',
377+
$query->getOneOrNullResult(AbstractQuery::HYDRATE_SINGLE_SCALAR)
378+
);
379+
}
380+
381+
/**
382+
* Test that we properly infer the return type of Query methods with explicit hydration mode of HYDRATE_SIMPLEOBJECT
383+
*
384+
* We are never able to infer the return type here
385+
*/
386+
public function testReturnTypeOfQueryMethodsWithExplicitSimpleObjectHydrationMode(EntityManagerInterface $em): void
387+
{
388+
$query = $em->createQuery('
389+
SELECT m
390+
FROM QueryResult\Entities\Many m
391+
');
392+
393+
assertType(
394+
'array<QueryResult\Entities\Many>',
395+
$query->getResult(AbstractQuery::HYDRATE_SIMPLEOBJECT)
396+
);
397+
assertType(
398+
'iterable<QueryResult\Entities\Many>',
399+
$query->toIterable([], AbstractQuery::HYDRATE_SIMPLEOBJECT)
400+
);
401+
assertType(
402+
'array<QueryResult\Entities\Many>',
403+
$query->execute(null, AbstractQuery::HYDRATE_SIMPLEOBJECT)
404+
);
405+
assertType(
406+
'array<QueryResult\Entities\Many>',
407+
$query->executeIgnoreQueryCache(null, AbstractQuery::HYDRATE_SIMPLEOBJECT)
408+
);
409+
assertType(
410+
'array<QueryResult\Entities\Many>',
411+
$query->executeUsingQueryCache(null, AbstractQuery::HYDRATE_SIMPLEOBJECT)
412+
);
413+
assertType(
414+
'QueryResult\Entities\Many',
415+
$query->getSingleResult(AbstractQuery::HYDRATE_SIMPLEOBJECT)
416+
);
417+
assertType(
418+
'QueryResult\Entities\Many|null',
419+
$query->getOneOrNullResult(AbstractQuery::HYDRATE_SIMPLEOBJECT)
420+
);
421+
422+
$query = $em->createQuery('
423+
SELECT m.intColumn, m.stringNullColumn
424+
FROM QueryResult\Entities\Many m
425+
');
426+
427+
assertType(
428+
'array',
429+
$query->getResult(AbstractQuery::HYDRATE_SIMPLEOBJECT)
430+
);
431+
assertType(
432+
'array',
433+
$query->execute(null, AbstractQuery::HYDRATE_SIMPLEOBJECT)
434+
);
435+
assertType(
436+
'array',
437+
$query->executeIgnoreQueryCache(null, AbstractQuery::HYDRATE_SIMPLEOBJECT)
438+
);
439+
assertType(
440+
'array',
441+
$query->executeUsingQueryCache(null, AbstractQuery::HYDRATE_SIMPLEOBJECT)
442+
);
443+
assertType(
444+
'mixed',
445+
$query->getSingleResult(AbstractQuery::HYDRATE_SIMPLEOBJECT)
446+
);
447+
assertType(
448+
'mixed',
449+
$query->getOneOrNullResult(AbstractQuery::HYDRATE_SIMPLEOBJECT)
450+
);
451+
}
452+
453+
/**
454+
* Test that we properly infer the return type of Query methods with explicit hydration mode of HYDRATE_SCALAR_COLUMN
455+
*
456+
* We are never able to infer the return type here
457+
*/
458+
public function testReturnTypeOfQueryMethodsWithExplicitScalarColumnHydrationMode(EntityManagerInterface $em): void
459+
{
460+
$query = $em->createQuery('
461+
SELECT m
462+
FROM QueryResult\Entities\Many m
463+
');
464+
465+
assertType(
466+
'array',
467+
$query->getResult(AbstractQuery::HYDRATE_SCALAR_COLUMN)
468+
);
469+
assertType(
470+
'array',
471+
$query->getSingleColumnResult()
472+
);
473+
assertType(
474+
'iterable',
475+
$query->toIterable([], AbstractQuery::HYDRATE_SCALAR_COLUMN)
476+
);
477+
assertType(
478+
'array',
479+
$query->execute(null, AbstractQuery::HYDRATE_SCALAR_COLUMN)
480+
);
481+
assertType(
482+
'array',
483+
$query->executeIgnoreQueryCache(null, AbstractQuery::HYDRATE_SCALAR_COLUMN)
484+
);
485+
assertType(
486+
'array',
487+
$query->executeUsingQueryCache(null, AbstractQuery::HYDRATE_SCALAR_COLUMN)
488+
);
489+
assertType(
490+
'mixed',
491+
$query->getSingleResult(AbstractQuery::HYDRATE_SCALAR_COLUMN)
492+
);
493+
assertType(
494+
'mixed',
495+
$query->getOneOrNullResult(AbstractQuery::HYDRATE_SCALAR_COLUMN)
496+
);
497+
498+
$query = $em->createQuery('
499+
SELECT m.intColumn
500+
FROM QueryResult\Entities\Many m
501+
');
502+
503+
assertType(
504+
'array<int>',
505+
$query->getResult(AbstractQuery::HYDRATE_SCALAR_COLUMN)
506+
);
507+
assertType(
508+
'array<int>',
509+
$query->getSingleColumnResult()
510+
);
511+
assertType(
512+
'array<int>',
513+
$query->execute(null, AbstractQuery::HYDRATE_SCALAR_COLUMN)
514+
);
515+
assertType(
516+
'array<int>',
517+
$query->executeIgnoreQueryCache(null, AbstractQuery::HYDRATE_SCALAR_COLUMN)
518+
);
519+
assertType(
520+
'array<int>',
521+
$query->executeUsingQueryCache(null, AbstractQuery::HYDRATE_SCALAR_COLUMN)
522+
);
523+
assertType(
524+
'int',
525+
$query->getSingleResult(AbstractQuery::HYDRATE_SCALAR_COLUMN)
526+
);
527+
assertType(
528+
'int|null',
529+
$query->getOneOrNullResult(AbstractQuery::HYDRATE_SCALAR_COLUMN)
530+
);
185531
}
186532

187533
/**

0 commit comments

Comments
 (0)
Please sign in to comment.