From 29bd11bf12d548ac574b4178143fe13210d2ea7d Mon Sep 17 00:00:00 2001 From: Jan Nedbal Date: Wed, 17 Jun 2026 10:02:35 +0200 Subject: [PATCH 1/2] Support `::class` magic constant in array shape keys and const types Resolve the `::class` pseudo-constant in PHPDoc `ConstFetchNode`s instead of failing `hasConstant('class')` and degrading to ErrorType. Applies to both array-shape keys (`array{Foo::class: int}`) and const value types (`@return Foo::class`). Co-Authored-By: Claude Code --- src/PhpDoc/TypeNodeResolver.php | 16 ++++ .../nsrt/array-shape-class-constant-key.php | 77 +++++++++++++++++++ 2 files changed, 93 insertions(+) create mode 100644 tests/PHPStan/Analyser/nsrt/array-shape-class-constant-key.php diff --git a/src/PhpDoc/TypeNodeResolver.php b/src/PhpDoc/TypeNodeResolver.php index 4bef12bf24f..660a6eefa10 100644 --- a/src/PhpDoc/TypeNodeResolver.php +++ b/src/PhpDoc/TypeNodeResolver.php @@ -1276,6 +1276,14 @@ private function resolveArrayShapeOffsetType(ArrayShapeItemNode $itemNode, NameS } $constantName = $constExpr->name; + if (strtolower($constantName) === 'class') { + if ($isStatic) { + return new GenericClassStringType(new StaticType($classReflection)); + } + + return new ConstantStringType($classReflection->getName(), true); + } + if (!$classReflection->hasConstant($constantName)) { return new ErrorType(); } @@ -1380,6 +1388,14 @@ private function resolveConstTypeNode(ConstTypeNode $typeNode, NameScope $nameSc } $constantName = $constExpr->name; + if (strtolower($constantName) === 'class') { + if ($isStatic) { + return new GenericClassStringType(new StaticType($classReflection)); + } + + return new ConstantStringType($classReflection->getName(), true); + } + if (Strings::contains($constantName, '*')) { // convert * into .*? and escape everything else so the constants can be matched against the pattern $pattern = '{^' . str_replace('\\*', '.*?', preg_quote($constantName)) . '$}D'; diff --git a/tests/PHPStan/Analyser/nsrt/array-shape-class-constant-key.php b/tests/PHPStan/Analyser/nsrt/array-shape-class-constant-key.php new file mode 100644 index 00000000000..5c5772cd051 --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/array-shape-class-constant-key.php @@ -0,0 +1,77 @@ + 1]; + assertType('array{ArrayShapeClassConstantKey\\Test: 1}', $r); + + return $r; + } + + /** @return Test::class */ + public static function classConstant(): string + { + return self::class; + } + + /** @return array{Test::class, int} */ + public static function inTuple(): array + { + return [self::class, 1]; + } + +} + +final class FinalTest +{ + + /** @return array{static::class: int} */ + public static function staticInFinal(): array + { + return [self::class => 1]; + } + +} + +class Base +{ +} + +class Child extends Base +{ + + /** @return array{parent::class: int} */ + public static function parentKey(): array + { + return [parent::class => 1]; + } + + /** @return static::class */ + public static function staticClassConstant(): string + { + return static::class; + } + +} + +function test(): void +{ + assertType('array{ArrayShapeClassConstantKey\\Test: int}', Test::foo()); + assertType('\'ArrayShapeClassConstantKey\\\\Test\'', Test::classConstant()); + assertType('array{\'ArrayShapeClassConstantKey\\\\Test\', int}', Test::inTuple()); + assertType('array{ArrayShapeClassConstantKey\\FinalTest: int}', FinalTest::staticInFinal()); + assertType('array{ArrayShapeClassConstantKey\\Base: int}', Child::parentKey()); + assertType('class-string', Child::staticClassConstant()); +} From b0d7fffc7560a9d67ca127f18a2007e88c2edf2b Mon Sep 17 00:00:00 2001 From: Jan Nedbal Date: Wed, 17 Jun 2026 12:02:24 +0200 Subject: [PATCH 2/2] Cover static::class array-shape key on non-final class --- .../Analyser/nsrt/array-shape-class-constant-key.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/PHPStan/Analyser/nsrt/array-shape-class-constant-key.php b/tests/PHPStan/Analyser/nsrt/array-shape-class-constant-key.php index 5c5772cd051..704fde3a417 100644 --- a/tests/PHPStan/Analyser/nsrt/array-shape-class-constant-key.php +++ b/tests/PHPStan/Analyser/nsrt/array-shape-class-constant-key.php @@ -58,6 +58,12 @@ public static function parentKey(): array return [parent::class => 1]; } + /** @return array{static::class: int} */ + public static function staticKey(): array + { + return [static::class => 1]; + } + /** @return static::class */ public static function staticClassConstant(): string { @@ -73,5 +79,6 @@ function test(): void assertType('array{\'ArrayShapeClassConstantKey\\\\Test\', int}', Test::inTuple()); assertType('array{ArrayShapeClassConstantKey\\FinalTest: int}', FinalTest::staticInFinal()); assertType('array{ArrayShapeClassConstantKey\\Base: int}', Child::parentKey()); + assertType('non-empty-array, int>', Child::staticKey()); assertType('class-string', Child::staticClassConstant()); }