Skip to content

Commit 1240f80

Browse files
staabmclxmstaab
andauthored
doctrine-dbal: union query fetch types (#296)
Co-authored-by: Markus Staab <[email protected]>
1 parent 83a5b51 commit 1240f80

File tree

6 files changed

+54
-27
lines changed

6 files changed

+54
-27
lines changed

src/DoctrineReflection/DoctrineReflection.php

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,42 @@
1818
use PHPStan\Type\IntegerType;
1919
use PHPStan\Type\Type;
2020
use PHPStan\Type\TypeCombinator;
21+
use PHPStan\Type\UnionType;
2122
use staabm\PHPStanDba\QueryReflection\QueryReflection;
2223
use staabm\PHPStanDba\QueryReflection\QueryReflector;
2324
use Traversable;
2425

2526
final class DoctrineReflection
2627
{
27-
public function reduceResultType(MethodReflection $methodReflection, Type $resultRowType): ?Type
28+
public function reduceResultType(MethodReflection $methodReflection, Type $resultType): ?Type
2829
{
30+
if ($resultType instanceof UnionType) {
31+
$resultTypes = [];
32+
33+
foreach ($resultType->getTypes() as $type) {
34+
$rowType = $this->reduceResultType($methodReflection, $type);
35+
if (null === $rowType) {
36+
return null;
37+
}
38+
$resultTypes[] = $rowType;
39+
}
40+
41+
return TypeCombinator::union(...$resultTypes);
42+
}
43+
44+
if ($resultType instanceof GenericObjectType) {
45+
$genericTypes = $resultType->getTypes();
46+
47+
if (1 !== \count($genericTypes)) {
48+
return null;
49+
}
50+
51+
$resultRowType = $genericTypes[0];
52+
53+
return $this->reduceResultType($methodReflection, $resultRowType);
54+
}
55+
56+
$resultRowType = $resultType;
2957
$usedMethod = strtolower($methodReflection->getName());
3058

3159
switch ($usedMethod) {

src/Extensions/DoctrineConnectionFetchDynamicReturnTypeExtension.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,12 +85,12 @@ private function inferType(MethodReflection $methodReflection, Expr $queryExpr,
8585
$doctrineReflection = new DoctrineReflection();
8686

8787
if (null === $paramsExpr) {
88-
$queryString = $queryReflection->resolveQueryStrings($queryExpr, $scope);
88+
$queryStrings = $queryReflection->resolveQueryStrings($queryExpr, $scope);
8989
} else {
9090
$parameterTypes = $scope->getType($paramsExpr);
91-
$queryString = $queryReflection->resolvePreparedQueryStrings($queryExpr, $parameterTypes, $scope);
91+
$queryStrings = $queryReflection->resolvePreparedQueryStrings($queryExpr, $parameterTypes, $scope);
9292
}
9393

94-
return $doctrineReflection->createFetchType($queryString, $methodReflection);
94+
return $doctrineReflection->createFetchType($queryStrings, $methodReflection);
9595
}
9696
}

src/Extensions/DoctrineResultDynamicReturnTypeExtension.php

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -56,33 +56,30 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method
5656
}
5757

5858
$resultType = $scope->getType($methodCall->var);
59-
if (!$resultType instanceof GenericObjectType) {
60-
return $defaultReturn;
61-
}
62-
63-
$genericTypes = $resultType->getTypes();
64-
65-
if (1 !== \count($genericTypes)) {
66-
return $defaultReturn;
67-
}
68-
69-
$resultRowType = $genericTypes[0];
70-
7159
if ('columncount' === strtolower($methodReflection->getName())) {
72-
if ($resultRowType instanceof ConstantArrayType) {
73-
$columnCount = \count($resultRowType->getKeyTypes()) / 2;
74-
if (!\is_int($columnCount)) {
75-
throw new ShouldNotHappenException();
60+
if ($resultType instanceof GenericObjectType) {
61+
$genericTypes = $resultType->getTypes();
62+
if (1 !== \count($genericTypes)) {
63+
return $defaultReturn;
7664
}
65+
$resultRowType = $genericTypes[0];
66+
67+
if ($resultRowType instanceof ConstantArrayType) {
68+
$columnCount = \count($resultRowType->getKeyTypes()) / 2;
69+
if (!\is_int($columnCount)) {
70+
throw new ShouldNotHappenException();
71+
}
7772

78-
return new ConstantIntegerType($columnCount);
73+
return new ConstantIntegerType($columnCount);
74+
}
7975
}
8076

8177
return $defaultReturn;
8278
}
8379

8480
$doctrineReflection = new DoctrineReflection();
85-
$fetchResultType = $doctrineReflection->reduceResultType($methodReflection, $resultRowType);
81+
$fetchResultType = $doctrineReflection->reduceResultType($methodReflection, $resultType);
82+
8683
if (null !== $fetchResultType) {
8784
return $fetchResultType;
8885
}

tests/default/data/doctrine-dbal-union-result.php

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@ public function doFoo(Connection $conn)
2121
$result = $stmt->execute([]);
2222
assertType('Doctrine\DBAL\Result<array{adaid: int<0, 4294967295>, 0: int<0, 4294967295>}>|Doctrine\DBAL\Result<array{email: string, 0: string}>', $result);
2323

24-
// XXX todo
25-
// $fetch = $result->fetchOne();
26-
// assertType('Doctrine\DBAL\Result<array{adaid: int<0, 4294967295>, 0: int<0, 4294967295>}>', $fetch);
24+
$fetch = $result->fetchOne();
25+
assertType('int<0, 4294967295>|string|false', $fetch);
26+
27+
$fetch = $result->fetchAssociative();
28+
assertType('array{adaid: int<0, 4294967295>}|array{email: string}|false', $fetch);
2729
}
2830
}
2931

tests/rules/config/.phpstan-dba-mysqli.cache

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/rules/config/.phpstan-dba-pdo.cache

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)