Skip to content

Commit 47aa7ca

Browse files
arnaud-lbondrejmirtes
authored andcommitted
We can not assume the type of Query::getResult() if ResultType may be void
1 parent 1325277 commit 47aa7ca

File tree

3 files changed

+138
-1
lines changed

3 files changed

+138
-1
lines changed

Diff for: src/Type/Doctrine/Query/QueryResultDynamicReturnTypeExtension.php

+9-1
Original file line numberDiff line numberDiff line change
@@ -93,12 +93,20 @@ private function getMethodReturnTypeForHydrationMode(
9393
Type $queryResultType
9494
): Type
9595
{
96-
if ($queryResultType instanceof VoidType) {
96+
$isVoidType = (new VoidType())->isSuperTypeOf($queryResultType);
97+
98+
if ($isVoidType->yes()) {
9799
// A void query result type indicates an UPDATE or DELETE query.
98100
// In this case all methods return the number of affected rows.
99101
return new IntegerType();
100102
}
101103

104+
if ($isVoidType->maybe()) {
105+
// We can't be sure what the query type is, so we return the
106+
// declared return type of the method.
107+
return $this->originalReturnType($methodReflection);
108+
}
109+
102110
if (!$this->isObjectHydrationMode($hydrationMode)) {
103111
// We support only HYDRATE_OBJECT. For other hydration modes, we
104112
// return the declared return type of the method.

Diff for: tests/Type/Doctrine/data/QueryResult/queryBuilderGetQuery.php

+22
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,26 @@ public function testQueryResultTypeIsMixedWhenDQLIsInvalid(EntityManagerInterfac
4444
assertType('Doctrine\ORM\Query<mixed>', $query);
4545
}
4646

47+
public function testQueryResultTypeIsVoidWithDeleteOrUpdate(EntityManagerInterface $em): void
48+
{
49+
$query = $em->getRepository(Many::class)
50+
->createQueryBuilder('m')
51+
->where('m.id IN (:ids)')
52+
->setParameter('ids', $ids)
53+
->delete()
54+
->getQuery();
55+
56+
assertType('Doctrine\ORM\Query<void>', $query);
57+
58+
$query = $em->getRepository(Many::class)
59+
->createQueryBuilder('m')
60+
->where('m.id IN (:ids)')
61+
->setParameter('ids', $ids)
62+
->update()
63+
->set('m.intColumn', '42')
64+
->getQuery();
65+
66+
assertType('Doctrine\ORM\Query<void>', $query);
67+
68+
}
4769
}

Diff for: tests/Type/Doctrine/data/QueryResult/queryResult.php

+107
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use Doctrine\ORM\AbstractQuery;
66
use Doctrine\ORM\EntityManagerInterface;
7+
use Doctrine\ORM\Query;
78
use function PHPStan\Testing\assertType;
89

910
class QueryResultTest
@@ -210,4 +211,110 @@ public function testReturnTypeOfQueryMethodsWithExplicitNonConstantHydrationMode
210211
$query->getOneOrNullResult($hydrationMode)
211212
);
212213
}
214+
215+
/**
216+
* Test that we return the original return type when ResultType may be
217+
* VoidType
218+
*
219+
* @param Query<mixed> $query
220+
*/
221+
public function testReturnTypeOfQueryMethodsWithReturnTypeIsMixed(EntityManagerInterface $em, Query $query): void
222+
{
223+
assertType(
224+
'mixed',
225+
$query->getResult(AbstractQuery::HYDRATE_OBJECT)
226+
);
227+
assertType(
228+
'mixed',
229+
$query->execute(null, AbstractQuery::HYDRATE_OBJECT)
230+
);
231+
assertType(
232+
'mixed',
233+
$query->executeIgnoreQueryCache(null, AbstractQuery::HYDRATE_OBJECT)
234+
);
235+
assertType(
236+
'mixed',
237+
$query->executeUsingQueryCache(null, AbstractQuery::HYDRATE_OBJECT)
238+
);
239+
assertType(
240+
'mixed',
241+
$query->getSingleResult(AbstractQuery::HYDRATE_OBJECT)
242+
);
243+
assertType(
244+
'mixed',
245+
$query->getOneOrNullResult(AbstractQuery::HYDRATE_OBJECT)
246+
);
247+
}
248+
249+
/**
250+
* Test that we return the original return type when ResultType may be
251+
* VoidType (TemplateType variant)
252+
*
253+
* @template T
254+
*
255+
* @param Query<T> $query
256+
*/
257+
public function testReturnTypeOfQueryMethodsWithReturnTypeIsTemplateMixedType(EntityManagerInterface $em, Query $query): void
258+
{
259+
assertType(
260+
'mixed',
261+
$query->getResult(AbstractQuery::HYDRATE_OBJECT)
262+
);
263+
assertType(
264+
'mixed',
265+
$query->execute(null, AbstractQuery::HYDRATE_OBJECT)
266+
);
267+
assertType(
268+
'mixed',
269+
$query->executeIgnoreQueryCache(null, AbstractQuery::HYDRATE_OBJECT)
270+
);
271+
assertType(
272+
'mixed',
273+
$query->executeUsingQueryCache(null, AbstractQuery::HYDRATE_OBJECT)
274+
);
275+
assertType(
276+
'mixed',
277+
$query->getSingleResult(AbstractQuery::HYDRATE_OBJECT)
278+
);
279+
assertType(
280+
'mixed',
281+
$query->getOneOrNullResult(AbstractQuery::HYDRATE_OBJECT)
282+
);
283+
}
284+
285+
286+
/**
287+
* Test that we return ResultType return ResultType can not be VoidType
288+
*
289+
* @template T of array|object
290+
*
291+
* @param Query<T> $query
292+
*/
293+
public function testReturnTypeOfQueryMethodsWithReturnTypeIsNonVoidTemplate(EntityManagerInterface $em, Query $query): void
294+
{
295+
assertType(
296+
'array<T of array|object (method QueryResult\queryResult\QueryResultTest::testReturnTypeOfQueryMethodsWithReturnTypeIsNonVoidTemplate(), argument)>',
297+
$query->getResult(AbstractQuery::HYDRATE_OBJECT)
298+
);
299+
assertType(
300+
'array<T of array|object (method QueryResult\queryResult\QueryResultTest::testReturnTypeOfQueryMethodsWithReturnTypeIsNonVoidTemplate(), argument)>',
301+
$query->execute(null, AbstractQuery::HYDRATE_OBJECT)
302+
);
303+
assertType(
304+
'array<T of array|object (method QueryResult\queryResult\QueryResultTest::testReturnTypeOfQueryMethodsWithReturnTypeIsNonVoidTemplate(), argument)>',
305+
$query->executeIgnoreQueryCache(null, AbstractQuery::HYDRATE_OBJECT)
306+
);
307+
assertType(
308+
'array<T of array|object (method QueryResult\queryResult\QueryResultTest::testReturnTypeOfQueryMethodsWithReturnTypeIsNonVoidTemplate(), argument)>',
309+
$query->executeUsingQueryCache(null, AbstractQuery::HYDRATE_OBJECT)
310+
);
311+
assertType(
312+
'T of array|object (method QueryResult\queryResult\QueryResultTest::testReturnTypeOfQueryMethodsWithReturnTypeIsNonVoidTemplate(), argument)',
313+
$query->getSingleResult(AbstractQuery::HYDRATE_OBJECT)
314+
);
315+
assertType(
316+
'(T of array|object (method QueryResult\queryResult\QueryResultTest::testReturnTypeOfQueryMethodsWithReturnTypeIsNonVoidTemplate(), argument))|null',
317+
$query->getOneOrNullResult(AbstractQuery::HYDRATE_OBJECT)
318+
);
319+
}
213320
}

0 commit comments

Comments
 (0)