Skip to content

Fix phpstan/phpstan#10089: Multi-dimensional array shape wrongly inferred#5259

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

Fix phpstan/phpstan#10089: Multi-dimensional array shape wrongly inferred#5259
phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-l15c0wj

Conversation

@phpstan-bot
Copy link
Collaborator

Summary

When assigning to a multi-dimensional array with a non-constant outer key (e.g., $matrix[$size - 1][8] = 3), PHPStan incorrectly applied hasOffsetValue(8, 3) to ALL inner arrays instead of just the one at the specified index. This caused false positive errors like "Strict comparison using === between 3 and 0 will always evaluate to false" when accessing other rows of the matrix.

Changes

  • Modified src/Analyser/ExprHandler/AssignHandler.php in the produceArrayDimFetchAssignValueToWrite() method
  • Added a $previousIterationUsedExistingBranch flag to track whether the inner dimension's processing used the "existing expression" branch (which indicates the expression is tracked in scope, e.g., from a foreach binding)
  • When processing an outer dimension with a non-constant offset where the inner dimension was NOT a tracked expression, HasOffsetValueType accessories are stripped from the inner value before applying it to the outer array
  • This prevents the incorrect propagation of hasOffsetValue to all array entries while preserving correct behavior for foreach loops where all entries are genuinely modified

Root cause

In produceArrayDimFetchAssignValueToWrite(), nested array assignments are processed from innermost to outermost dimension. When the inner dimension has a constant offset (like [8]), HasOffsetValueType(8, 3) is correctly added to represent that specific inner array entry. However, when the outer dimension has a non-constant offset (like [$size - 1]), setOffsetValueType with $unionValues = false replaces ALL entries' value type with the inner result — including the HasOffsetValueType that should only apply to one specific entry.

The fix strips HasOffsetValueType from the inner value before propagating to the outer dimension, but only when the inner expression is not tracked in scope (distinguishing single assignments from foreach loops where all entries are modified).

Test

Added tests/PHPStan/Analyser/nsrt/bug-10089.php which reproduces the original issue: creating a matrix with array_fill, assigning to one row, and verifying that the matrix type doesn't claim all rows have the modified offset value.

Fixes phpstan/phpstan#10089

- When assigning to a nested array with a non-constant outer key like
  $matrix[$size - 1][8] = 3, the HasOffsetValueType(8, 3) from the inner
  dimension was incorrectly propagated to ALL entries of the outer array
- Added logic in AssignHandler::produceArrayDimFetchAssignValueToWrite()
  to strip HasOffsetValueType from the inner value when the outer offset
  is non-constant and the inner dimension was not a tracked expression
  (i.e., not from a foreach binding)
- New regression test in tests/PHPStan/Analyser/nsrt/bug-10089.php

Fixes phpstan/phpstan#10089
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