-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Implement ReflectionProperty::is{Readable,Writable}() #16209
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: PHP-8.4
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -569,6 +569,10 @@ public function hasHook(PropertyHookType $type): bool {} | |
public function getHook(PropertyHookType $type): ?ReflectionMethod {} | ||
|
||
public function isFinal(): bool {} | ||
|
||
public function isReadable(?string $scope = 'static'): bool {} | ||
|
||
public function isWritable(object $object, ?string $scope = 'static'): bool {} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that object should be a nullable optional second parameter, because a property might be static. Also non-readonly properties can be checked without an object. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point. |
||
} | ||
|
||
/** @not-serializable */ | ||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
--TEST-- | ||
Test ReflectionProperty::isReadable() | ||
--FILE-- | ||
<?php | ||
|
||
class A {} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would have added some tests for traits. For example, a test ensuring that protected properties from a shared trait are not accessible (https://3v4l.org/hM5TM). Also a trait must not be accepted as a scope.
This comment was marked as duplicate.
Sorry, something went wrong. |
||
|
||
class B extends A { | ||
public $a; | ||
protected $b; | ||
private $c; | ||
public protected(set) int $d; | ||
public $e { get => 42; } | ||
public $f { set {} } | ||
} | ||
|
||
class C extends B {} | ||
|
||
$test = static function ($scope) { | ||
$rc = new ReflectionClass(B::class); | ||
foreach ($rc->getProperties() as $rp) { | ||
echo $rp->getName() . ' from ' . ($scope ?? 'global') . ': '; | ||
var_dump($rp->isReadable($scope)); | ||
} | ||
}; | ||
|
||
foreach (['A', 'B', 'C'] as $scope) { | ||
$test($scope); | ||
$test->bindTo(null, $scope)('static'); | ||
} | ||
|
||
$test(null); | ||
$test->bindTo(null, null)('static'); | ||
|
||
?> | ||
--EXPECT-- | ||
a from A: bool(true) | ||
b from A: bool(true) | ||
c from A: bool(false) | ||
d from A: bool(true) | ||
e from A: bool(true) | ||
f from A: bool(false) | ||
a from static: bool(true) | ||
b from static: bool(true) | ||
c from static: bool(false) | ||
d from static: bool(true) | ||
e from static: bool(true) | ||
f from static: bool(false) | ||
a from B: bool(true) | ||
b from B: bool(true) | ||
c from B: bool(true) | ||
d from B: bool(true) | ||
e from B: bool(true) | ||
f from B: bool(false) | ||
a from static: bool(true) | ||
b from static: bool(true) | ||
c from static: bool(true) | ||
d from static: bool(true) | ||
e from static: bool(true) | ||
f from static: bool(false) | ||
a from C: bool(true) | ||
b from C: bool(true) | ||
c from C: bool(false) | ||
d from C: bool(true) | ||
e from C: bool(true) | ||
f from C: bool(false) | ||
a from static: bool(true) | ||
b from static: bool(true) | ||
c from static: bool(false) | ||
d from static: bool(true) | ||
e from static: bool(true) | ||
f from static: bool(false) | ||
a from global: bool(true) | ||
b from global: bool(false) | ||
c from global: bool(false) | ||
d from global: bool(true) | ||
e from global: bool(true) | ||
f from global: bool(false) | ||
a from static: bool(true) | ||
b from static: bool(false) | ||
c from static: bool(false) | ||
d from static: bool(true) | ||
e from static: bool(true) | ||
f from static: bool(false) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
--TEST-- | ||
Test ReflectionProperty::isWritable() | ||
--FILE-- | ||
<?php | ||
|
||
class A { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why this test does not check protected properties from parent/child classes? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because the impl uses the same code for checking scope as read, so it's somewhat redundant. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Tests should not assume any implementation details, because implementation might change later, but the tests should detect regression no matter how things are implemented. |
||
public $a; | ||
protected $b; | ||
private $c; | ||
public protected(set) int $d; | ||
public $e { get => 42; } | ||
public $f { set {} } | ||
public readonly int $g; | ||
public private(set) int $h; | ||
|
||
public function setG($g) { | ||
$this->g = $g; | ||
} | ||
|
||
public function __clone() { | ||
$rp = new ReflectionProperty(A::class, 'g'); | ||
echo $rp->getName() . ' from static (initialized, clone): '; | ||
var_dump($rp->isWritable($this)); | ||
} | ||
} | ||
|
||
$test = static function ($scope) { | ||
$rc = new ReflectionClass(A::class); | ||
foreach ($rc->getProperties() as $rp) { | ||
echo $rp->getName() . ' from ' . ($scope ?? 'global') . ': '; | ||
var_dump($rp->isWritable(new A(), $scope)); | ||
|
||
if ($rp->name == 'g') { | ||
$a = new A(); | ||
$a->setG(42); | ||
echo $rp->getName() . ' from ' . ($scope ?? 'global') . ' (initialized): '; | ||
var_dump($rp->isWritable($a, $scope)); | ||
clone $a; | ||
} | ||
} | ||
}; | ||
|
||
$test('A'); | ||
$test->bindTo(null, 'A')('static'); | ||
|
||
$test(null); | ||
$test->bindTo(null, null)('static'); | ||
|
||
?> | ||
--EXPECT-- | ||
a from A: bool(true) | ||
b from A: bool(true) | ||
c from A: bool(true) | ||
d from A: bool(true) | ||
e from A: bool(false) | ||
f from A: bool(true) | ||
g from A: bool(true) | ||
g from A (initialized): bool(false) | ||
g from static (initialized, clone): bool(true) | ||
h from A: bool(true) | ||
a from static: bool(true) | ||
b from static: bool(true) | ||
c from static: bool(true) | ||
d from static: bool(true) | ||
e from static: bool(false) | ||
f from static: bool(true) | ||
g from static: bool(true) | ||
g from static (initialized): bool(false) | ||
g from static (initialized, clone): bool(true) | ||
h from static: bool(true) | ||
a from global: bool(true) | ||
b from global: bool(false) | ||
c from global: bool(false) | ||
d from global: bool(false) | ||
e from global: bool(false) | ||
f from global: bool(true) | ||
g from global: bool(false) | ||
g from global (initialized): bool(false) | ||
g from static (initialized, clone): bool(true) | ||
h from global: bool(false) | ||
a from static: bool(true) | ||
b from static: bool(false) | ||
c from static: bool(false) | ||
d from static: bool(false) | ||
e from static: bool(false) | ||
f from static: bool(true) | ||
g from static: bool(false) | ||
g from static (initialized): bool(false) | ||
g from static (initialized, clone): bool(true) | ||
h from static: bool(false) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thinking about this some more, it would likely be beneficial to also accept the object here and check it for
IS_UNDEF
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What
IS_UNDEF
means in userland?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unset()
properties or typed properties that have not been assigned to.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then yes, it would be great to have an object parameter here.