Skip to content

Commit 93af41b

Browse files
committed
Fix infinite recursion with self-referencing class constant
1 parent 2ec416e commit 93af41b

File tree

3 files changed

+36
-0
lines changed

3 files changed

+36
-0
lines changed

src/Reflection/InitializerExprTypeResolver.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
use PHPStan\Type\TypeUtils;
6767
use PHPStan\Type\TypeWithClassName;
6868
use PHPStan\Type\UnionType;
69+
use function array_key_exists;
6970
use function array_keys;
7071
use function array_merge;
7172
use function assert;
@@ -79,6 +80,7 @@
7980
use function is_int;
8081
use function max;
8182
use function min;
83+
use function sprintf;
8284
use function strtolower;
8385
use const INF;
8486

@@ -87,6 +89,9 @@ class InitializerExprTypeResolver
8789

8890
public const CALCULATE_SCALARS_LIMIT = 128;
8991

92+
/** @var array<string, true> */
93+
private array $currentlyResolvingClassConstant = [];
94+
9095
public function __construct(
9196
private ConstantResolver $constantResolver,
9297
private ReflectionProviderProvider $reflectionProviderProvider,
@@ -1876,9 +1881,18 @@ function (Type $type, callable $traverse): Type {
18761881
continue;
18771882
}
18781883

1884+
$resolvingName = sprintf('%s::%s', $constantClassReflection->getName(), $constantName);
1885+
if (array_key_exists($resolvingName, $this->currentlyResolvingClassConstant)) {
1886+
$types[] = new MixedType();
1887+
continue;
1888+
}
1889+
1890+
$this->currentlyResolvingClassConstant[$resolvingName] = true;
1891+
18791892
if (!$isObject) {
18801893
$reflectionConstant = $constantClassReflection->getNativeReflection()->getReflectionConstant($constantName);
18811894
if ($reflectionConstant === false) {
1895+
unset($this->currentlyResolvingClassConstant[$resolvingName]);
18821896
continue;
18831897
}
18841898
$reflectionConstantDeclaringClass = $reflectionConstant->getDeclaringClass();
@@ -1893,6 +1907,7 @@ function (Type $type, callable $traverse): Type {
18931907
$constantType,
18941908
$nativeType,
18951909
);
1910+
unset($this->currentlyResolvingClassConstant[$resolvingName]);
18961911
continue;
18971912
}
18981913

@@ -1903,6 +1918,7 @@ function (Type $type, callable $traverse): Type {
19031918
&& !$constantReflection->hasPhpDocType()
19041919
&& !$constantReflection->hasNativeType()
19051920
) {
1921+
unset($this->currentlyResolvingClassConstant[$resolvingName]);
19061922
return new MixedType();
19071923
}
19081924

@@ -1925,6 +1941,7 @@ function (Type $type, callable $traverse): Type {
19251941
$constantType,
19261942
$nativeType,
19271943
);
1944+
unset($this->currentlyResolvingClassConstant[$resolvingName]);
19281945
$types[] = $constantType;
19291946
}
19301947

tests/PHPStan/Analyser/AnalyserIntegrationTest.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1264,6 +1264,12 @@ public function testBug10086(): void
12641264
$this->assertNoErrors($errors);
12651265
}
12661266

1267+
public function testBug10147(): void
1268+
{
1269+
$errors = $this->runAnalyse(__DIR__ . '/data/bug-10147.php');
1270+
$this->assertNoErrors($errors);
1271+
}
1272+
12671273
/**
12681274
* @param string[]|null $allAnalysedFiles
12691275
* @return Error[]
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
namespace Bug10147;
4+
5+
abstract class AbstractClass
6+
{
7+
const BASE_URL = self::BASE_URL;
8+
}
9+
10+
final class TestClass extends AbstractClass
11+
{
12+
const BASE_URL = 'http://example.com';
13+
}

0 commit comments

Comments
 (0)