From 14af3a155ea067979bbe9be9d857fe04a2e517bd Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sat, 31 Oct 2026 09:18:03 +0100 Subject: [PATCH] `mysqli_result->fetch_assoc()` can return `false` or `null` --- ...MysqliResultDynamicReturnTypeExtension.php | 11 +++++++- tests/default/data/mysqli.php | 6 ++-- .../config/.phpunit-phpstan-dba-mysqli.cache | 28 +++++++++---------- 3 files changed, 27 insertions(+), 18 deletions(-) diff --git a/src/Extensions/MysqliResultDynamicReturnTypeExtension.php b/src/Extensions/MysqliResultDynamicReturnTypeExtension.php index a1ed6bd8..028433b7 100644 --- a/src/Extensions/MysqliResultDynamicReturnTypeExtension.php +++ b/src/Extensions/MysqliResultDynamicReturnTypeExtension.php @@ -12,9 +12,11 @@ use PHPStan\Type\ArrayType; use PHPStan\Type\Constant\ConstantArrayType; use PHPStan\Type\Constant\ConstantArrayTypeBuilder; +use PHPStan\Type\Constant\ConstantBooleanType; use PHPStan\Type\Constant\ConstantIntegerType; use PHPStan\Type\DynamicMethodReturnTypeExtension; use PHPStan\Type\IntegerType; +use PHPStan\Type\NullType; use PHPStan\Type\Type; use PHPStan\Type\TypeCombinator; use PHPStan\Type\UnionType; @@ -38,7 +40,14 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method $methodName = strtolower($methodReflection->getName()); if ('fetch_assoc' === $methodName) { if (count($methodCall->getArgs()) === 0) { - return $this->getRowType($resultType); + $rowType = $this->getRowType($resultType); + if ($rowType !== null) { + return TypeCombinator::union( + $rowType, + new NullType(), + new ConstantBooleanType(false) + ); + } } return null; diff --git a/tests/default/data/mysqli.php b/tests/default/data/mysqli.php index 10d8889c..4a896ed1 100644 --- a/tests/default/data/mysqli.php +++ b/tests/default/data/mysqli.php @@ -61,7 +61,7 @@ public function unionResult(mysqli $mysqli) public function mysqliResultfetch(mysqli $mysqli) { $result = mysqli_query($mysqli, 'SELECT adaid FROM ada'); - assertType('array{adaid: int<-32768, 32767>}', $result->fetch_assoc()); + assertType('array{adaid: int<-32768, 32767>}|false|null', $result->fetch_assoc()); $result = mysqli_query($mysqli, 'SELECT adaid FROM ada'); assertType('list}>', $result->fetch_all()); @@ -77,7 +77,7 @@ public function fetchAllColumns(mysqli $mysqli) assertType('list}>', $result->fetch_all(MYSQLI_ASSOC)); assertType('list}>', $result->fetch_all(MYSQLI_NUM)); assertType('list, 1: int<-32768, 32767>}>', $result->fetch_all(MYSQLI_BOTH)); - assertType('array{email: string, adaid: int<-32768, 32767>}', $result->fetch_assoc()); + assertType('array{email: string, adaid: int<-32768, 32767>}|false|null', $result->fetch_assoc()); foreach ($result as $row) { assertType('array{email: string, adaid: int<-32768, 32767>}', $row); } @@ -93,7 +93,7 @@ public function fetchAllUnionResult(mysqli $mysqli) assertType('list}|array{email: string}>', $result->fetch_all(MYSQLI_ASSOC)); assertType('list}|array{string}>', $result->fetch_all(MYSQLI_NUM)); assertType('list, 0: int<-32768, 32767>}|array{email: string, 0: string}>', $result->fetch_all(MYSQLI_BOTH)); - assertType('array{adaid: int<-32768, 32767>}|array{email: string}', $result->fetch_assoc()); + assertType('array{adaid: int<-32768, 32767>}|array{email: string}|false|null', $result->fetch_assoc()); } } diff --git a/tests/sqlAst/config/.phpunit-phpstan-dba-mysqli.cache b/tests/sqlAst/config/.phpunit-phpstan-dba-mysqli.cache index 33b7e962..f0b25c33 100644 --- a/tests/sqlAst/config/.phpunit-phpstan-dba-mysqli.cache +++ b/tests/sqlAst/config/.phpunit-phpstan-dba-mysqli.cache @@ -164,7 +164,7 @@ array ( 5 => array ( - 'type-description' => 'array{str: string|null, 0: string|null}', + 'type-description' => 'array{str: string, 0: string}', ), ), ), @@ -204,7 +204,7 @@ array ( 5 => array ( - 'type-description' => 'array{str: string|null, 0: string|null}', + 'type-description' => 'array{str: string, 0: string}', ), ), ), @@ -744,7 +744,7 @@ array ( 5 => array ( - 'type-description' => 'array{col: string|null, 0: string|null}', + 'type-description' => 'array{col: string, 0: string}', ), ), ), @@ -754,7 +754,7 @@ array ( 5 => array ( - 'type-description' => 'array{col: string|null, 0: string|null}', + 'type-description' => 'array{col: string, 0: string}', ), ), ), @@ -794,7 +794,7 @@ array ( 5 => array ( - 'type-description' => 'array{col: string|null, 0: string|null}', + 'type-description' => 'array{col: string, 0: string}', ), ), ), @@ -804,7 +804,7 @@ array ( 5 => array ( - 'type-description' => 'array{col: string|null, 0: string|null}', + 'type-description' => 'array{col: string, 0: string}', ), ), ), @@ -1164,7 +1164,7 @@ array ( 5 => array ( - 'type-description' => 'array{field: string|null, 0: string|null}', + 'type-description' => 'array{field: string, 0: string}', ), ), ), @@ -1214,7 +1214,7 @@ array ( 5 => array ( - 'type-description' => 'array{field: string|null, 0: string|null}', + 'type-description' => 'array{field: string, 0: string}', ), ), ), @@ -1224,7 +1224,7 @@ array ( 5 => array ( - 'type-description' => 'array{field: string|null, 0: string|null}', + 'type-description' => 'array{field: string, 0: string}', ), ), ), @@ -1244,7 +1244,7 @@ array ( 5 => array ( - 'type-description' => 'array{field: string|null, 0: string|null}', + 'type-description' => 'array{field: string, 0: string}', ), ), ), @@ -1254,7 +1254,7 @@ array ( 5 => array ( - 'type-description' => 'array{col: string|null, 0: string|null}', + 'type-description' => 'array{col: string, 0: string}', ), ), ), @@ -1274,7 +1274,7 @@ array ( 5 => array ( - 'type-description' => 'array{field: string|null, 0: string|null}', + 'type-description' => 'array{field: string, 0: string}', ), ), ), @@ -1614,7 +1614,7 @@ array ( 5 => array ( - 'type-description' => 'array{field: string|null, 0: string|null}', + 'type-description' => 'array{field: string, 0: string}', ), ), ), @@ -1624,7 +1624,7 @@ array ( 5 => array ( - 'type-description' => 'array{field: string|null, 0: string|null}', + 'type-description' => 'array{field: string, 0: string}', ), ), ),