diff --git a/src/main/php/lang/reflection/Modifiers.class.php b/src/main/php/lang/reflection/Modifiers.class.php index 05c60e9..faea9dc 100755 --- a/src/main/php/lang/reflection/Modifiers.class.php +++ b/src/main/php/lang/reflection/Modifiers.class.php @@ -1,6 +1,6 @@ self::IS_PUBLIC, - 'protected' => self::IS_PROTECTED, - 'private' => self::IS_PRIVATE, - 'static' => self::IS_STATIC, - 'final' => self::IS_FINAL, - 'abstract' => self::IS_ABSTRACT, - 'native' => self::IS_NATIVE, - 'readonly' => self::IS_READONLY, + 'public' => self::IS_PUBLIC, + 'protected' => self::IS_PROTECTED, + 'private' => self::IS_PRIVATE, + 'static' => self::IS_STATIC, + 'final' => self::IS_FINAL, + 'abstract' => self::IS_ABSTRACT, + 'native' => self::IS_NATIVE, + 'readonly' => self::IS_READONLY, + 'private(set)' => self::IS_PRIVATE_SET, + 'protected(set)' => self::IS_PROTECTED_SET, + 'public(set)' => self::IS_PUBLIC_SET, ]; private $bits; @@ -103,19 +109,55 @@ public function isAbstract() { return 0 !== ($this->bits & self::IS_ABSTRACT); } public function isFinal() { return 0 !== ($this->bits & self::IS_FINAL); } /** @return bool */ - public function isPublic() { return 0 !== ($this->bits & self::IS_PUBLIC); } + public function isNative() { return 0 !== ($this->bits & self::IS_NATIVE); } /** @return bool */ - public function isProtected() { return 0 !== ($this->bits & self::IS_PROTECTED); } + public function isReadonly() { return 0 !== ($this->bits & self::IS_READONLY); } - /** @return bool */ - public function isPrivate() { return 0 !== ($this->bits & self::IS_PRIVATE); } + /** + * Gets whether these modifiers are public in regard to the specified hook + * + * @param ?string $hook + * @return bool + * @throws lang.IllegalArgumentException + */ + public function isPublic($hook= 'get') { + switch ($hook) { + case 'get': return 0 !== ($this->bits & self::IS_PUBLIC); + case 'set': return 0 !== ($this->bits & self::IS_PUBLIC_SET); + default: throw new IllegalArgumentException('Unknown hook '.$hook); + } + } - /** @return bool */ - public function isNative() { return 0 !== ($this->bits & self::IS_NATIVE); } + /** + * Gets whether these modifiers are protected in regard to the specified hook + * + * @param ?string $hook + * @return bool + * @throws lang.IllegalArgumentException + */ + public function isProtected($hook= 'get') { + switch ($hook) { + case 'get': return 0 !== ($this->bits & self::IS_PROTECTED); + case 'set': return 0 !== ($this->bits & self::IS_PROTECTED_SET); + default: throw new IllegalArgumentException('Unknown hook '.$hook); + } + } - /** @return bool */ - public function isReadonly() { return 0 !== ($this->bits & self::IS_READONLY); } + /** + * Gets whether these modifiers are private in regard to the specified hook + * + * @param ?string $hook + * @return bool + * @throws lang.IllegalArgumentException + */ + public function isPrivate($hook= 'get') { + switch ($hook) { + case 'get': return 0 !== ($this->bits & self::IS_PRIVATE); + case 'set': return 0 !== ($this->bits & self::IS_PRIVATE_SET); + default: throw new IllegalArgumentException('Unknown hook '.$hook); + } + } /** * Compares a given value to this modifiers instance diff --git a/src/test/php/lang/reflection/unittest/ModifiersTest.class.php b/src/test/php/lang/reflection/unittest/ModifiersTest.class.php index 7ca9975..5b62355 100755 --- a/src/test/php/lang/reflection/unittest/ModifiersTest.class.php +++ b/src/test/php/lang/reflection/unittest/ModifiersTest.class.php @@ -15,6 +15,9 @@ private function cases() { yield [Modifiers::IS_PRIVATE, 'private']; yield [Modifiers::IS_NATIVE, 'native']; yield [Modifiers::IS_READONLY, 'readonly']; + yield [Modifiers::IS_PRIVATE_SET, 'private(set)']; + yield [Modifiers::IS_PROTECTED_SET, 'protected(set)']; + yield [Modifiers::IS_PUBLIC_SET, 'public(set)']; yield [Modifiers::IS_FINAL | Modifiers::IS_PUBLIC, 'public final']; yield [Modifiers::IS_ABSTRACT | Modifiers::IS_PUBLIC, 'public abstract']; yield [Modifiers::IS_ABSTRACT | Modifiers::IS_PROTECTED, 'protected abstract']; @@ -83,6 +86,36 @@ public function isReadonly($input, $expected) { Assert::equals($expected, (new Modifiers($input))->isReadonly()); } + #[Test, Values([['public(set)', true], ['public', true]])] + public function isPublicGet($input, $expected) { + Assert::equals($expected, (new Modifiers($input))->isPublic('get')); + } + + #[Test, Values([['protected(set)', false], ['protected', true]])] + public function isProtectedGet($input, $expected) { + Assert::equals($expected, (new Modifiers($input))->isProtected('get')); + } + + #[Test, Values([['private(set)', false], ['private', true]])] + public function isPrivateGet($input, $expected) { + Assert::equals($expected, (new Modifiers($input))->isPrivate('get')); + } + + #[Test, Values([['public(set)', true], ['public', false]])] + public function isPublicSet($input, $expected) { + Assert::equals($expected, (new Modifiers($input))->isPublic('set')); + } + + #[Test, Values([['protected(set)', true], ['protected', false]])] + public function isProtectedSet($input, $expected) { + Assert::equals($expected, (new Modifiers($input))->isProtected('set')); + } + + #[Test, Values([['private(set)', true], ['private', false]])] + public function isPrivateSet($input, $expected) { + Assert::equals($expected, (new Modifiers($input))->isPrivate('set')); + } + #[Test] public function public_modifier_default_no_arg() { Assert::true((new Modifiers())->isPublic()); diff --git a/src/test/php/lang/reflection/unittest/PropertiesTest.class.php b/src/test/php/lang/reflection/unittest/PropertiesTest.class.php index 8576b59..1c349fc 100755 --- a/src/test/php/lang/reflection/unittest/PropertiesTest.class.php +++ b/src/test/php/lang/reflection/unittest/PropertiesTest.class.php @@ -227,4 +227,13 @@ public function set_accessing_failed_exceptions_target_member() { Assert::equals($t->property('fixture'), $expected->target()); } } + + #[Test, Runtime(php: '>=8.4')] + public function asymmetric_visibility() { + $t= $this->declare('{ public private(set) string $fixture; }'); + Assert::equals( + 'public private(set) string $name', + $t->property('fixture')->toString() + ); + } } \ No newline at end of file