Skip to content

Commit 1b0a1b0

Browse files
Rely on default type when it cannot be precisely inferred
1 parent a268dbc commit 1b0a1b0

File tree

2 files changed

+70
-45
lines changed

2 files changed

+70
-45
lines changed

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

+44-19
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,10 @@ private function getMethodReturnTypeForHydrationMode(
154154
return $this->originalReturnType($methodReflection);
155155
}
156156

157+
if (null === $queryResultType) {
158+
return $this->originalReturnType($methodReflection);
159+
}
160+
157161
switch ($methodReflection->getName()) {
158162
case 'getSingleResult':
159163
return $queryResultType;
@@ -187,13 +191,21 @@ private function getMethodReturnTypeForHydrationMode(
187191
}
188192
}
189193

190-
private function getArrayHydratedReturnType(Type $queryResultType): Type
194+
/**
195+
* When we're array-hydrating object, we're not sure of the shape of the array.
196+
* We could return `new ArrayTyp(new MixedType(), new MixedType())`
197+
* but the lack of precision in the array keys/values would give false positive.
198+
*
199+
* @see https://github.com/phpstan/phpstan-doctrine/pull/412#issuecomment-1497092934
200+
*/
201+
private function getArrayHydratedReturnType(Type $queryResultType): ?Type
191202
{
192203
$objectManager = $this->objectMetadataResolver->getObjectManager();
193204

194-
return TypeTraverser::map(
205+
$mixedFound = false;
206+
$queryResultType = TypeTraverser::map(
195207
$queryResultType,
196-
static function (Type $type, callable $traverse) use ($objectManager): Type {
208+
static function (Type $type, callable $traverse) use ($objectManager, &$mixedFound): Type {
197209
$isObject = (new ObjectWithoutClassType())->isSuperTypeOf($type);
198210
if ($isObject->no()) {
199211
return $traverse($type);
@@ -203,25 +215,35 @@ static function (Type $type, callable $traverse) use ($objectManager): Type {
203215
|| !$type instanceof TypeWithClassName
204216
|| $objectManager === null
205217
) {
218+
$mixedFound = true;
219+
206220
return new MixedType();
207221
}
208222

209223
if (!$objectManager->getMetadataFactory()->hasMetadataFor($type->getClassName())) {
210224
return $traverse($type);
211225
}
212226

213-
// We could return `new ArrayTyp(new MixedType(), new MixedType())`
214-
// but the lack of precision in the array keys/values would give false positive
215-
// @see https://github.com/phpstan/phpstan-doctrine/pull/412#issuecomment-1497092934
227+
$mixedFound = true;
228+
216229
return new MixedType();
217230
}
218231
);
232+
233+
return $mixedFound ? null : $queryResultType;
219234
}
220235

221-
private function getScalarHydratedReturnType(Type $queryResultType): Type
236+
/**
237+
* When we're scalar-hydrating object, we're not sure of the shape of the array.
238+
* We could return `new ArrayTyp(new MixedType(), new MixedType())`
239+
* but the lack of precision in the array keys/values would give false positive.
240+
*
241+
* @see https://github.com/phpstan/phpstan-doctrine/pull/453#issuecomment-1895415544
242+
*/
243+
private function getScalarHydratedReturnType(Type $queryResultType): ?Type
222244
{
223245
if (!$queryResultType->isArray()->yes()) {
224-
return new ArrayType(new MixedType(), new MixedType());
246+
return null;
225247
}
226248

227249
foreach ($queryResultType->getArrays() as $arrayType) {
@@ -231,34 +253,37 @@ private function getScalarHydratedReturnType(Type $queryResultType): Type
231253
!(new ObjectWithoutClassType())->isSuperTypeOf($itemType)->no()
232254
|| !$itemType->isArray()->no()
233255
) {
234-
return new ArrayType(new MixedType(), new MixedType());
256+
// We could return `new ArrayTyp(new MixedType(), new MixedType())`
257+
// but the lack of precision in the array keys/values would give false positive
258+
// @see https://github.com/phpstan/phpstan-doctrine/pull/453#issuecomment-1895415544
259+
return null;
235260
}
236261
}
237262

238263
return $queryResultType;
239264
}
240265

241-
private function getSimpleObjectHydratedReturnType(Type $queryResultType): Type
266+
private function getSimpleObjectHydratedReturnType(Type $queryResultType): ?Type
242267
{
243268
if ((new ObjectWithoutClassType())->isSuperTypeOf($queryResultType)->yes()) {
244269
return $queryResultType;
245270
}
246271

247-
return new MixedType();
272+
return null;
248273
}
249274

250-
private function getSingleScalarHydratedReturnType(Type $queryResultType): Type
275+
private function getSingleScalarHydratedReturnType(Type $queryResultType): ?Type
251276
{
252277
$queryResultType = $this->getScalarHydratedReturnType($queryResultType);
253-
if (!$queryResultType->isConstantArray()->yes()) {
254-
return new MixedType();
278+
if (null === $queryResultType || !$queryResultType->isConstantArray()->yes()) {
279+
return null;
255280
}
256281

257282
$types = [];
258283
foreach ($queryResultType->getConstantArrays() as $constantArrayType) {
259284
$values = $constantArrayType->getValueTypes();
260285
if (count($values) !== 1) {
261-
return new MixedType();
286+
return null;
262287
}
263288

264289
$types[] = $constantArrayType->getFirstIterableValueType();
@@ -267,18 +292,18 @@ private function getSingleScalarHydratedReturnType(Type $queryResultType): Type
267292
return TypeCombinator::union(...$types);
268293
}
269294

270-
private function getScalarColumnHydratedReturnType(Type $queryResultType): Type
295+
private function getScalarColumnHydratedReturnType(Type $queryResultType): ?Type
271296
{
272297
$queryResultType = $this->getScalarHydratedReturnType($queryResultType);
273-
if (!$queryResultType->isConstantArray()->yes()) {
274-
return new MixedType();
298+
if (null === $queryResultType || !$queryResultType->isConstantArray()->yes()) {
299+
return null;
275300
}
276301

277302
$types = [];
278303
foreach ($queryResultType->getConstantArrays() as $constantArrayType) {
279304
$values = $constantArrayType->getValueTypes();
280305
if (count($values) !== 1) {
281-
return new MixedType();
306+
return null;
282307
}
283308

284309
$types[] = $constantArrayType->getFirstIterableValueType();

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

+26-26
Original file line numberDiff line numberDiff line change
@@ -156,27 +156,27 @@ public function testReturnTypeOfQueryMethodsWithExplicitArrayHydrationMode(Entit
156156
');
157157

158158
assertType(
159-
'list<mixed>',
159+
'mixed',
160160
$query->getResult(AbstractQuery::HYDRATE_ARRAY)
161161
);
162162
assertType(
163-
'list<mixed>',
163+
'array',
164164
$query->getArrayResult()
165165
);
166166
assertType(
167-
'iterable<int, mixed>',
167+
'iterable',
168168
$query->toIterable([], AbstractQuery::HYDRATE_ARRAY)
169169
);
170170
assertType(
171-
'list<mixed>',
171+
'mixed',
172172
$query->execute(null, AbstractQuery::HYDRATE_ARRAY)
173173
);
174174
assertType(
175-
'list<mixed>',
175+
'mixed',
176176
$query->executeIgnoreQueryCache(null, AbstractQuery::HYDRATE_ARRAY)
177177
);
178178
assertType(
179-
'list<mixed>',
179+
'mixed',
180180
$query->executeUsingQueryCache(null, AbstractQuery::HYDRATE_ARRAY)
181181
);
182182
assertType(
@@ -234,35 +234,35 @@ public function testReturnTypeOfQueryMethodsWithExplicitScalarHydrationMode(Enti
234234
');
235235

236236
assertType(
237-
'list<array>',
237+
'mixed',
238238
$query->getResult(AbstractQuery::HYDRATE_SCALAR)
239239
);
240240
assertType(
241-
'list<array>',
241+
'array',
242242
$query->getScalarResult()
243243
);
244244
assertType(
245-
'iterable<int, array>',
245+
'iterable',
246246
$query->toIterable([], AbstractQuery::HYDRATE_SCALAR)
247247
);
248248
assertType(
249-
'list<array>',
249+
'mixed',
250250
$query->execute(null, AbstractQuery::HYDRATE_SCALAR)
251251
);
252252
assertType(
253-
'list<array>',
253+
'mixed',
254254
$query->executeIgnoreQueryCache(null, AbstractQuery::HYDRATE_SCALAR)
255255
);
256256
assertType(
257-
'list<array>',
257+
'mixed',
258258
$query->executeUsingQueryCache(null, AbstractQuery::HYDRATE_SCALAR)
259259
);
260260
assertType(
261-
'array',
261+
'mixed',
262262
$query->getSingleResult(AbstractQuery::HYDRATE_SCALAR)
263263
);
264264
assertType(
265-
'array|null',
265+
'mixed',
266266
$query->getOneOrNullResult(AbstractQuery::HYDRATE_SCALAR)
267267
);
268268

@@ -316,11 +316,11 @@ public function testReturnTypeOfQueryMethodsWithExplicitSingleScalarHydrationMod
316316
$query->getResult(AbstractQuery::HYDRATE_SINGLE_SCALAR)
317317
);
318318
assertType(
319-
'mixed',
319+
'bool|float|int|string|null',
320320
$query->getSingleScalarResult()
321321
);
322322
assertType(
323-
'iterable<int, mixed>',
323+
'iterable',
324324
$query->toIterable([], AbstractQuery::HYDRATE_SINGLE_SCALAR)
325325
);
326326
assertType(
@@ -460,19 +460,19 @@ public function testReturnTypeOfQueryMethodsWithExplicitSimpleObjectHydrationMod
460460
');
461461

462462
assertType(
463-
'list<mixed>',
463+
'mixed',
464464
$query->getResult(AbstractQuery::HYDRATE_SIMPLEOBJECT)
465465
);
466466
assertType(
467-
'list<mixed>',
467+
'mixed',
468468
$query->execute(null, AbstractQuery::HYDRATE_SIMPLEOBJECT)
469469
);
470470
assertType(
471-
'list<mixed>',
471+
'mixed',
472472
$query->executeIgnoreQueryCache(null, AbstractQuery::HYDRATE_SIMPLEOBJECT)
473473
);
474474
assertType(
475-
'list<mixed>',
475+
'mixed',
476476
$query->executeUsingQueryCache(null, AbstractQuery::HYDRATE_SIMPLEOBJECT)
477477
);
478478
assertType(
@@ -498,27 +498,27 @@ public function testReturnTypeOfQueryMethodsWithExplicitScalarColumnHydrationMod
498498
');
499499

500500
assertType(
501-
'list<mixed>',
501+
'mixed',
502502
$query->getResult(AbstractQuery::HYDRATE_SCALAR_COLUMN)
503503
);
504504
assertType(
505-
'list<mixed>',
505+
'array',
506506
$query->getSingleColumnResult()
507507
);
508508
assertType(
509-
'iterable<int, mixed>',
509+
'iterable',
510510
$query->toIterable([], AbstractQuery::HYDRATE_SCALAR_COLUMN)
511511
);
512512
assertType(
513-
'list<mixed>',
513+
'mixed',
514514
$query->execute(null, AbstractQuery::HYDRATE_SCALAR_COLUMN)
515515
);
516516
assertType(
517-
'list<mixed>',
517+
'mixed',
518518
$query->executeIgnoreQueryCache(null, AbstractQuery::HYDRATE_SCALAR_COLUMN)
519519
);
520520
assertType(
521-
'list<mixed>',
521+
'mixed',
522522
$query->executeUsingQueryCache(null, AbstractQuery::HYDRATE_SCALAR_COLUMN)
523523
);
524524
assertType(

0 commit comments

Comments
 (0)