Skip to content

Commit 62e90f4

Browse files
staabmclxmstaab
andauthored
support PDO::FETCH_COLUMN in fetchAll() (#301)
Co-authored-by: Markus Staab <[email protected]>
1 parent 1240f80 commit 62e90f4

File tree

6 files changed

+52
-2
lines changed

6 files changed

+52
-2
lines changed

src/Extensions/PdoQueryDynamicReturnTypeExtension.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use PHPStan\Type\TypeCombinator;
1919
use staabm\PHPStanDba\PdoReflection\PdoStatementReflection;
2020
use staabm\PHPStanDba\QueryReflection\QueryReflection;
21+
use staabm\PHPStanDba\QueryReflection\QueryReflector;
2122

2223
final class PdoQueryDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
2324
{
@@ -83,6 +84,10 @@ private function inferType(MethodCall $methodCall, Expr $queryExpr, Scope $scope
8384
if (null === $reflectionFetchType) {
8485
return null;
8586
}
87+
// not yet implemented on query()
88+
if (QueryReflector::FETCH_TYPE_COLUMN === $reflectionFetchType) {
89+
return null;
90+
}
8691
}
8792

8893
$queryReflection = new QueryReflection();

src/Extensions/PdoStatementFetchDynamicReturnTypeExtension.php

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,15 @@
1212
use PHPStan\Reflection\ParametersAcceptorSelector;
1313
use PHPStan\Type\ArrayType;
1414
use PHPStan\Type\Constant\ConstantBooleanType;
15+
use PHPStan\Type\Constant\ConstantIntegerType;
1516
use PHPStan\Type\DynamicMethodReturnTypeExtension;
1617
use PHPStan\Type\IntegerType;
1718
use PHPStan\Type\MixedType;
1819
use PHPStan\Type\Type;
1920
use PHPStan\Type\TypeCombinator;
2021
use staabm\PHPStanDba\PdoReflection\PdoStatementReflection;
2122
use staabm\PHPStanDba\QueryReflection\QueryReflection;
23+
use staabm\PHPStanDba\QueryReflection\QueryReflector;
2224

2325
final class PdoStatementFetchDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
2426
{
@@ -76,7 +78,23 @@ private function inferType(MethodReflection $methodReflection, MethodCall $metho
7678
}
7779
}
7880

79-
$rowType = $pdoStatementReflection->getRowType($statementType, $fetchType);
81+
if ('fetchAll' === $methodReflection->getName() && QueryReflector::FETCH_TYPE_COLUMN === $fetchType) {
82+
$columnIndex = 0;
83+
84+
if (\count($args) > 1) {
85+
$columnIndexType = $scope->getType($args[1]->value);
86+
if ($columnIndexType instanceof ConstantIntegerType) {
87+
$columnIndex = $columnIndexType->getValue();
88+
} else {
89+
return null;
90+
}
91+
}
92+
93+
$rowType = $pdoStatementReflection->getColumnRowType($statementType, $columnIndex);
94+
} else {
95+
$rowType = $pdoStatementReflection->getRowType($statementType, $fetchType);
96+
}
97+
8098
if (null === $rowType) {
8199
return null;
82100
}

src/PdoReflection/PdoStatementReflection.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ public function getFetchType(Type $fetchModeType): ?int
6464
return QueryReflector::FETCH_TYPE_NUMERIC;
6565
} elseif (PDO::FETCH_BOTH === $fetchModeType->getValue()) {
6666
return QueryReflector::FETCH_TYPE_BOTH;
67+
} elseif (PDO::FETCH_COLUMN === $fetchModeType->getValue()) {
68+
return QueryReflector::FETCH_TYPE_COLUMN;
6769
}
6870

6971
return null;
@@ -132,6 +134,20 @@ public function getRowType(Type $statementType, int $fetchType): ?Type
132134
return null;
133135
}
134136

137+
public function getColumnRowType(Type $statementType, int $columnIndex): ?Type
138+
{
139+
$statementType = $this->getRowType($statementType, QueryReflector::FETCH_TYPE_NUMERIC);
140+
141+
if ($statementType instanceof ConstantArrayType) {
142+
$valueTypes = $statementType->getValueTypes();
143+
if (\array_key_exists($columnIndex, $valueTypes)) {
144+
return $valueTypes[$columnIndex];
145+
}
146+
}
147+
148+
return null;
149+
}
150+
135151
/**
136152
* @param QueryReflector::FETCH_TYPE* $fetchType
137153
*/

src/QueryReflection/QueryReflector.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ interface QueryReflector
1414
public const FETCH_TYPE_BOTH = 5;
1515
public const FETCH_TYPE_KEY_VALUE = 6;
1616

17+
public const FETCH_TYPE_COLUMN = 50;
18+
1719
public function validateQueryString(string $queryString): ?Error;
1820

1921
/**

tests/default/data/pdo-stmt-fetch.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,15 @@ public function fetchAll(PDO $pdo)
2828
$all = $stmt->fetchAll(PDO::FETCH_ASSOC);
2929
assertType('array<int, array{email: string, adaid: int<0, 4294967295>}>', $all);
3030

31+
$all = $stmt->fetchAll(PDO::FETCH_COLUMN);
32+
assertType('array<int, string>', $all);
33+
34+
$all = $stmt->fetchAll(PDO::FETCH_COLUMN, 0);
35+
assertType('array<int, string>', $all);
36+
37+
$all = $stmt->fetchAll(PDO::FETCH_COLUMN, 1);
38+
assertType('array<int, int<0, 4294967295>>', $all);
39+
3140
// not yet supported fetch types
3241
$all = $stmt->fetchAll(PDO::FETCH_OBJ);
3342
assertType('array', $all); // XXX since php8 this cannot return false

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)