From 7cce9ee08b2056f6f4fde0b5f659a983db9e0636 Mon Sep 17 00:00:00 2001 From: Jan Nedbal Date: Fri, 11 Jul 2025 12:39:36 +0200 Subject: [PATCH] ObjectType: fix hasConstant behaviour --- src/Type/ObjectType.php | 16 ++++++++---- tests/PHPStan/Type/ObjectTypeTest.php | 35 +++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 5 deletions(-) diff --git a/src/Type/ObjectType.php b/src/Type/ObjectType.php index 04441d0e03..3d9bb96326 100644 --- a/src/Type/ObjectType.php +++ b/src/Type/ObjectType.php @@ -869,14 +869,20 @@ public function canAccessConstants(): TrinaryLogic public function hasConstant(string $constantName): TrinaryLogic { - $class = $this->getClassReflection(); - if ($class === null) { + $classReflection = $this->getClassReflection(); + if ($classReflection === null) { + return TrinaryLogic::createMaybe(); + } + + if ($classReflection->hasConstant($constantName)) { + return TrinaryLogic::createYes(); + } + + if ($classReflection->isFinal()) { return TrinaryLogic::createNo(); } - return TrinaryLogic::createFromBoolean( - $class->hasConstant($constantName), - ); + return TrinaryLogic::createMaybe(); } public function getConstant(string $constantName): ClassConstantReflection diff --git a/tests/PHPStan/Type/ObjectTypeTest.php b/tests/PHPStan/Type/ObjectTypeTest.php index fe9e864507..253e9f0c3a 100644 --- a/tests/PHPStan/Type/ObjectTypeTest.php +++ b/tests/PHPStan/Type/ObjectTypeTest.php @@ -557,6 +557,41 @@ public function testAccepts( ); } + public static function dataHasConstant(): Iterator + { + yield [ + new ObjectType(DateTimeImmutable::class), + 'ATOM', + TrinaryLogic::createYes(), + ]; + yield [ + new ObjectType(DateTimeImmutable::class), + 'CUSTOM', + TrinaryLogic::createMaybe(), + ]; + yield [ + new ObjectType(Closure::class), // is final + 'CUSTOM', + TrinaryLogic::createNo(), + ]; + yield [ + new ObjectType('SomeNonExistingClass'), + 'CUSTOM', + TrinaryLogic::createMaybe(), + ]; + } + + #[DataProvider('dataHasConstant')] + public function testHasConstant(ObjectType $type, string $constantName, TrinaryLogic $expectedResult): void + { + $actualResult = $type->hasConstant($constantName); + $this->assertSame( + $expectedResult->describe(), + $actualResult->describe(), + sprintf('%s -> hasConstant("%s")', $type->describe(VerbosityLevel::precise()), $constantName), + ); + } + public function testGetClassReflectionOfGenericClass(): void { $objectType = new ObjectType(Traversable::class);