Skip to content

Commit a131dd0

Browse files
authored
Support ::class magic constant in array shape keys and const types (#5891)
1 parent ca1918c commit a131dd0

2 files changed

Lines changed: 100 additions & 0 deletions

File tree

‎src/PhpDoc/TypeNodeResolver.php‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1276,6 +1276,14 @@ private function resolveArrayShapeOffsetType(ArrayShapeItemNode $itemNode, NameS
12761276
}
12771277

12781278
$constantName = $constExpr->name;
1279+
if (strtolower($constantName) === 'class') {
1280+
if ($isStatic) {
1281+
return new GenericClassStringType(new StaticType($classReflection));
1282+
}
1283+
1284+
return new ConstantStringType($classReflection->getName(), true);
1285+
}
1286+
12791287
if (!$classReflection->hasConstant($constantName)) {
12801288
return new ErrorType();
12811289
}
@@ -1380,6 +1388,14 @@ private function resolveConstTypeNode(ConstTypeNode $typeNode, NameScope $nameSc
13801388
}
13811389

13821390
$constantName = $constExpr->name;
1391+
if (strtolower($constantName) === 'class') {
1392+
if ($isStatic) {
1393+
return new GenericClassStringType(new StaticType($classReflection));
1394+
}
1395+
1396+
return new ConstantStringType($classReflection->getName(), true);
1397+
}
1398+
13831399
if (Strings::contains($constantName, '*')) {
13841400
// convert * into .*? and escape everything else so the constants can be matched against the pattern
13851401
$pattern = '{^' . str_replace('\\*', '.*?', preg_quote($constantName)) . '$}D';
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace ArrayShapeClassConstantKey;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
class Test
8+
{
9+
10+
/**
11+
* @return array{
12+
* Test::class: int
13+
* }
14+
*/
15+
public static function foo(): array
16+
{
17+
$r = [self::class => 1];
18+
assertType('array{ArrayShapeClassConstantKey\\Test: 1}', $r);
19+
20+
return $r;
21+
}
22+
23+
/** @return Test::class */
24+
public static function classConstant(): string
25+
{
26+
return self::class;
27+
}
28+
29+
/** @return array{Test::class, int} */
30+
public static function inTuple(): array
31+
{
32+
return [self::class, 1];
33+
}
34+
35+
}
36+
37+
final class FinalTest
38+
{
39+
40+
/** @return array{static::class: int} */
41+
public static function staticInFinal(): array
42+
{
43+
return [self::class => 1];
44+
}
45+
46+
}
47+
48+
class Base
49+
{
50+
}
51+
52+
class Child extends Base
53+
{
54+
55+
/** @return array{parent::class: int} */
56+
public static function parentKey(): array
57+
{
58+
return [parent::class => 1];
59+
}
60+
61+
/** @return array{static::class: int} */
62+
public static function staticKey(): array
63+
{
64+
return [static::class => 1];
65+
}
66+
67+
/** @return static::class */
68+
public static function staticClassConstant(): string
69+
{
70+
return static::class;
71+
}
72+
73+
}
74+
75+
function test(): void
76+
{
77+
assertType('array{ArrayShapeClassConstantKey\\Test: int}', Test::foo());
78+
assertType('\'ArrayShapeClassConstantKey\\\\Test\'', Test::classConstant());
79+
assertType('array{\'ArrayShapeClassConstantKey\\\\Test\', int}', Test::inTuple());
80+
assertType('array{ArrayShapeClassConstantKey\\FinalTest: int}', FinalTest::staticInFinal());
81+
assertType('array{ArrayShapeClassConstantKey\\Base: int}', Child::parentKey());
82+
assertType('non-empty-array<class-string<ArrayShapeClassConstantKey\\Child>, int>', Child::staticKey());
83+
assertType('class-string<ArrayShapeClassConstantKey\\Child>', Child::staticClassConstant());
84+
}

0 commit comments

Comments
 (0)