Skip to content

Fix phpstan/phpstan#11488: Wrongly assumed undefined variable since 1.11.10#5150

Closed
phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-92iamwb
Closed

Fix phpstan/phpstan#11488: Wrongly assumed undefined variable since 1.11.10#5150
phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-92iamwb

Conversation

@phpstan-bot
Copy link
Copy Markdown
Collaborator

Summary

When using count($row) !== N on a union of constant arrays like array{mixed}|array{mixed, string|null, mixed}, PHPStan incorrectly narrowed the type to *NEVER* instead of filtering out the array member that matches the count. This caused false positives about undefined variables from destructured assignments.

Changes

  • Added targeted handling in src/Analyser/TypeSpecifier.php in resolveNormalizedIdentical() for the falsey context when the argument is a union of multiple constant arrays
  • The new code directly filters union members by their array size and uses setAlwaysOverwriteTypes() to bypass the problematic TypeCombinator::remove behavior
  • Added regression test tests/PHPStan/Analyser/nsrt/bug-11488.php

Root cause

The bug was introduced when specifyTypesForCountFuncCall() was added to resolveNormalizedIdentical(). In the falsey context (e.g., count($row) !== 1), the method builds a type representing arrays that match the count, then uses TypeCombinator::remove to subtract it from the original union. However, array{mixed} (a 1-element constant array with a mixed value) is considered a supertype of array{mixed, string|null, mixed} (a 3-element array) because mixed accepts all values and extra keys don't prevent the supertype relationship. This caused TypeCombinator::remove to eliminate ALL union members, producing *NEVER*.

The fix adds a pre-check specifically for the falsey context with unions of multiple constant arrays. It directly filters out union members whose sizes exactly match the comparison value and uses setAlwaysOverwriteTypes() to set the result type directly, avoiding the supertype-related TypeCombinator::remove issue.

Test

Added tests/PHPStan/Analyser/nsrt/bug-11488.php which tests that count($row) !== 1 on array{mixed}|array{mixed, string|null, mixed} correctly narrows to array{mixed, string|null, mixed}, and that destructured variables from the narrowed array have correct types.

Fixes phpstan/phpstan#11488

- Added targeted handling in resolveNormalizedIdentical for falsey context
  with unions of multiple constant arrays to avoid TypeCombinator::remove
  issues caused by supertype relationships between constant array types
- New regression test in tests/PHPStan/Analyser/nsrt/bug-11488.php
- The root cause was that array{mixed} is a supertype of array{mixed,
  string|null, mixed}, so TypeCombinator::remove incorrectly eliminated
  all union members when negating count() === N comparisons

Closes phpstan/phpstan#11488
@VincentLanglet VincentLanglet deleted the create-pull-request/patch-92iamwb branch March 8, 2026 17:25
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