Fix phpstan/phpstan#13857: Incorrect narrowing of nested array after assignment#5162
Closed
phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
Closed
Fix phpstan/phpstan#13857: Incorrect narrowing of nested array after assignment#5162phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
Conversation
…stant key
When assigning to a nested array with a non-constant key like
`$array[$id]['state'] = 'foo'`, PHPStan incorrectly inferred that ALL
elements of the outer array had the narrowed type, not just the one
being modified. For example, `array<int, array{state: string}>` became
`non-empty-array<int, array{state: 'foo'}>` instead of the correct
`non-empty-array<int, array{state: string}>`.
The fix ensures that when the outer array key is non-constant (no
constant scalar decomposition), the new value type is unioned with
the existing item type rather than replacing it entirely, since we
cannot know which specific element was modified.
Fixes phpstan/phpstan#13857
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes phpstan/phpstan#13857
When assigning to a nested array with a non-constant key (
$array[$id]['state'] = 'foo'), PHPStan incorrectly narrowed the type of ALL elements in the outer array, not just the element being modified. This causedarray<int, array{state: string}>to becomenon-empty-array<int, array{state: 'foo'}>.Root cause
In
AssignHandler::produceArrayDimFetchAssignValueToWrite(), the reverse loop that processes nested array dim fetch assignments from innermost to outermost passed$unionValues = ($i === 0)tosetOffsetValueType(). For outer levels ($i > 0) with non-constant keys, this causedArrayType::setOffsetValueType()to replace the entire item type with the new value type, discarding the original item type.Fix
The fix changes the
$unionValueslogic so that non-constant keys (keys with no constant scalar decomposition) always use union semantics, preserving the original item type alongside the new value type:This correctly reflects that when modifying
$array[$id]['state']where$idis non-constant, we only know that ONE element was modified — the other elements retain their original types.Test expectation changes
Several existing test expectations were updated to reflect the more correct (but less precise) types:
assign-nested-arrays.php:{bar: 1, baz: 2}→{bar: 1, baz?: 2}(second assignment to same non-constant key can't guarantee both keys exist on all elements)pr-4390.php: Inner array key range lost (non-constant loop variable key)bug-10438.php:list<string>→list<string>|string(union with existing item type)bug-11679.php:array{foo: true}→array{foo?: bool}(only one element modified)bug-12927.php: LosthasOffsetValueprecision for non-constant key assignmentslevomat-foreach-array-key-exists-bug.php: More general item type after non-constant key modificationAutowiredAttributeServicesExtension.php: PHPDoc changednon-empty-listtolist(append in loop with non-constant key)All updated types are type-theoretically correct — the previous types were over-precise, incorrectly claiming knowledge about all array elements when only one was modified.