Skip to content

Commit 067038b

Browse files
Introduce default hydration mode
1 parent 31a924b commit 067038b

14 files changed

+90
-57
lines changed

src/Type/Doctrine/CreateQueryDynamicReturnTypeExtension.php

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use PHPStan\Php\PhpVersion;
1717
use PHPStan\Reflection\MethodReflection;
1818
use PHPStan\Type\Constant\ConstantStringType;
19+
use PHPStan\Type\ConstantTypeHelper;
1920
use PHPStan\Type\Doctrine\Query\QueryResultTypeBuilder;
2021
use PHPStan\Type\Doctrine\Query\QueryResultTypeWalker;
2122
use PHPStan\Type\Doctrine\Query\QueryType;
@@ -76,7 +77,7 @@ public function getTypeFromMethodCall(
7677
if (!isset($args[$queryStringArgIndex])) {
7778
return new GenericObjectType(
7879
Query::class,
79-
[new MixedType(), new MixedType()],
80+
[new MixedType(), new MixedType(), new MixedType()],
8081
);
8182
}
8283

@@ -105,11 +106,17 @@ public function getTypeFromMethodCall(
105106
return new QueryType($queryString, null, null);
106107
}
107108

108-
return new QueryType($queryString, $typeBuilder->getIndexType(), $typeBuilder->getResultType());
109+
return new QueryType(
110+
$queryString,
111+
$typeBuilder->getIndexType(),
112+
$typeBuilder->getResultType(),
113+
null,
114+
ConstantTypeHelper::getTypeFromValue($query->getHydrationMode()),
115+
);
109116
}
110117
return new GenericObjectType(
111118
Query::class,
112-
[new MixedType(), new MixedType()],
119+
[new MixedType(), new MixedType(), new MixedType()],
113120
);
114121
});
115122
}

src/Type/Doctrine/HydrationModeReturnTypeResolver.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
namespace PHPStan\Type\Doctrine;
44

55
use Doctrine\ORM\AbstractQuery;
6-
use Doctrine\Persistence\ObjectManager;
76
use PHPStan\Type\Accessory\AccessoryArrayListType;
87
use PHPStan\Type\ArrayType;
98
use PHPStan\Type\BenevolentUnionType;
@@ -25,7 +24,7 @@ public function getMethodReturnTypeForHydrationMode(
2524
Type $hydrationMode,
2625
Type $queryKeyType,
2726
Type $queryResultType,
28-
?ObjectManager $objectManager
27+
?Type $defaultHydrationModeType = null
2928
): ?Type
3029
{
3130
$isVoidType = (new VoidType())->isSuperTypeOf($queryResultType);
@@ -42,6 +41,10 @@ public function getMethodReturnTypeForHydrationMode(
4241
return null;
4342
}
4443

44+
if ($defaultHydrationModeType !== null && $hydrationMode->isNull()->yes()) {
45+
$hydrationMode = $defaultHydrationModeType;
46+
}
47+
4548
if (!$hydrationMode instanceof ConstantIntegerType) {
4649
return null;
4750
}

src/Type/Doctrine/Query/QueryResultDynamicReturnTypeExtension.php

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
use PHPStan\Reflection\ParametersAcceptorSelector;
1010
use PHPStan\ShouldNotHappenException;
1111
use PHPStan\Type\Doctrine\HydrationModeReturnTypeResolver;
12-
use PHPStan\Type\Doctrine\ObjectMetadataResolver;
1312
use PHPStan\Type\DynamicMethodReturnTypeExtension;
1413
use PHPStan\Type\NullType;
1514
use PHPStan\Type\Type;
@@ -27,16 +26,12 @@ final class QueryResultDynamicReturnTypeExtension implements DynamicMethodReturn
2726
'getSingleResult' => 0,
2827
];
2928

30-
private ObjectMetadataResolver $objectMetadataResolver;
31-
3229
private HydrationModeReturnTypeResolver $hydrationModeReturnTypeResolver;
3330

3431
public function __construct(
35-
ObjectMetadataResolver $objectMetadataResolver,
3632
HydrationModeReturnTypeResolver $hydrationModeReturnTypeResolver
3733
)
3834
{
39-
$this->objectMetadataResolver = $objectMetadataResolver;
4035
$this->hydrationModeReturnTypeResolver = $hydrationModeReturnTypeResolver;
4136
}
4237

@@ -84,7 +79,7 @@ public function getTypeFromMethodCall(
8479
$hydrationMode,
8580
$queryType->getTemplateType(AbstractQuery::class, 'TKey'),
8681
$queryType->getTemplateType(AbstractQuery::class, 'TResult'),
87-
$this->objectMetadataResolver->getObjectManager(),
82+
$queryType->getTemplateType(AbstractQuery::class, 'THydrationMode'),
8883
);
8984
}
9085

src/Type/Doctrine/Query/QueryType.php

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,26 @@ class QueryType extends GenericObjectType
1515

1616
private Type $resultType;
1717

18+
private Type $defaultHydrationModeType;
19+
1820
private string $dql;
1921

