-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Make ReflectionProperty#setAccessible()
and ReflectionMethod#setAccessible()
no-op
#5412
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
Conversation
I'd probably also add a deprecation if |
Similar to #5411 (comment), I would probably open an alternate PR to add the deprecation there. Effectively 3 levels:
|
46751dc
to
f3fb39a
Compare
EDIT: deferred to later RFC about removal of |
@Ocramius imho the dependency on the attribute RFC is not really there, ad it would also trigger the runtime deprecation. Marking the method as deprecated is PHP project convention, so your preference against runtime deprecations is ranked lower here :) |
EDIT: I've explicitly deferred the deprecation to a future RFC: any inter-dependencies and side-effects are valuable/to be discussed for 8.2+. |
…otected` symbols by default With this patch, it is no longer required to call `ReflectionProperty#setAccessible()` or `ReflectionMethod#setAccessible()` with `true`. If a userland consumer already got to the point of accessing object/class information via reflection, it makes little sense for `ext/reflection` to disallow accessing `private`/`protected` symbols by default. After this patch, calling `ReflectionProperty#setAccessible(true)` or `ReflectionMethod#setAccessible(true)` on newly instantiated `ReflectionProperty` or `ReflectionMethod` respectively will have no effect. Full RFC text for the voted upon https://wiki.php.net/rfc/make-reflection-setaccessible-no-op is as follows: ``` ====== PHP RFC: Make reflection setAccessible() no-op ====== * Version: 1.0 * Date: 2021-06-13 * Author: Marco Pivetta * Status: Accepted * First Published at: https://wiki.php.net/rfc/make-reflection-setaccessible-no-op ===== Introduction ===== The `ext-reflection` API is designed to inspect static details of a code-base, as well as reading and manipulating runtime state and calling internal details of objects that are otherwise inaccessible. These methods are most notably: * `ReflectionMethod#invoke(): mixed` * `ReflectionMethod#invokeArgs(mixed ...$args): mixed` * `ReflectionProperty#getValue(object $object): mixed` * `ReflectionProperty#setValue(object $object, mixed $value): void` While breaking encapsulation principles that allow for safe coding practices, these methods are extremely valuable to tools like: * mappers * serializers * debuggers * etc. Infrastructural instrumentation is often required to do things that are in direct conflict with encapsulation itself. The 4 methods listed above change behavior depending on the only mutable state within the scope of `ext-reflection` classes, which is an "accessible" flag. This "accessibility" flag is steered by: * `ReflectionMethod#setAccessible(bool $accessible): void` * `ReflectionProperty#setAccessible(bool $accessible): void` Attempting to use any of the above listed methods without configuring accessibility first will lead to an exception being thrown. For example: <code php> class Foo { private $bar = 'a'; } (new ReflectionProperty(Foo::class, 'bar'))->getValue(); </code> https://3v4l.org/ousrD : <code> Fatal error: Uncaught ReflectionException: Cannot access non-public property Foo::$bar in <SNIP> </code> ==== The problem with mutability ==== By having `ReflectionProperty#setAccessible()` and `ReflectionMethod#setAccessible()`, any consumer of a `ReflectionMethod` or `ReflectionProperty` that is given by a third party must ensure that `#setAccessible()` is called: <code php> function doSomethingWithState(MyObject $o, ReflectionProperty $p) : void { $p->setAccessible(true); // wasteful safety check doSomethingWith($p->getValue($o)); } </code> In addition to that, any developer that is intentionally using the reflection API (after having evaluated its trade-off) will have to use this obnoxious syntax in order to use it at its fullest: <code php> $p = new ReflectionProperty(MyClass::class, 'propertyName'); $p->setAccessible(true); // now $p is usable </code> ===== Proposal ===== This RFC proposes to: * make `ReflectionProperty` and `ReflectionMethod` behave as if `#setAccessible(true)` had been called upfront * make `ReflectionProperty#setAccessible()` and `ReflectionMethod#setAccessible()` no-op operations, with no side-effects nor state mutation involved After the RFC is successfully accepted/implemented, the following code should no longer throw, improving therefore the ergonomics around reflection. <code php> class Foo { private $bar = 'a'; } (new ReflectionProperty(Foo::class, 'bar'))->getValue(); </code> ==== Deprecations ==== In order to ease migration to PHP 8.1, and minimize runtime side-effects, a deprecation is explicitly avoided in this RFC. Instead, a deprecation should be introduced when a new/separate RFC plans for the removal of `ReflectionProperty#setAccessible()` and `ReflectionMethod#setAccessible()`. Such RFC will be raised **after** the release of PHP 8.1, if this RFC is accepted. ===== Backward Incompatible Changes ===== Although of minimal concern, it is true that some behavior will change: * `ReflectionProperty#getValue()` will no longer throw an exception when used against a protected/private property * `ReflectionProperty#setValue()` will no longer throw an exception when used against a protected/private property * `ReflectionMethod#invoke()` will no longer throw an exception when used against a protected/private method * `ReflectionMethod#invokeArgs()` will no longer throw an exception when used against a protected/private method * for extensions developers, `reflection_object->ignore_visibility` no longer exists ===== Proposed PHP Version(s) ===== 8.1 ===== RFC Impact ===== ==== To SAPIs ==== None ==== To Existing Extensions ==== None ==== To Opcache ==== None ==== New Constants ==== None ==== php.ini Defaults ==== None ===== Open Issues ===== None ===== Proposed Voting Choices ===== Accept turning `ReflectionProperty#setAccessible()` and `ReflectionMethod#setAccessible()` into a no-op? (yes/no) ===== Patches and Tests ===== php#5412 ===== Vote ===== This is a Yes/No vote, requiring a 2/3 majority. Voting started on 2021-06-23 and ends on 2021-07-07. <doodle title="Make reflection setAccessible() no-op" auth="ocramius" voteType="single" closed="true"> * Yes * No </doodle> ```
f3fb39a
to
cad43f4
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.
@patrickallaert or @ramsey can you take this to merge into master
and into 8.1.x
?, according to voting results?
Ref:
- https://externals.io/message/115068#115351
- https://marc.info/?l=php-internals&m=162566643315847&w=2
- https://wiki.php.net/rfc/make-reflection-setaccessible-no-op
Waited for green build before pinging :)
…rom PHP 8.1 onwards This will highlight unused code. Ref: php/php-src#5412 Ref: https://wiki.php.net/rfc/make-reflection-setaccessible-no-op Ref: php/php-src#5411 Example https://3v4l.org/PNeeZ ```php <?php class Foo { private $bar = 'baz'; private function taz() { return 'waz'; } } //var_dump((new ReflectionProperty(Foo::class, 'bar'))->getValue(new Foo)); //var_dump((new ReflectionMethod(Foo::class, 'taz'))->invoke(new Foo)); ``` Produces (starting from PHP 8.1): ``` string(3) "baz" string(3) "waz" ```
These calls are no longer needed as per: * https://wiki.php.net/rfc/make-reflection-setaccessible-no-op * php/php-src#5412
These calls are no longer needed as per: * https://wiki.php.net/rfc/make-reflection-setaccessible-no-op * php/php-src#5412
RFC: https://wiki.php.net/rfc/make-reflection-setaccessible-no-op
Follow-up to #5411: also removes the
ReflectionProperty#setAccessible(false)
andReflectionMethod#setAccessible(false)
.Make
ReflectionProperty#setAccessible()
andReflectionMethod#setAccessible()
no-opWith this patch, it is no longer required to call
ReflectionProperty#setAccessible()
orReflectionMethod#setAccessible()
at all: calling these methods will no longer have anyeffect on the behavior of
ReflectionMethod
andReflectionProperty
.If a userland consumer already got to the point of accessing object/class information via reflection,
it makes little sense for
ext/reflection
to disallow accessingprivate
/protected
symbols ever.TODOs