Skip to content

Fix phpstan/phpstan#13828: Reference to static const behaves as self const#5351

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

Fix phpstan/phpstan#13828: Reference to static const behaves as self const#5351
phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-nhqatd7

Conversation

@phpstan-bot
Copy link
Copy Markdown
Collaborator

Summary

When using @return static::CONST in a PHPDoc, PHPStan was resolving the constant value from the declaring class (like self::CONST), ignoring subclass overrides. This fix introduces late static binding for class constant references in PHPDoc types.

Changes

  • Added src/Type/ClassConstantAccessType.php — a new LateResolvableType that wraps a class type and constant name, deferring resolution until the actual class is known
  • Modified src/PhpDoc/TypeNodeResolver.phpresolveConstTypeNode() now returns a ClassConstantAccessType(StaticType, constantName) for static::CONST on non-final classes, instead of immediately resolving the constant value

Root cause

In TypeNodeResolver::resolveConstTypeNode(), both static and self were mapped to $nameScope->getClassName(), and the constant value was resolved immediately. This lost the late static binding semantics of static::. The fix preserves the StaticType reference inside a ClassConstantAccessType, which gets resolved when the method's return type is transformed by CalledOnTypeUnresolvedMethodPrototypeReflection::transformStaticType() — at that point, the StaticType is replaced with the actual called class type, and the constant is looked up from the correct class.

Test

Added tests/PHPStan/Analyser/nsrt/bug-13828.php with a parent class declaring @return static::FOO_BAR and two subclasses overriding the constant. The test verifies that calling test() on each subclass returns the correct constant value.

Fixes phpstan/phpstan#13828

…ic binding

- Added ClassConstantAccessType as a LateResolvableType that wraps a class type and constant name
- Modified TypeNodeResolver::resolveConstTypeNode to return ClassConstantAccessType for static::CONST
- When the type gets resolved (e.g., via transformStaticType), StaticType is replaced with the actual class, and the constant is looked up from the correct subclass
- New regression test in tests/PHPStan/Analyser/nsrt/bug-13828.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