From 6dac52ebaa925ea05b3e178ed3bb4dbf87be29f2 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Tue, 26 Nov 2024 22:46:03 +0100 Subject: [PATCH] doctrine-dbal might return `list` (#720) --- src/DoctrineReflection/DoctrineReflection.php | 38 +++++++++++++++++-- tests/default/data/doctrine-dbal.php | 18 ++++----- 2 files changed, 44 insertions(+), 12 deletions(-) diff --git a/src/DoctrineReflection/DoctrineReflection.php b/src/DoctrineReflection/DoctrineReflection.php index 71109cd8..e30143ed 100644 --- a/src/DoctrineReflection/DoctrineReflection.php +++ b/src/DoctrineReflection/DoctrineReflection.php @@ -5,6 +5,7 @@ namespace staabm\PHPStanDba\DoctrineReflection; use PHPStan\Reflection\MethodReflection; +use PHPStan\Type\Accessory\AccessoryArrayListType; use PHPStan\Type\ArrayType; use PHPStan\Type\Constant\ConstantArrayTypeBuilder; use PHPStan\Type\Constant\ConstantBooleanType; @@ -43,6 +44,7 @@ public function reduceResultType(MethodReflection $methodReflection, Type $resul $usedMethod = strtolower($methodReflection->getName()); + $returnsList = false; switch ($usedMethod) { case 'fetchallkeyvalue': case 'iteratekeyvalue': @@ -52,16 +54,25 @@ public function reduceResultType(MethodReflection $methodReflection, Type $resul $fetchType = QueryReflector::FETCH_TYPE_ONE; break; case 'fetchfirstcolumn': + $returnsList = true; + $fetchType = QueryReflector::FETCH_TYPE_FIRST_COL; + break; case 'iteratecolumn': $fetchType = QueryReflector::FETCH_TYPE_FIRST_COL; break; case 'fetchnumeric': case 'fetchallnumeric': + $returnsList = true; + $fetchType = QueryReflector::FETCH_TYPE_NUMERIC; + break; case 'iteratenumeric': $fetchType = QueryReflector::FETCH_TYPE_NUMERIC; break; - case 'fetchassociative': case 'fetchallassociative': + $returnsList = true; + $fetchType = QueryReflector::FETCH_TYPE_ASSOC; + break; + case 'fetchassociative': case 'iterateassociative': $fetchType = QueryReflector::FETCH_TYPE_ASSOC; break; @@ -99,7 +110,14 @@ public function reduceResultType(MethodReflection $methodReflection, Type $resul return new GenericObjectType(Traversable::class, [new IntegerType(), $valueTypes[$i]]); } - return new ArrayType(IntegerRangeType::fromInterval(0, null), $valueTypes[$i]); + $arrayType = new ArrayType(IntegerRangeType::fromInterval(0, null), $valueTypes[$i]); + if ($returnsList) { + return TypeCombinator::intersect( + $arrayType, + new AccessoryArrayListType() + ); + } + return $arrayType; } if (QueryReflector::FETCH_TYPE_NUMERIC === $fetchType && $keyType->isInteger()->yes()) { @@ -116,7 +134,21 @@ public function reduceResultType(MethodReflection $methodReflection, Type $resul } if (\in_array($usedMethod, ['fetchallnumeric', 'fetchallassociative'], true)) { - return new ArrayType(IntegerRangeType::fromInterval(0, null), $resultType); + $arrayType = new ArrayType(IntegerRangeType::fromInterval(0, null), $resultType); + if ($returnsList) { + return TypeCombinator::intersect( + $arrayType, + new AccessoryArrayListType() + ); + } + return $arrayType; + } + + if ($returnsList) { + $resultType = TypeCombinator::intersect( + $resultType, + new AccessoryArrayListType() + ); } // false is returned if no rows are found. diff --git a/tests/default/data/doctrine-dbal.php b/tests/default/data/doctrine-dbal.php index f641a9db..4fc0f957 100644 --- a/tests/default/data/doctrine-dbal.php +++ b/tests/default/data/doctrine-dbal.php @@ -24,16 +24,16 @@ public function foo(Connection $conn) assertType('array{string, int<-32768, 32767>}|false', $fetch); $fetch = $result->fetchFirstColumn(); - assertType('array, string>', $fetch); + assertType('list', $fetch); $fetch = $result->fetchAssociative(); assertType('array{email: string, adaid: int<-32768, 32767>}|false', $fetch); $fetch = $result->fetchAllNumeric(); - assertType('array, array{string, int<-32768, 32767>}>', $fetch); + assertType('list}>', $fetch); $fetch = $result->fetchAllAssociative(); - assertType('array, array{email: string, adaid: int<-32768, 32767>}>', $fetch); + assertType('list}>', $fetch); $fetch = $result->fetchAllKeyValue(); assertType('array>', $fetch); @@ -155,33 +155,33 @@ public function fetchFirstColumn(Connection $conn) { $query = 'SELECT email, adaid FROM ada WHERE adaid = ?'; $fetchResult = $conn->fetchFirstColumn($query, [1]); - assertType('array, string>', $fetchResult); + assertType('list', $fetchResult); $query = 'SELECT email, adaid FROM ada'; $fetchResult = $conn->fetchFirstColumn($query); - assertType('array, string>', $fetchResult); + assertType('list', $fetchResult); } public function fetchAllNumeric(Connection $conn) { $query = 'SELECT email, adaid FROM ada WHERE adaid = ?'; $fetchResult = $conn->fetchAllNumeric($query, [1]); - assertType('array, array{string, int<-32768, 32767>}>', $fetchResult); + assertType('list}>', $fetchResult); $query = 'SELECT email, adaid FROM ada'; $fetchResult = $conn->fetchAllNumeric($query); - assertType('array, array{string, int<-32768, 32767>}>', $fetchResult); + assertType('list}>', $fetchResult); } public function fetchAllAssociative(Connection $conn) { $query = 'SELECT email, adaid FROM ada WHERE adaid = ?'; $fetchResult = $conn->fetchAllAssociative($query, [1]); - assertType('array, array{email: string, adaid: int<-32768, 32767>}>', $fetchResult); + assertType('list}>', $fetchResult); $query = 'SELECT email, adaid FROM ada'; $fetchResult = $conn->fetchAllAssociative($query); - assertType('array, array{email: string, adaid: int<-32768, 32767>}>', $fetchResult); + assertType('list}>', $fetchResult); } public function fetchAllKeyValue(Connection $conn)