PHP 8.1 : Accessing private/protected properties & methods via reflection API is now allowed without calling setAccessible()
16, October 2021

PHP has a set of reflection APIs that can to used to introspect classes, interfaces, functions, methods and extensions. Among many other feature, reflection allows us to access class properties and invoke class methods even if they are declared as private/protected. I heavily use it to write unit tests for private methods in some legacy project where testing public methods is too complex.

So, how does one do it?

class Foo {
    private $bar = 'a';
    
    protected function call() {
        return 'bazz';
    }
}


$obj = new Foo;

// lets create reflection for private $bar property
$reflectedProperty = new ReflectionProperty(Foo::class, 'bar');

// set it as accessible
$reflectedProperty->setAccessible(true);

// now we can read the value of the property
echo ($reflectedProperty)->getValue($obj);

// now reflection for protected call() method
$reflectedMethod = new ReflectionMethod(Foo::class, 'call');

// set it as accessible
$reflectedMethod->setAccessible(true);

// now we can invoke/call that method as if it is public method
echo $reflectedMethod->invoke($obj); 


As we can see, to access a private/protected property or method, we need to call setAccessible(true) method before accessing. If we do not call this method, we will get an exception:

Uncaught ReflectionException: Trying to invoke protected method Foo::call() from scope ReflectionMethod

It is bit annoying because if we are receiving ReflectionProperty or ReflectionMethod object from any third-party library or module, we don't know if setAccessible() has been called on that object or not. So to be extra safe, we would need to call setAccessible() again.


PHP 8.1 solves that issue.

From now on, we can access property or invoke method via reflection API without any need to call setAccessible() explicitly, even if they are protected/private.

Any time we try to access private/protected property or invoke a method, it will behave as if setAccessible(true) had been called upfront.


$obj = new Foo;

$reflectedProperty = new ReflectionProperty(Foo::class, 'bar');

// no need for this anymore
// $reflectedProperty->setAccessible(true);

echo ($reflectedProperty)->getValue($obj); // works fine for property

$reflectedMethod = new ReflectionMethod(Foo::class, 'call');

// no need for this anymore
// $reflectedMethod->setAccessible(true);

echo $reflectedMethod->invoke($obj); // works fine to invoke method too


RFC: https://wiki.php.net/rfc/make-reflection-setaccessible-no-op