Fix phpstan/phpstan#7280: array_reduce could infer the precise type of the $initial argument within the callable and in the return type#5168
Open
phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
Conversation
- Added ArrayReduceCallbackClosureTypeExtension implementing FunctionParameterClosureTypeExtension - The extension provides the initial argument type (generalized to remove literals) as the carry parameter type - The array's value type is provided as the value parameter type - New regression test in tests/PHPStan/Analyser/nsrt/bug-7280.php Closes phpstan/phpstan#7280
staabm
reviewed
Mar 10, 2026
| $result1 = array_reduce( | ||
| ['test1', 'test2'], | ||
| static function (array $carry, string $value): array { | ||
| assertType("array{starts: array{}, ends: array{}}", $carry); |
Contributor
There was a problem hiding this comment.
I think this type is wrong. it only is correct for the first invocation when initial is used.
in this example, I think we need to aim for `assertType("array{starts: array, ends: array}", $carry);
staabm
reviewed
Mar 10, 2026
| $result3 = array_reduce( | ||
| [1, 2, 3], | ||
| static function (?int $carry, int $value): int { | ||
| assertType('null', $carry); |
Contributor
There was a problem hiding this comment.
Suggested change
| assertType('null', $carry); | |
| assertType('int|null', $carry); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
array_reducenow infers the precise type of the$initialargument as the$carryparameter type within the callback. Previously,$carrywas typed asmixed(from the function signature), which when intersected with the closure's native type hint gave only broad types likearrayornon-empty-array. Now, constant array shapes likearray{starts: array{}, ends: array{}}are preserved inside the callback.Changes
src/Type/Php/ArrayReduceCallbackClosureTypeExtension.php— a newFunctionParameterClosureTypeExtensionthat overrides the callback parameter types forarray_reduce:0→int)tests/PHPStan/Analyser/nsrt/bug-7280.php— regression test covering:Root cause
The function signature for
array_reduceinfunctionMap.phpdeclares the callback ascallable(mixed,mixed):mixed. This meant the carry parameter had no type information beyondmixed, so PHPStan could only use the closure's native type hints. By implementing aFunctionParameterClosureTypeExtension, the initial argument's type is now propagated to the carry parameter, which is then intersected with the native type hint to produce precise types inside the callback body.Test
The regression test verifies that:
$carryinside a callback withinitial: ['starts' => [], 'ends' => []]is typed asarray{starts: array{}, ends: array{}}(wasnon-empty-array)0and''are generalized tointandstring(no over-narrowing)nullFixes phpstan/phpstan#7280