Skip to content

Fix phpstan/phpstan#7170: Can not set optional array element to explicit value in generic#5335

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

Fix phpstan/phpstan#7170: Can not set optional array element to explicit value in generic#5335
phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-o8o7c2a

Conversation

@phpstan-bot
Copy link
Copy Markdown
Collaborator

Summary

When a template type is bounded by a constant array with optional keys (e.g. @template Tdata of array{extension?: array<mixed>}), setting an optional key's value inside the class produced a false positive "does not accept" error. The same code without generics worked fine.

Changes

  • Override setOffsetValueType, setExistingOffsetValueType, and unsetOffset in src/Type/Generic/TemplateConstantArrayType.php to preserve the template type wrapper when the result of the parent operation is still compatible with the template's bound
  • Added test method testBug7170 in tests/PHPStan/Rules/Properties/TypesAssignedToPropertiesRuleTest.php
  • Added test data file tests/PHPStan/Rules/Properties/data/bug-7170.php

Root cause

TemplateConstantArrayType extends ConstantArrayType but did not override setOffsetValueType. When called, the parent ConstantArrayType::setOffsetValueType used ConstantArrayTypeBuilder which returned a plain ConstantArrayType, completely losing the template wrapper. The resulting concrete type (e.g. array{extension: array{}}) was then checked against the property's template type Tdata using TemplateTypeParameterStrategy, which rejected it because a concrete type is not the same as the template type.

The fix checks whether the result of the parent operation is still within the template's bound. If so, the template type itself is returned, preserving the template wrapper and allowing the property assignment to succeed.

Test

The regression test reproduces the original issue: a generic class with @template Tdata of array{extension?: array<mixed>} that sets $this->data['extension'] = [] inside an !isset guard. The test expects no errors, matching the behavior of the equivalent non-generic class.

Fixes phpstan/phpstan#7170

…c class

- Override setOffsetValueType, setExistingOffsetValueType, and unsetOffset in TemplateConstantArrayType to preserve the template wrapper when the result is compatible with the bound
- The root cause was that ConstantArrayType::setOffsetValueType returned a plain ConstantArrayType, losing the template context, which then failed the template parameter acceptance check
- New regression test in tests/PHPStan/Rules/Properties/data/bug-7170.php
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