Skip to content

Fix phpstan/phpstan#11705: Value of a variable is forgotten in case where it ought to be known with certainty#5191

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

Fix phpstan/phpstan#11705: Value of a variable is forgotten in case where it ought to be known with certainty#5191
phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-h0j2sbp

Conversation

@phpstan-bot
Copy link
Collaborator

Summary

When array_key_exists($tag, $theInput) is called with a non-constant key $tag, PHPStan stores the full iterable value type for the expression $theInput[$tag]. If $tag is later narrowed (e.g., in a switch/case), the stored expression type is not updated, causing PHPStan to report incorrect errors because it uses the broad stored type instead of recomputing based on the narrowed key.

Changes

  • Modified src/Analyser/MutatingScope.php in resolveType(): when resolving an ArrayDimFetch expression that has a stored expression type, and the dim has been narrowed to a constant scalar value, recompute the offset value type and use it if it is strictly more specific than the stored type
  • Added regression test tests/PHPStan/Analyser/nsrt/bug-11705.php

Root cause

ArrayKeyExistsFunctionTypeSpecifyingExtension stores the full getIterableValueType() (e.g., string|array<int,string>) for $theInput[$tag] when the key is non-constant. This stored type takes precedence in resolveType() over computing the type fresh. When $tag is later narrowed to a constant like 'name' inside a switch case, the stored broad type is returned instead of the specific string that getOffsetValueType(ConstantStringType('name')) would produce.

The fix adds a check in resolveType() that, for ArrayDimFetch nodes with stored expression types, recomputes the offset value type when the dim is a constant scalar. If the recomputed type is strictly more specific (stored type is a supertype but not vice versa), the more specific type is used.

Test

Added tests/PHPStan/Analyser/nsrt/bug-11705.php which reproduces the original issue: a shaped array array{'name':string,'owners':array<int,string>} accessed with $theInput[$tag] inside a case 'name': block. Asserts that:

  • $tag is correctly narrowed to 'name'
  • $theInput[$tag] resolves to string (not string|array<int,string>)
  • The narrowing persists after an always-true if ($tag === 'name') check

Fixes phpstan/phpstan#11705

…h after narrowing

- In MutatingScope::resolveType(), when an ArrayDimFetch has a stored expression type
  and its dim has been narrowed to a constant scalar value, recompute the offset value
  type from the current array and dim types
- Only replace the stored type when the recomputed type is strictly more specific
- New regression test in tests/PHPStan/Analyser/nsrt/bug-11705.php
- Root cause: array_key_exists with non-constant key stored a broad type for the
  ArrayDimFetch expression, which was not updated when the key was later narrowed
  (e.g., in a switch case)
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.

1 participant