Skip to content

Fix phpstan/phpstan#13705: Adding elements to empty array in while loop does not result in array being recognized as potentially non-empty for in_array#5290

Open
phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-3tsf3dm
Open

Fix phpstan/phpstan#13705: Adding elements to empty array in while loop does not result in array being recognized as potentially non-empty for in_array#5290
phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-3tsf3dm

Conversation

@phpstan-bot
Copy link
Collaborator

This PR fixes phpstan/phpstan#13705.

Summary

When count($array) < $variable (where $variable has a range type like int<1, 42>) was used as a while loop condition, PHPStan incorrectly narrowed the array type to array{} inside the loop body. This caused false positives with in_array() and similar functions that depend on the array being potentially non-empty.

Root cause

The condition count($codes) < $quantity is internally transformed to !($quantity <= count($codes)). The SmallerOrEqual handler in TypeSpecifier computes a sizeType from the left operand and passes it to specifyTypesForCountFuncCall. In the falsey context (negated by BooleanNot), this function interprets sizeType = int<1, 42> as "count is NOT in int<1,42>", concluding count must be 0. But the actual semantic is "count < $quantity where $quantity is somewhere in int<1,42>" — count could easily be non-zero (e.g., count=3 when $quantity=5).

Fix

Skip specifyTypesForCountFuncCall when the context is falsey AND the left operand is an IntegerRangeType. The sound narrowing for this case cannot be expressed with the current sizeType abstraction, so no narrowing is better than unsound narrowing.

Two existing test files (bug-4700.php, bug11480.php) had expectations that relied on the previous unsound over-narrowing and have been updated.

Test plan

  • Added regression test tests/PHPStan/Analyser/nsrt/bug-13705.php covering while loop, do-while loop, and simple while loop cases
  • Updated expectations in bug-4700.php and bug11480.php
  • Full test suite passes (11656 tests)
  • PHPStan self-analysis passes

…sons

When `count($arr) < $rangeVar` was used as a while loop condition (or
similar), the TypeSpecifier would incorrectly narrow the array to
`array{}` inside the loop body. This happened because
`specifyTypesForCountFuncCall` treated an IntegerRangeType sizeType
in falsey context as "count NOT in range", which is stricter than
the actual semantic "count < some value within the range".

Skip specifyTypesForCountFuncCall entirely when context is falsey
and the left operand is an IntegerRangeType, since the correct
narrowing cannot be expressed with the current sizeType abstraction.

Update test expectations in bug-4700 and bug11480 which relied on
the previous unsound over-narrowing behavior.

Fixes phpstan/phpstan#13705
gnutix pushed a commit to gnutix/phpstan-src that referenced this pull request Mar 25, 2026
…an#5059)

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
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.

2 participants