20-
public function __construct(string $dql, ?Type $indexType = null, ?Type $resultType = null, ?Type $subtractedType = null)
22+
public function __construct(
23+
string $dql,
24+
?Type $indexType = null,
25+
?Type $resultType = null,
26+
?Type $subtractedType = null,
27+
?Type $defaultHydrationModeType = null
28+
)
2129
{
2230
$this->indexType = $indexType ?? new MixedType();
2331
$this->resultType = $resultType ?? new MixedType();
32+
$this->defaultHydrationModeType = $defaultHydrationModeType ?? new MixedType();
2433

2534
parent::__construct('Doctrine\ORM\Query', [
2635
$this->indexType,
2736
$this->resultType,
37+
$this->defaultHydrationModeType,
2838
], $subtractedType);
2939

3040
$this->dql = $dql;
@@ -41,7 +51,7 @@ public function equals(Type $type): bool
4151

4252
public function changeSubtractedType(?Type $subtractedType): Type
4353
{
44-
return new self('Doctrine\ORM\Query', $this->indexType, $this->resultType, $subtractedType);
54+
return new self('Doctrine\ORM\Query', $this->indexType, $this->resultType, $subtractedType, $this->defaultHydrationModeType);
4555
}
4656

4757
public function isSuperTypeOf(Type $type): IsSuperTypeOfResult

src/Type/Doctrine/QueryBuilder/QueryBuilderGetQueryDynamicReturnTypeExtension.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use PHPStan\Php\PhpVersion;
1616
use PHPStan\Reflection\MethodReflection;
1717
use PHPStan\Rules\Doctrine\ORM\DynamicQueryBuilderArgumentException;
18+
use PHPStan\Type\ConstantTypeHelper;
1819
use PHPStan\Type\Doctrine\ArgumentsProcessor;
1920
use PHPStan\Type\Doctrine\DescriptorRegistry;
2021
use PHPStan\Type\Doctrine\DoctrineTypeUtils;
@@ -207,7 +208,13 @@ private function getQueryType(string $dql): Type
207208
return new QueryType($dql, null);
208209
}
209210

210-
return new QueryType($dql, $typeBuilder->getIndexType(), $typeBuilder->getResultType());
211+
return new QueryType(
212+
$dql,
213+
$typeBuilder->getIndexType(),
214+
$typeBuilder->getResultType(),
215+
null,
216+
ConstantTypeHelper::getTypeFromValue($query->getHydrationMode()),
217+
);
211218
}
212219

213220
}

stubs/ORM/AbstractQuery.stub

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use Doctrine\ORM\NoResultException;
99
/**
1010
* @template-covariant TKey The type of column used in indexBy
1111
* @template-covariant TResult The type of results returned by this query in HYDRATE_OBJECT mode
12+
* @template-covariant THydrationMode = AbstractQuery::HYDRATE_OBJECT The default hydration mode when none is provided
1213
*/
1314
abstract class AbstractQuery
1415
{
@@ -84,4 +85,14 @@ abstract class AbstractQuery
8485
{
8586
}
8687

88+
/**
89+
* @template TNewHydrationMode of string|AbstractQuery::HYDRATE_*
90+
* @param TNewHydrationMode $hydrationMode
91+
* @phpstan-self-out self<TKey, TResult, TNewHydrationMode>
92+
* @return self<TKey, TResult, TNewHydrationMode>
93+
*/
94+
public function setHydrationMode($hydrationMode): static
95+
{
96+
}
97+
8798
}

stubs/ORM/Query.stub

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ namespace Doctrine\ORM;
55
/**
66
* @template-covariant TKey The type of column used in indexBy
77
* @template-covariant TResult The type of results returned by this query in HYDRATE_OBJECT mode
8+
* @template-covariant THydrationMode = AbstractQuery::HYDRATE_OBJECT The default hydration mode when none is provided
89
*
9-
* @extends AbstractQuery<TKey, TResult>
10+
* @extends AbstractQuery<TKey, TResult, THydrationMode>
1011
*/
1112
final class Query extends AbstractQuery
1213
{

stubs/ORM/QueryBuilder.stub

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ class QueryBuilder
1717
}
1818

1919
/**
20-
* @return Query<mixed>
20+
* @return Query<null, mixed, 1>
2121
*/
2222
public function getQuery()
2323
{

tests/DoctrineIntegration/ORM/data/queryBuilder.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ public function usingMethodThatReturnStatic(): ?MyEntity
6767
]);
6868

6969
$result = $queryBuilder->getQuery()->getOneOrNullResult();
70-
assertType('mixed', $result);
70+
assertType('PHPStan\DoctrineIntegration\ORM\CustomRepositoryUsage\MyEntity|null', $result);
7171

7272
return $result;
7373
}

tests/Type/Doctrine/Query/QueryResultTypeWalkerHydrationModeTest.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,6 @@ public function test(Type $expectedType, string $dql, string $methodName, ?int $
9191
new ConstantIntegerType($this->getRealHydrationMode($methodName, $hydrationMode)),
9292
$typeBuilder->getIndexType(),
9393
$typeBuilder->getResultType(),
94-
$entityManager,
9594
) ?? new MixedType();
9695

9796
self::assertSame(

0 commit comments

Comments
 (0)