Skip to content

Commit 0463255

Browse files
authored
Fix enum performance problem
1 parent 49498ce commit 0463255

File tree

5 files changed

+836
-17
lines changed

5 files changed

+836
-17
lines changed

phpstan-baseline.neon

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1602,11 +1602,6 @@ parameters:
16021602
count: 4
16031603
path: src/Type/TypeCombinator.php
16041604

1605-
-
1606-
message: "#^Doing instanceof PHPStan\\\\Type\\\\Enum\\\\EnumCaseObjectType is error\\-prone and deprecated\\. Use Type\\:\\:getEnumCases\\(\\) instead\\.$#"
1607-
count: 1
1608-
path: src/Type/TypeCombinator.php
1609-
16101605
-
16111606
message: "#^Doing instanceof PHPStan\\\\Type\\\\FloatType is error\\-prone and deprecated\\. Use Type\\:\\:isFloat\\(\\) instead\\.$#"
16121607
count: 1

src/Type/ObjectType.php

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,9 @@ class ObjectType implements TypeWithClassName, SubtractableType
9494

9595
private ?string $cachedDescription = null;
9696

97+
/** @var array<string, list<EnumCaseObjectType>> */
98+
private static array $enumCases = [];
99+
97100
/** @api */
98101
public function __construct(
99102
private string $className,
@@ -114,6 +117,7 @@ public static function resetCaches(): void
114117
self::$methods = [];
115118
self::$properties = [];
116119
self::$ancestors = [];
120+
self::$enumCases = [];
117121
}
118122

119123
private static function createFromReflection(ClassReflection $reflection): self
@@ -1215,23 +1219,31 @@ public function getEnumCases(): array
12151219
return [];
12161220
}
12171221

1218-
$subtracted = [];
1219-
if ($this->subtractedType !== null) {
1220-
foreach ($this->subtractedType->getEnumCases() as $enumCase) {
1221-
$subtracted[$enumCase->getEnumCaseName()] = true;
1222-
}
1222+
$cacheKey = $this->describeCache();
1223+
if (array_key_exists($cacheKey, self::$enumCases)) {
1224+
return self::$enumCases[$cacheKey];
12231225
}
12241226

1225-
$cases = [];
12261227
$className = $classReflection->getName();
1228+
1229+
$cases = [];
12271230
foreach ($classReflection->getEnumCases() as $enumCase) {
1228-
if (array_key_exists($enumCase->getName(), $subtracted)) {
1229-
continue;
1230-
}
12311231
$cases[] = new EnumCaseObjectType($className, $enumCase->getName(), $classReflection);
12321232
}
12331233

1234-
return $cases;
1234+
if ($this->subtractedType !== null) {
1235+
foreach ($cases as $i => $case) {
1236+
$caseName = $case->getEnumCaseName();
1237+
foreach ($this->subtractedType->getEnumCases() as $subtracedCase) {
1238+
if ($caseName === $subtracedCase->getEnumCaseName()) {
1239+
unset($cases[$i]);
1240+
continue 2;
1241+
}
1242+
}
1243+
}
1244+
}
1245+
1246+
return self::$enumCases[$cacheKey] = array_values($cases);
12351247
}
12361248

12371249
public function isCallable(): TrinaryLogic

src/Type/TypeCombinator.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
use PHPStan\Type\Constant\ConstantFloatType;
1717
use PHPStan\Type\Constant\ConstantIntegerType;
1818
use PHPStan\Type\Constant\ConstantStringType;
19-
use PHPStan\Type\Enum\EnumCaseObjectType;
2019
use PHPStan\Type\Generic\GenericClassStringType;
2120
use PHPStan\Type\Generic\TemplateArrayType;
2221
use PHPStan\Type\Generic\TemplateBenevolentUnionType;
@@ -201,7 +200,8 @@ public static function union(Type ...$types): Type
201200
if ($types[$i] instanceof StringType && !$types[$i] instanceof ClassStringType) {
202201
$hasGenericScalarTypes[ConstantStringType::class] = true;
203202
}
204-
if ($types[$i] instanceof EnumCaseObjectType) {
203+
$enumCases = $types[$i]->getEnumCases();
204+
if (count($enumCases) === 1) {
205205
$enumCaseTypes[$types[$i]->describe(VerbosityLevel::cache())] = $types[$i];
206206

207207
unset($types[$i]);

tests/PHPStan/Analyser/AnalyserIntegrationTest.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1338,6 +1338,16 @@ public function testBug10772(): void
13381338
$this->assertNoErrors($errors);
13391339
}
13401340

1341+
public function testBug10979(): void
1342+
{
1343+
if (PHP_VERSION_ID < 80100) {
1344+
$this->markTestSkipped('Test requires PHP 8.1.');
1345+
}
1346+
1347+
$errors = $this->runAnalyse(__DIR__ . '/data/bug-10979.php');
1348+
$this->assertNoErrors($errors);
1349+
}
1350+
13411351
/**
13421352
* @param string[]|null $allAnalysedFiles
13431353
* @return Error[]

0 commit comments

Comments
 (0)