Skip to content

Commit 9a3c964

Browse files
xPawstaabmclxmstaab
authored
Add RuntimeConfiguration->defaultFetchMode() option (#256)
Co-authored-by: Markus Staab <[email protected]> Co-authored-by: Markus Staab <[email protected]>
1 parent 16fc4c8 commit 9a3c964

18 files changed

+638
-16
lines changed

.github/workflows/phpstan.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,7 @@ jobs:
5252

5353
- name: Setup mysql
5454
run: |
55-
mysql -uroot -h127.0.0.1 -proot -e 'create database phpstan_dba;'
56-
mysql -uroot -h127.0.0.1 -proot phpstan_dba < tests/schema.sql
55+
mysql -uroot -h127.0.0.1 -proot < tests/schema.sql
5756
5857
- run: composer phpstan
5958

.github/workflows/tests.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,7 @@ jobs:
5454

5555
- name: Setup mysql
5656
run: |
57-
mysql -uroot -h127.0.0.1 -proot -e 'create database phpstan_dba;'
58-
mysql -uroot -h127.0.0.1 -proot phpstan_dba < tests/schema.sql
57+
mysql -uroot -h127.0.0.1 -proot < tests/schema.sql
5958
6059
- run: composer phpunit
6160

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ If not configured otherwise, the following defaults are used:
9696
- type-inference works as precise as possible. In case your database access layer returns strings instead of integers and floats, use the [`stringifyTypes`](https://github.com/staabm/phpstan-dba/tree/main/src/QueryReflection/RuntimeConfiguration.php) option.
9797
- when analyzing a php8+ codebase, [`PDO::ERRMODE_EXCEPTION` error handling](https://www.php.net/manual/en/pdo.error-handling.php) is assumed.
9898
- when analyzing a php8.1+ codebase, [`mysqli_report(\MYSQLI_REPORT_ERROR | \MYSQLI_REPORT_STRICT);` error handling](https://www.php.net/mysqli_report) is assumed.
99+
- the fetch mode defaults to `QueryReflector::FETCH_TYPE_BOTH`, but can be configured using the [`defaultFetchMode`](https://github.com/staabm/phpstan-dba/tree/main/src/QueryReflection/RuntimeConfiguration.php) option.
99100

100101
### Record and Replay
101102

composer.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,13 @@
4545
"phpstan": [
4646
"phpstan analyse -c phpstan.neon.dist",
4747
"phpstan analyse -c tests/default/config/phpstan.neon.dist",
48-
"phpstan analyse -c tests/stringify/config/phpstan.neon.dist"
48+
"phpstan analyse -c tests/stringify/config/phpstan.neon.dist",
49+
"phpstan analyse -c tests/defaultFetchAssoc/config/phpstan.neon.dist"
4950
],
5051
"phpunit": [
5152
"phpunit -c tests/default/config/phpunit.xml",
52-
"phpunit -c tests/stringify/config/phpunit.xml"
53+
"phpunit -c tests/stringify/config/phpunit.xml",
54+
"phpunit -c tests/defaultFetchAssoc/config/phpunit.xml"
5355
]
5456
},
5557
"config": {

src/Extensions/PdoPrepareDynamicReturnTypeExtension.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
use PHPStan\Type\Type;
2020
use PHPStan\Type\TypeCombinator;
2121
use staabm\PHPStanDba\QueryReflection\QueryReflection;
22-
use staabm\PHPStanDba\QueryReflection\QueryReflector;
2322

2423
final class PdoPrepareDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
2524
{
@@ -76,7 +75,7 @@ private function inferType(Expr $queryExpr, Scope $scope): ?Type
7675
return null;
7776
}
7877

79-
$reflectionFetchType = QueryReflector::FETCH_TYPE_BOTH;
78+
$reflectionFetchType = QueryReflection::getRuntimeConfiguration()->getDefaultFetchMode();
8079
$resultType = $queryReflection->getResultType($queryString, $reflectionFetchType);
8180
if ($resultType) {
8281
return new GenericObjectType(PDOStatement::class, [$resultType]);

src/Extensions/PdoQueryDynamicReturnTypeExtension.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
use PHPStan\Type\TypeCombinator;
2121
use staabm\PHPStanDba\PdoReflection\PdoStatementReflection;
2222
use staabm\PHPStanDba\QueryReflection\QueryReflection;
23-
use staabm\PHPStanDba\QueryReflection\QueryReflector;
2423

2524
final class PdoQueryDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
2625
{
@@ -78,7 +77,7 @@ private function inferType(MethodCall $methodCall, Expr $queryExpr, Scope $scope
7877
$args = $methodCall->getArgs();
7978
$pdoStatementReflection = new PdoStatementReflection();
8079

81-
$reflectionFetchType = QueryReflector::FETCH_TYPE_BOTH;
80+
$reflectionFetchType = QueryReflection::getRuntimeConfiguration()->getDefaultFetchMode();
8281
if (\count($args) >= 2) {
8382
$fetchModeType = $scope->getType($args[1]->value);
8483

src/Extensions/PdoStatementExecuteTypeSpecifyingExtension.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
use PHPStan\Type\Type;
1818
use staabm\PHPStanDba\PdoReflection\PdoStatementReflection;
1919
use staabm\PHPStanDba\QueryReflection\QueryReflection;
20-
use staabm\PHPStanDba\QueryReflection\QueryReflector;
2120

2221
final class PdoStatementExecuteTypeSpecifyingExtension implements MethodTypeSpecifyingExtension, TypeSpecifierAwareExtension
2322
{
@@ -73,7 +72,7 @@ private function inferStatementType(MethodReflection $methodReflection, MethodCa
7372
return null;
7473
}
7574

76-
$reflectionFetchType = QueryReflector::FETCH_TYPE_BOTH;
75+
$reflectionFetchType = QueryReflection::getRuntimeConfiguration()->getDefaultFetchMode();
7776
$resultType = $queryReflection->getResultType($queryString, $reflectionFetchType);
7877

7978
if ($resultType) {

src/Extensions/PdoStatementFetchDynamicReturnTypeExtension.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
use PHPStan\Type\UnionType;
2121
use staabm\PHPStanDba\PdoReflection\PdoStatementReflection;
2222
use staabm\PHPStanDba\QueryReflection\QueryReflection;
23-
use staabm\PHPStanDba\QueryReflection\QueryReflector;
2423

2524
final class PdoStatementFetchDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
2625
{
@@ -67,7 +66,7 @@ private function inferType(MethodReflection $methodReflection, MethodCall $metho
6766

6867
$statementType = $scope->getType($methodCall->var);
6968

70-
$fetchType = QueryReflector::FETCH_TYPE_BOTH;
69+
$fetchType = QueryReflection::getRuntimeConfiguration()->getDefaultFetchMode();
7170
if (\count($args) > 0) {
7271
$fetchModeType = $scope->getType($args[0]->value);
7372
$fetchType = $pdoStatementReflection->getFetchType($fetchModeType);

src/PdoReflection/PdoStatementReflection.php

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use PHPStan\Type\Generic\GenericObjectType;
1515
use PHPStan\Type\Type;
1616
use staabm\PHPStanDba\QueryReflection\ExpressionFinder;
17+
use staabm\PHPStanDba\QueryReflection\QueryReflection;
1718
use staabm\PHPStanDba\QueryReflection\QueryReflector;
1819

1920
final class PdoStatementReflection
@@ -68,7 +69,27 @@ public function getStatementResultType(Type $statementType, int $fetchType): ?Ty
6869
}
6970

7071
$resultType = $genericTypes[0];
71-
if ((QueryReflector::FETCH_TYPE_NUMERIC === $fetchType || QueryReflector::FETCH_TYPE_ASSOC === $fetchType) &&
72+
73+
// turn ASSOC typed statement into a NUMERIC one
74+
$defaultFetchType = QueryReflection::getRuntimeConfiguration()->getDefaultFetchMode();
75+
if (QueryReflector::FETCH_TYPE_ASSOC === $defaultFetchType && QueryReflector::FETCH_TYPE_NUMERIC === $fetchType &&
76+
$resultType instanceof ConstantArrayType && \count($resultType->getValueTypes()) > 0) {
77+
$builder = ConstantArrayTypeBuilder::createEmpty();
78+
79+
$valueTypes = $resultType->getValueTypes();
80+
81+
$i = 0;
82+
foreach ($valueTypes as $valueType) {
83+
$builder->setOffsetValueType(new ConstantIntegerType($i), $valueType);
84+
++$i;
85+
}
86+
87+
return $builder->getArray();
88+
}
89+
90+
// turn a BOTH typed statement into either NUMERIC or ASSOC
91+
if (QueryReflector::FETCH_TYPE_BOTH === $defaultFetchType &&
92+
(QueryReflector::FETCH_TYPE_NUMERIC === $fetchType || QueryReflector::FETCH_TYPE_ASSOC === $fetchType) &&
7293
$resultType instanceof ConstantArrayType && \count($resultType->getValueTypes()) > 0) {
7394
$builder = ConstantArrayTypeBuilder::createEmpty();
7495

src/QueryReflection/RuntimeConfiguration.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ final class RuntimeConfiguration
2727
* @var self::ERROR_MODE*
2828
*/
2929
private $errorMode = self::ERROR_MODE_DEFAULT;
30+
/**
31+
* @var QueryReflector::FETCH_TYPE*
32+
*/
33+
private $defaultFetchMode = QueryReflector::FETCH_TYPE_BOTH;
3034
/**
3135
* @var bool
3236
*/
@@ -53,6 +57,19 @@ public function errorMode(string $mode): self
5357
return $this;
5458
}
5559

60+
/**
61+
* Defines the PDO default fetch mode.
62+
* This might be necessary in case you are using `\PDO::ATTR_DEFAULT_FETCH_MODE`.
63+
*
64+
* @param QueryReflector::FETCH_TYPE_BOTH|QueryReflector::FETCH_TYPE_ASSOC $mode
65+
*/
66+
public function defaultFetchMode(int $mode): self
67+
{
68+
$this->defaultFetchMode = $mode;
69+
70+
return $this;
71+
}
72+
5673
public function debugMode(bool $mode): self
5774
{
5875
$this->debugMode = $mode;
@@ -81,6 +98,14 @@ public function isStringifyTypes(): bool
8198
return $this->stringifyTypes;
8299
}
83100

101+
/**
102+
* @return QueryReflector::FETCH_TYPE*
103+
*/
104+
public function getDefaultFetchMode(): int
105+
{
106+
return $this->defaultFetchMode;
107+
}
108+
84109
public function throwsPdoExceptions(PhpVersion $phpVersion): bool
85110
{
86111
if (self::ERROR_MODE_EXCEPTION === $this->errorMode) {

0 commit comments

Comments
 (0)