Skip to content

Fix #13996: array_count_values array key inference broken since 2.1.34#4909

Merged
ondrejmirtes merged 2 commits into2.1.xfrom
create-pull-request/patch-3mzk8f9
Feb 12, 2026
Merged

Fix #13996: array_count_values array key inference broken since 2.1.34#4909
ondrejmirtes merged 2 commits into2.1.xfrom
create-pull-request/patch-3mzk8f9

Conversation

@phpstan-bot
Copy link
Collaborator

Summary

array_count_values() on array<string> was incorrectly inferred as returning non-empty-array<string, int<1, max>> since 2.1.34. The correct return type should be non-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 via toArrayKey(), check if the item type is a string where isNumericString() returns maybe. If so, union IntegerType into the key type to account for PHP's numeric string → integer key coercion.
  • tests/PHPStan/Analyser/nsrt/bug-13996.php: New regression test covering array<string>, array<int>, and array<int|string> inputs.
  • tests/PHPStan/Analyser/nsrt/array-count-values.php: Updated existing test expectation for general string case from string to int|string keys.

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, but StringType::toArrayKey() returns string — it doesn't account for the fact that a general string might 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 with isNumericString() returning maybe, the key type is widened to int|string.

Test

The regression test in tests/PHPStan/Analyser/nsrt/bug-13996.php verifies:

  • array_count_values(array<string>) returns non-empty-array<int|string, int<1, max>>
  • array_count_values(array<int>) returns non-empty-array<int, int<1, max>>
  • array_count_values(array<int|string>) returns non-empty-array<int|string, int<1, max>>

Fixes phpstan/phpstan#13996

ondrejmirtes and others added 2 commits February 12, 2026 19:28
- 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.
@ondrejmirtes ondrejmirtes merged commit 1470227 into 2.1.x Feb 12, 2026
632 of 642 checks passed
@ondrejmirtes ondrejmirtes deleted the create-pull-request/patch-3mzk8f9 branch February 12, 2026 20:01
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());
}
Copy link
Contributor

@VincentLanglet VincentLanglet Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I was thinking it should be a benevolent union.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also found another bug with this change
https://phpstan.org/r/f929c97d-ddc5-4318-9cf8-0700c0ab0a43

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

array_count_values array key inference broken since 2.1.34

3 participants