diff --git a/src/main/php/lang/meta/MetaInformation.class.php b/src/main/php/lang/meta/MetaInformation.class.php index da7fbbe..57f792c 100755 --- a/src/main/php/lang/meta/MetaInformation.class.php +++ b/src/main/php/lang/meta/MetaInformation.class.php @@ -192,6 +192,23 @@ public function propertyComment($reflect) { } } + /** + * Returns modifiers for a given property, including non-declared + * + * @param \ReflectionProperty $reflect + * @return int + */ + public function propertyModifiers($reflect) { + $name= $reflect->getDeclaringClass()->name; + $c= \xp::$cn[$name] ?? strtr($name, '\\', '.'); + if ($meta= \xp::$meta[$c][0][$reflect->getName()][DETAIL_ARGUMENTS] ?? null) { + return $reflect->getModifiers() | (int)$meta[0]; + } else { + $tags= $this->tags($reflect); + return $reflect->getModifiers() | (isset($tags['final']) ? MODIFIER_FINAL : 0); + } + } + /** * Returns annotation map (type => arguments) for a given method * diff --git a/src/main/php/lang/reflection/Property.class.php b/src/main/php/lang/reflection/Property.class.php index 4bedfea..fd26b19 100755 --- a/src/main/php/lang/reflection/Property.class.php +++ b/src/main/php/lang/reflection/Property.class.php @@ -53,7 +53,7 @@ public function modifiers($hook= null) { ]; // Readonly implies protected(set) - $bits= $this->reflect->getModifiers(); + $bits= Reflection::meta()->propertyModifiers($this->reflect); $bits & Modifiers::IS_READONLY && $bits|= Modifiers::IS_PROTECTED_SET; switch ($hook) { diff --git a/src/test/php/lang/reflection/unittest/PropertiesTest.class.php b/src/test/php/lang/reflection/unittest/PropertiesTest.class.php index ab05b57..fcb1bf5 100755 --- a/src/test/php/lang/reflection/unittest/PropertiesTest.class.php +++ b/src/test/php/lang/reflection/unittest/PropertiesTest.class.php @@ -34,6 +34,22 @@ public function modifiers() { ); } + #[Test] + public function modifiers_tagged_final() { + Assert::equals( + new Modifiers(['public', 'final']), + $this->declare('{ /** @final */ public $fixture; }')->property('fixture')->modifiers() + ); + } + + #[Test, Runtime(php: '>=8.4')] + public function modifiers_declared_final() { + Assert::equals( + new Modifiers(['public', 'final']), + $this->declare('{ public final $fixture; }')->property('fixture')->modifiers() + ); + } + #[Test, Values(['public', 'protected', 'private'])] public function get_modifiers($modifier) { Assert::equals(