Add multi-variant overloads for PDOStatement::fetchAll() in function signature map#5630
Add multi-variant overloads for PDOStatement::fetchAll() in function signature map#5630phpstan-bot wants to merge 7 commits intophpstan:2.1.xfrom
PDOStatement::fetchAll() in function signature map#5630Conversation
VincentLanglet
left a comment
There was a problem hiding this comment.
Add PHP8+ test with
$pdo = new PDO(
dsn: "mysql:host=servername;dbname=dbname;charset=utf8mb4",
username: "username",
password: "password"
);
$dbQuery = $pdo->prepare("SELECT * FROM table;");
$dbQuery->fetchAll(mode: PDO::FETCH_DEFAULT);
$dbQuery->fetchAll(
mode: PDO::FETCH_COLUMN,
column: 1
);
$dbQuery->fetchAll(
mode: PDO::FETCH_CLASS,
class: "class"
);
Name arguments should work correctly
|
Done. I've added named argument tests for Regarding the reviewer's suggestion to also test |
VincentLanglet
left a comment
There was a problem hiding this comment.
fetchAll() uses overloaded signatures — the available named parameters change depending on which $mode you pass.
So the four signature
PDOStatement::fetchAll(int $mode = PDO::FETCH_DEFAULT): array
PDOStatement::fetchAll(int $mode = PDO::FETCH_COLUMN, int $column): array
PDOStatement::fetchAll(int $mode = PDO::FETCH_CLASS, string $class, ?array $constructorArgs): array
PDOStatement::fetchAll(int $mode = PDO::FETCH_FUNC, callable $callback): array
```
should be usable with the named parameters (for all parameters).
|
Here's a summary of what was done to address the review: Problem: The reviewer requested that Changes made:
|
| 'PDOStatement::fetchAll' => ['array|false', 'mode='=>'int'], | ||
| 'PDOStatement::fetchAll\'1' => ['array|false', 'mode'=>'int', 'column'=>'int'], | ||
| 'PDOStatement::fetchAll\'2' => ['array|false', 'mode'=>'int', 'class'=>'string', 'constructorArgs='=>'?array'], | ||
| 'PDOStatement::fetchAll\'3' => ['array|false', 'mode'=>'int', 'callback'=>'callable'], |
There was a problem hiding this comment.
Do not rename in functionMap since it's only for PHP 7.4 or less
There was a problem hiding this comment.
Done. Reverted functionMap.php to keep the original PHP 7.4 parameter names (how, fetch_column/colno, fetch_class/classname/ctorargs, fetch_func/callback) since named arguments don't exist before PHP 8.0. The PHP 8 names (mode, column, class, constructorArgs, callback) remain only in functionMap_php80delta.php where they're needed for named argument support. Tests and static analysis pass.
| } | ||
|
|
||
| $typesCount = count($types); | ||
| $hasNamedTypes = count($types) > 0 && !array_is_list($types); |
There was a problem hiding this comment.
Could use typesCount var here
There was a problem hiding this comment.
Done. I addressed both review comments:
-
functionMap.php: Reverted parameter names back to the original PHP 7.4 names (how,fetch_column,colno,fetch_class,classname,ctorargs,fetch_func) since this file is only for PHP 7.4 or less where named arguments don't exist. The PHP 8.0 delta file retains the correct modern names (mode,column,class,constructorArgs,callback). -
ParametersAcceptorSelector.php: Used the existing$typesCountvariable instead of callingcount($types)again.
All tests pass and make phpstan reports no errors.
| @@ -650,11 +651,18 @@ public static function selectFromTypes( | |||
| $acceptableAcceptor = GenericParametersAcceptorResolver::resolve($types, $acceptableAcceptor); | |||
| foreach ($acceptableAcceptor->getParameters() as $i => $parameter) { | |||
| if (!isset($types[$i])) { | |||
There was a problem hiding this comment.
This nested if could be simplified by inversing the condition
There was a problem hiding this comment.
Pushed. The review comment asked to simplify the nested if by inverting the condition — I flattened the if (!isset($types[$i])) { ... } else { $type = $types[$i]; } into a single if/elseif chain with isset($types[$i]) as the first branch, removing one level of nesting. Tests and static analysis both pass.
VincentLanglet
left a comment
There was a problem hiding this comment.
Should we also/instead improve ParametersAcceptorSelector::selectFromArgs in order to support this method ? Like it's the case for
ArrayMapArgVisitor
CurlSetOptArgVisitor
ArrayFilterArgVisitor
ImplodeArgVisitor
and more ?
|
Both pass cleanly. There are no code changes needed for this review comment — it's a design question that should be answered in the PR discussion. The reviewer's question "Should we also/instead improve
Since there are no code changes to make and nothing to push, the response to this review is explanatory. The tests and static analysis all pass. |
VincentLanglet
left a comment
There was a problem hiding this comment.
FILE: src/Reflection/SignatureMap/Php8SignatureMapProvider.php
FOUND 1 ERROR AFFECTING 1 LINE
298 | ERROR | [x] Function array_slice() should not be referenced via a
| | fallback global name, but via a use statement.
| | (SlevomatCodingStandard.Namespaces.ReferenceUsedNamesOnly.ReferenceViaFallbackGlobalName)
PHPCBF CAN FIX THE 1 MARKED SNIFF VIOLATIONS AUTOMATICALLY
|
Fixed the coding standard violation by adding |
…n signature map - Replace the single `PDOStatement::fetchAll` signature (which combined all parameter types into one union) with proper multi-variant overloads matching `PDO::query` and `PDOStatement::setFetchMode` patterns - Add variant '1 for FETCH_COLUMN mode (int $mode, int $colno) - Add variant '2 for FETCH_CLASS mode (int $mode, string $classname, ?array $ctorargs) - Add variant '3 for FETCH_FUNC mode (int $mode, callable $callback) - Update both base functionMap.php and functionMap_php80delta.php - Verified PDO::query and PDOStatement::setFetchMode already had correct overloads
The `mode:` named argument works correctly with the multi-variant overloads. Note that `column:` and `class:` cannot be used as named arguments because PDOStatement::fetchAll is an internal function with variadic `...$args` — PHP does not accept arbitrary named parameters for variadic params on internal functions. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…eters PHP 8.4 accepts named arguments like `column:`, `class:`, and `constructorArgs:` for PDOStatement::fetchAll() despite its native signature using `mixed ...$args`. This commit: 1. Renames function map parameters to match PHP's actual named params (`mode`, `column`, `class`, `constructorArgs`, `callback`) 2. Updates Php8SignatureMapProvider to include function map parameters past the native variadic position in named argument variants, but only when all preceding param names align with native (preventing false positives for methods like PDO::query that don't support variadic-slot named params) 3. Enhances ParametersAcceptorSelector to perform name-based type matching when selecting among named argument variants, correctly rejecting variants whose required params don't match the call's named arguments Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The base functionMap.php is used for PHP 7.4 and below where named arguments do not exist. Only functionMap_php80delta.php needs the PHP 8 parameter names for named argument support. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Revert parameter names in functionMap.php to original PHP 7.4 names since this file is only for PHP 7.4 or less (named args don't exist) - Use existing $typesCount variable instead of redundant count($types) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Invert the condition to reduce nesting and use $typesCount variable instead of redundant count($types) call. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
6e601fb to
da44eea
Compare
Summary
PDOStatement::fetchAll()had a single function map entry withfetch_argumenttyped asint|string|callable, which caused false positives when passing constructor arguments (an array) as the third parameter inFETCH_CLASSmode. The fix adds proper multi-variant overloads.Changes
resources/functionMap.php: Replaced singlePDOStatement::fetchAllentry with 4 variants:(int $how)— for simple fetch modes likeFETCH_ASSOC(int $fetch_column, int $colno)— forFETCH_COLUMN(int $fetch_class, string $classname, ?array $ctorargs)— forFETCH_CLASS(int $fetch_func, callable $callback)— forFETCH_FUNCresources/functionMap_php80delta.php: Same overloads witharrayreturn type (instead ofarray|false)tests/PHPStan/Rules/Methods/data/bug-5509.phpRoot cause
The function map had a single signature where the second parameter was typed as
int|string|callable— a union of all possible types across different fetch modes. When callingfetchAll(PDO::FETCH_CLASS, StdClass::class, [new StdClass]), the array constructor argument didn't match any type in the variadic expansion ofint|string|callable. The fix follows the established pattern already used byPDO::queryandPDOStatement::setFetchMode, which have separate overload variants for each fetch mode.Analogous cases checked
PDO::query— already has proper overloads (variants '1, '2, '3)PDOStatement::setFetchMode— already has proper overloads (variants '1, '2, '3)PDOStatement::fetch— has fixed parameter types (int, int, int), no mode-dependent types neededTest
tests/PHPStan/Rules/Methods/data/bug-5509.php: Tests all valid calling patterns offetchAll()—FETCH_CLASSwith class name and constructor args,FETCH_CLASSwith just class name,FETCH_COLUMNwith column number,FETCH_FUNCwith callable, no args, and just mode.Fixes phpstan/phpstan#5509