From 483e9d6d08e2cefc1274e886f51d40c13b6db6e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaroslav=20Hansl=C3=ADk?= Date: Sat, 2 Dec 2023 16:00:09 +0100 Subject: [PATCH] Support for final modifier in traits --- composer.json | 2 +- composer.lock | 4 +-- src/Reflection/ReflectionClass.php | 8 +++++- test/unit/Fixture/TraitFixtureWithFinal.php | 21 ++++++++++++++++ test/unit/Reflection/ReflectionClassTest.php | 26 ++++++++++++++++++++ 5 files changed, 57 insertions(+), 4 deletions(-) create mode 100644 test/unit/Fixture/TraitFixtureWithFinal.php diff --git a/composer.json b/composer.json index a73fcdf1c..0360772ca 100644 --- a/composer.json +++ b/composer.json @@ -3,7 +3,7 @@ "description": "Better Reflection - an improved code reflection API", "license": "MIT", "require": { - "php": "~8.1.0 || ~8.2.0 || ~8.3.0", + "php": "~8.1.0 || ~8.2.0 || ~8.3.2", "ext-json": "*", "jetbrains/phpstorm-stubs": "2023.3", "nikic/php-parser": "^4.18.0", diff --git a/composer.lock b/composer.lock index 59c5cb968..23d10b4ff 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "2dcdc410ac226bdc5dcf85af656876ec", + "content-hash": "5686be6f24e0f98fd1a367b3a670482a", "packages": [ { "name": "jetbrains/phpstorm-stubs", @@ -5049,7 +5049,7 @@ "prefer-stable": true, "prefer-lowest": false, "platform": { - "php": "~8.1.0 || ~8.2.0 || ~8.3.0", + "php": "~8.1.0 || ~8.2.0 || ~8.3.2", "ext-json": "*" }, "platform-dev": [], diff --git a/src/Reflection/ReflectionClass.php b/src/Reflection/ReflectionClass.php index 8e977e4eb..2cd39aed7 100644 --- a/src/Reflection/ReflectionClass.php +++ b/src/Reflection/ReflectionClass.php @@ -357,7 +357,13 @@ private function createMethodsFromTrait(ReflectionMethod $method): array if (array_key_exists($methodHash, $this->traitsData['modifiers'])) { // PhpParser modifiers are compatible with PHP reflection modifiers - $methodModifiers = ($methodModifiers & ~ Node\Stmt\Class_::VISIBILITY_MODIFIER_MASK) | $this->traitsData['modifiers'][$methodHash]; + if ($this->traitsData['modifiers'][$methodHash] & Node\Stmt\Class_::VISIBILITY_MODIFIER_MASK) { + $methodModifiers = ($methodModifiers & ~ Node\Stmt\Class_::VISIBILITY_MODIFIER_MASK) | $this->traitsData['modifiers'][$methodHash]; + } + + if ($this->traitsData['modifiers'][$methodHash] & Node\Stmt\Class_::MODIFIER_FINAL) { + $methodModifiers |= Node\Stmt\Class_::MODIFIER_FINAL; + } } $createMethod = function (string|null $aliasMethodName) use ($method, $methodModifiers): ReflectionMethod { diff --git a/test/unit/Fixture/TraitFixtureWithFinal.php b/test/unit/Fixture/TraitFixtureWithFinal.php new file mode 100644 index 000000000..fc1b6ed9c --- /dev/null +++ b/test/unit/Fixture/TraitFixtureWithFinal.php @@ -0,0 +1,21 @@ +getMethod('d_renamed')->getDeclaringClass()->getName()); } + #[RequiresPhp('8.3')] + public function testMethodsFromTraitsWithFinal(): void + { + $reflector = new DefaultReflector(new SingleFileSourceLocator( + __DIR__ . '/../Fixture/TraitFixtureWithFinal.php', + $this->astLocator, + )); + + $classInfo = $reflector->reflectClass('TraitFixtureWithFinal'); + + self::assertTrue($classInfo->hasMethod('foo')); + self::assertTrue($classInfo->getMethod('foo')->isFinal()); + self::assertFalse($classInfo->getMethod('foo')->isPrivate()); + self::assertTrue($classInfo->getMethod('foo')->isProtected()); + self::assertFalse($classInfo->getMethod('foo')->isPublic()); + + $classInfo = $reflector->reflectClass('TraitFixtureWithPublic'); + + self::assertTrue($classInfo->hasMethod('boo')); + self::assertTrue($classInfo->getMethod('boo')->isFinal()); + self::assertFalse($classInfo->getMethod('boo')->isPrivate()); + self::assertFalse($classInfo->getMethod('boo')->isProtected()); + self::assertTrue($classInfo->getMethod('boo')->isPublic()); + } + public function testMethodsFromTraitsWithConflicts(): void { $reflector = new DefaultReflector(new SingleFileSourceLocator(