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