Skip to content

Commit

Permalink
Fix enum performance problem
Browse files Browse the repository at this point in the history
  • Loading branch information
staabm committed May 11, 2024
1 parent 49498ce commit 0463255
Show file tree
Hide file tree
Showing 5 changed files with 836 additions and 17 deletions.
5 changes: 0 additions & 5 deletions phpstan-baseline.neon
Expand Up @@ -1602,11 +1602,6 @@ parameters:
count: 4
path: src/Type/TypeCombinator.php

-
message: "#^Doing instanceof PHPStan\\\\Type\\\\Enum\\\\EnumCaseObjectType is error\\-prone and deprecated\\. Use Type\\:\\:getEnumCases\\(\\) instead\\.$#"
count: 1
path: src/Type/TypeCombinator.php

-
message: "#^Doing instanceof PHPStan\\\\Type\\\\FloatType is error\\-prone and deprecated\\. Use Type\\:\\:isFloat\\(\\) instead\\.$#"
count: 1
Expand Down
32 changes: 22 additions & 10 deletions src/Type/ObjectType.php
Expand Up @@ -94,6 +94,9 @@ class ObjectType implements TypeWithClassName, SubtractableType

private ?string $cachedDescription = null;

/** @var array<string, list<EnumCaseObjectType>> */
private static array $enumCases = [];

/** @api */
public function __construct(
private string $className,
Expand All @@ -114,6 +117,7 @@ public static function resetCaches(): void
self::$methods = [];
self::$properties = [];
self::$ancestors = [];
self::$enumCases = [];
}

private static function createFromReflection(ClassReflection $reflection): self
Expand Down Expand Up @@ -1215,23 +1219,31 @@ public function getEnumCases(): array
return [];
}

$subtracted = [];
if ($this->subtractedType !== null) {
foreach ($this->subtractedType->getEnumCases() as $enumCase) {
$subtracted[$enumCase->getEnumCaseName()] = true;
}
$cacheKey = $this->describeCache();
if (array_key_exists($cacheKey, self::$enumCases)) {
return self::$enumCases[$cacheKey];
}

$cases = [];
$className = $classReflection->getName();

$cases = [];
foreach ($classReflection->getEnumCases() as $enumCase) {
if (array_key_exists($enumCase->getName(), $subtracted)) {
continue;
}
$cases[] = new EnumCaseObjectType($className, $enumCase->getName(), $classReflection);
}

return $cases;
if ($this->subtractedType !== null) {
foreach ($cases as $i => $case) {
$caseName = $case->getEnumCaseName();
foreach ($this->subtractedType->getEnumCases() as $subtracedCase) {
if ($caseName === $subtracedCase->getEnumCaseName()) {
unset($cases[$i]);
continue 2;
}
}
}
}

return self::$enumCases[$cacheKey] = array_values($cases);
}

public function isCallable(): TrinaryLogic
Expand Down
4 changes: 2 additions & 2 deletions src/Type/TypeCombinator.php
Expand Up @@ -16,7 +16,6 @@
use PHPStan\Type\Constant\ConstantFloatType;
use PHPStan\Type\Constant\ConstantIntegerType;
use PHPStan\Type\Constant\ConstantStringType;
use PHPStan\Type\Enum\EnumCaseObjectType;
use PHPStan\Type\Generic\GenericClassStringType;
use PHPStan\Type\Generic\TemplateArrayType;
use PHPStan\Type\Generic\TemplateBenevolentUnionType;
Expand Down Expand Up @@ -201,7 +200,8 @@ public static function union(Type ...$types): Type
if ($types[$i] instanceof StringType && !$types[$i] instanceof ClassStringType) {
$hasGenericScalarTypes[ConstantStringType::class] = true;
}
if ($types[$i] instanceof EnumCaseObjectType) {
$enumCases = $types[$i]->getEnumCases();
if (count($enumCases) === 1) {
$enumCaseTypes[$types[$i]->describe(VerbosityLevel::cache())] = $types[$i];

unset($types[$i]);
Expand Down
10 changes: 10 additions & 0 deletions tests/PHPStan/Analyser/AnalyserIntegrationTest.php
Expand Up @@ -1338,6 +1338,16 @@ public function testBug10772(): void
$this->assertNoErrors($errors);
}

public function testBug10979(): void
{
if (PHP_VERSION_ID < 80100) {
$this->markTestSkipped('Test requires PHP 8.1.');
}

$errors = $this->runAnalyse(__DIR__ . '/data/bug-10979.php');
$this->assertNoErrors($errors);
}

/**
* @param string[]|null $allAnalysedFiles
* @return Error[]
Expand Down

0 comments on commit 0463255

Please sign in to comment.