-
Notifications
You must be signed in to change notification settings - Fork 824
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
FIX Don't try to access private properties/methods #10670
FIX Don't try to access private properties/methods #10670
Conversation
ec52ef6
to
b199638
Compare
I think we should keep the |
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.
While this may fix the bug, this change doesn't feel quite right
src/Core/CustomMethods.php
Outdated
@@ -147,7 +147,13 @@ protected function registerExtraMethodCallback($name, $callback) | |||
*/ | |||
public function hasMethod($method) | |||
{ | |||
return method_exists($this, $method ?? '') || $this->getExtraMethodConfig($method); | |||
return (method_exists($this, $method ?? '') && !$this->isPrivateMethod($method)) || $this->getExtraMethodConfig($method); |
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 happens when you call $this->hasMethod('somePrivateMethod')
... in a $this
context the method exists and should be callable?
The other PR looks like it made an assmption that set() had to be non private too, which is also possibly wrong. If I remember correctly we first wanted to use is_callable()
, though we couldn't due to the use of __call()
making everything callable
Also I think that method would be misnamed with this .. it should be called `hasNonPrivateMethod()'
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 happens when you call $this->hasMethod('somePrivateMethod') ... in a $this context the method exists and should be callable?
In what scenario will you define a private method in a class and then in that same class use $this->hasMethod('myPrivateMethod')
to see if the method exists? You defined it in that class, of course the method is there. You'd just call it without the condition.
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.
Also I think that method would be misnamed with this .. it should be called `hasNonPrivateMethod()'
I think that's splitting hairs personally. The intention of the method is to see if there's a method there that can be called. If it's private, it can only be called by the class that defined it in which case you wouldn't bother checking if the method is there.
It's only really useful for checking if some external class or this class by virtue of its superclasses defining it has the method.
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.
I feel this change is adding confusing which doesn't need to be there
A quick search in sink for $this->hasMethod
yields 26 results, so I think this concern has some merit
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.
How many of those are checking for private methods inside the classes that defined them?
Regardless, I have altered my approach to this.
The reason I did it this way is so that The alternative is to do |
I have noticed I forgot an important part though - if there's a |
9554291
to
934cd75
Compare
I've reworked the approach to not affect |
Thanks Guy. One example I can think of where hasMethod for a private method may make sense is when using traits in project code. You may not have direct visibility of what's in the trait (in terms of looking at the code of a single file) or when a trait is used (as an author of a trait in a 3rd-party module), and there I would expect hasMethod, regardless of its visibility, to always return true. Just a note as you've changed the approach. |
tests/php/View/ViewableDataTest.php
Outdated
@@ -208,16 +208,47 @@ public function testSetFailover() | |||
$this->assertFalse($container->hasMethod('testMethod'), 'testMethod() incorrectly reported as existing'); | |||
} | |||
|
|||
public function testIsPrivate() | |||
public function testIsPrivateMethod() |
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.
public function testIsPrivateMethod() | |
public function testIsAccessibleMethod() |
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.
Done
tests/php/View/ViewableDataTest.php
Outdated
$this->assertTrue($output, 'Method should be accessible'); | ||
} | ||
|
||
public function testIsPrivateProperty() |
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.
public function testIsPrivateProperty() | |
public function testIsAccessibleProperty() |
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.
Done
if (!method_exists($this, $method)) { | ||
return false; | ||
} | ||
if (static::class === self::class) { |
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.
I may (probably am) misunderstanding this, though won't self::class
always be ViewableData::class
?
Is this what we're trying to do here?
if (!$reflectionMethod->isPrivate()) {
return true;
} else {
// method is private
if (<method is defined on the class that called isAccessibleMethod(), or a trait that's been applied to that class>) {
return true;
} else {
return 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.
won't
self::class
always beViewableData::class
?
Yes. So we're checking if the class of the object is ViewableData
(and not some subclass).
What we're doing is:
- Returning
false
if the method just simply doesn't exist. It's not accessible if it doesn't exist. - Returning
true
if the method exists and the static class isViewableData
- it can always access its own methods. - Returning
false
if the method exists, but the static class is notViewableData
, and the method is private.ViewableData
can't access methods which are private and defined in subclasses. - Returning
true
if the method exists, the static class is notViewableData
, but the method is not private.ViewableData
can access protected and public methods of its subclasses.
The unit tests cover all of these bases, I believe.
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.
Ah my bad, forgot that isAccessibleMethod() is a private method itself so doesn't need to work on subclasses
934cd75
to
14a449f
Compare
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.
MOG
It's green and has two approvals, so self-merging. |
Accidentally broke this in silverstripe#10670
Accidentally broke this in #10670
A partial solution for this was done in #10637 - this PR makes that solution more robust and does the same for properties.
This also allows
ViewableData
to fall back to using the$data
array if there are private properties instead of just noping out.Parent issue