Skip to content

Commit 9a0bfd2

Browse files
authored
ObjectType: fix isEnum
1 parent adc504d commit 9a0bfd2

File tree

2 files changed

+49
-1
lines changed

2 files changed

+49
-1
lines changed

Diff for: src/Type/ObjectType.php

+20-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use ArrayObject;
77
use Closure;
88
use Countable;
9+
use DateTimeInterface;
910
use Iterator;
1011
use IteratorAggregate;
1112
use PHPStan\Analyser\OutOfClassScope;
@@ -44,6 +45,8 @@
4445
use PHPStan\Type\Traits\NonGeneralizableTypeTrait;
4546
use PHPStan\Type\Traits\NonGenericTypeTrait;
4647
use PHPStan\Type\Traits\UndecidedComparisonTypeTrait;
48+
use Stringable;
49+
use Throwable;
4750
use Traversable;
4851
use function array_key_exists;
4952
use function array_map;
@@ -730,7 +733,23 @@ public function isEnum(): TrinaryLogic
730733
return TrinaryLogic::createMaybe();
731734
}
732735

733-
return TrinaryLogic::createFromBoolean($classReflection->isEnum());
736+
if (
737+
$classReflection->isEnum()
738+
|| $classReflection->is('UnitEnum')
739+
) {
740+
return TrinaryLogic::createYes();
741+
}
742+
743+
if (
744+
$classReflection->isInterface()
745+
&& !$classReflection->is(Stringable::class) // enums cannot have __toString
746+
&& !$classReflection->is(Throwable::class) // enums cannot extend Exception/Error
747+
&& !$classReflection->is(DateTimeInterface::class) // userland classes cannot extend DateTimeInterface
748+
) {
749+
return TrinaryLogic::createMaybe();
750+
}
751+
752+
return TrinaryLogic::createNo();
734753
}
735754

736755
public function canAccessProperties(): TrinaryLogic

Diff for: tests/PHPStan/Type/ObjectTypeTest.php

+29
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,35 @@ public function testIsIterable(ObjectType $type, TrinaryLogic $expectedResult):
7070
);
7171
}
7272

73+
/**
74+
* @return iterable<array{0: ObjectType, 1: TrinaryLogic}>
75+
*/
76+
public function dataIsEnum(): iterable
77+
{
78+
if (PHP_VERSION_ID >= 80000) {
79+
yield [new ObjectType('UnitEnum'), TrinaryLogic::createYes()];
80+
yield [new ObjectType('BackedEnum'), TrinaryLogic::createYes()];
81+
}
82+
yield [new ObjectType('Unknown'), TrinaryLogic::createMaybe()];
83+
yield [new ObjectType('Countable'), TrinaryLogic::createMaybe()];
84+
yield [new ObjectType('Stringable'), TrinaryLogic::createNo()];
85+
yield [new ObjectType('Throwable'), TrinaryLogic::createNo()];
86+
yield [new ObjectType('DateTime'), TrinaryLogic::createNo()];
87+
}
88+
89+
/**
90+
* @dataProvider dataIsEnum
91+
*/
92+
public function testIsEnum(ObjectType $type, TrinaryLogic $expectedResult): void
93+
{
94+
$actualResult = $type->isEnum();
95+
$this->assertSame(
96+
$expectedResult->describe(),
97+
$actualResult->describe(),
98+
sprintf('%s -> isEnum()', $type->describe(VerbosityLevel::precise())),
99+
);
100+
}
101+
73102
public function dataIsCallable(): array
74103
{
75104
return [

0 commit comments

Comments
 (0)