This is an old revision of the document!
PHP RFC: #[Deprecated] Attribute
- Version: 1.0
- Date: 2021-06-13
- Author: Marco Pivetta
- Status: Draft
- 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:
class Foo { private $bar = 'a'; } (new ReflectionProperty(Foo::class, 'bar'))->getValue();
Fatal error: Uncaught ReflectionException: Cannot access non-public property Foo::$bar in <SNIP>
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:
function doSomethingWithState(MyObject $o, ReflectionProperty $p) : void { $p->setAccessible(true); // wasteful safety check doSomethingWith($p->getValue($o)); }
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:
$p = new ReflectionProperty(MyClass::class, 'propertyName'); $p->setAccessible(true); // now $p is usable
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
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
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?