Fix #13996: array_count_values array key inference broken since 2.1.34#4909
Merged
ondrejmirtes merged 2 commits into2.1.xfrom Feb 12, 2026
Merged
Fix #13996: array_count_values array key inference broken since 2.1.34#4909ondrejmirtes merged 2 commits into2.1.xfrom
ondrejmirtes merged 2 commits into2.1.xfrom
Conversation
- PHP casts numeric strings to integer array keys, so array_count_values on array<string> should return non-empty-array<int|string, int<1, max>> not non-empty-array<string, int<1, max>> - Added isNumericString() check in ArrayCountValuesDynamicReturnTypeExtension to union IntegerType into key type when string may be numeric - New regression test in tests/PHPStan/Analyser/nsrt/bug-13996.php - Updated existing test expectation for general string case Closes phpstan/phpstan#13996
Automated fix attempt 1 for CI failures.
Comment on lines
+57
to
+63
| $keyType = $itemType->toArrayKey(); | ||
|
|
||
| // PHP casts numeric strings to integer keys, so a general string | ||
| // that might be numeric should produce int|string keys | ||
| if ($itemType->isString()->yes() && $itemType->isNumericString()->maybe()) { | ||
| $keyType = TypeCombinator::union($keyType, new IntegerType()); | ||
| } |
Contributor
There was a problem hiding this comment.
Markus did a similar fix with #4798 (comment) and I commented on it, I dunno if you saw it @ondrejmirtes.
This will be globally solved with the issue phpstan/phpstan#6847 and cannot/shouldn't be handled locally.
I feel like this fix is not really the good one since now we're having a different behavior will all the other array<string> occurences, see https://phpstan.org/r/65e09a6e-3613-4f68-9b68-5bb5fdfd6b7a
And especially the fact that now
foreach (array_count_values($arr) as $key => $count) {
$value = $arr[$key];
}
will report an issue.
Member
There was a problem hiding this comment.
Yeah I was thinking it should be a benevolent union.
Contributor
There was a problem hiding this comment.
I also found another bug with this change
https://phpstan.org/r/f929c97d-ddc5-4318-9cf8-0700c0ab0a43
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_count_values()onarray<string>was incorrectly inferred as returningnon-empty-array<string, int<1, max>>since 2.1.34. The correct return type should benon-empty-array<int|string, int<1, max>>because PHP automatically casts numeric strings (like'123') to integer array keys.Changes
src/Type/Php/ArrayCountValuesDynamicReturnTypeExtension.php: After computing the key type viatoArrayKey(), check if the item type is a string whereisNumericString()returnsmaybe. If so, unionIntegerTypeinto the key type to account for PHP's numeric string → integer key coercion.tests/PHPStan/Analyser/nsrt/bug-13996.php: New regression test coveringarray<string>,array<int>, andarray<int|string>inputs.tests/PHPStan/Analyser/nsrt/array-count-values.php: Updated existing test expectation for generalstringcase fromstringtoint|stringkeys.Root cause
Commit 2de77d9 ("Fix ArrayCountValuesDynamicReturnTypeExtension") introduced
$itemType->toArrayKey()to properly convert value types to array key types. This correctly handles constant strings (e.g.,'1'→int(1)) and numeric strings, butStringType::toArrayKey()returnsstring— it doesn't account for the fact that a generalstringmight be a numeric string that PHP would cast to an integer key. The fix adds a post-hoc check: if the item type is a string withisNumericString()returningmaybe, the key type is widened toint|string.Test
The regression test in
tests/PHPStan/Analyser/nsrt/bug-13996.phpverifies:array_count_values(array<string>)returnsnon-empty-array<int|string, int<1, max>>array_count_values(array<int>)returnsnon-empty-array<int, int<1, max>>array_count_values(array<int|string>)returnsnon-empty-array<int|string, int<1, max>>Fixes phpstan/phpstan#13996