Вызов защищенных и приватных методов
Если вам понадобился данный трюк, то, скорее всего, вы имеете проблему в архитектуре вашего класса/приложения. Его использование — это крайняя мера.
Лучше всего тестировать приватные свойства и методы через публичные методы. Использование данного трюка оправдано в случае экономии времени и при полном понимании зачем вы это делаете.
Пример тестируемого класса:
Чтобы прочитать свойство $private_property на другое, нам поможет ReflectionMethod . Создаем метод run_inaccesible_method :
private function run_inaccesible_method( $object, string $method_name, array $args = [] ) < $method = new ReflectionMethod( $object, $method_name ); $method->setAccessible( true ); $result = $method->invokeArgs( $object, $args ); $method->setAccessible( false ); return $result; >
Разберем работу метода построчно:
- С помощью ReflectionMethod получаем в виде объекта метод idea объекта класса Duck ;
- Делаем метод доступным для вызова;
- Вызываем метод run_inaccesible_method объекта класса Duck с помощью метода invokeArgs ;
- Делаем метод недоступным для вызова;
- И возвращаем результат.
Пример теста полностью:
use PHPUnit\Framework\TestCase; class Test_Duck extends TestCase < private function run_inaccesible_method( $object, string $method_name, array $args = [] ) < $method = new ReflectionMethod( $object, $method_name ); $method->setAccessible( true ); $result = $method->invokeArgs( $object, $args ); $method->setAccessible( false ); return $result; > public function test_private_method() < $duck = new Duck(); $this->assertSame( 'I want to do quack-quack', $this->run_inaccesible_method( $duck, 'idea', [ 'quack-quack' ] ) ); > >
Accessing private methods and properties outside of class using reflection in PHP
In PHP, the visibility of a property, a method, or a constant can be defined by prefixing the declaration using keywords public , protected or private . Here is how these modifiers work.
- public — Class members declared public can be accessed everywhere.
- protected — Class members declared protected can be accessed only within the class itself and by inheriting and parent classes.
- private — Class members declared as private may only be accessed by the class that defines the member.
Generally, the class members are declared private for certain reasons. One is to implement encapsulation which makes the class members available inside of the originating class only i.e. to hide data from the user of the class and can only be modified using public getter and setter methods.
But in certain scenarios, you might want to access these private members outside of the class. There’s a workaround in PHP using which you can do so. First, let’s check how to access private methods.
Accessing private methods
First, check the following class.
class Foo private function privateMethod() return 'Hogwarts'; > >
As you can see, the privateMethod is a private method and if we want to access it outside of the class like so, we would get a fatal error.
$foo = new Foo; $foo->privateMethod(); // Fatal error: Uncaught Error: Call to private method // Foo::privateMethod() from context
To get around this, we can use PHP’s in-built ReflectionMethod class which can give handlful of information about the class. And also can “reverse engineer” things for us.
Basically, it shows the “mirror” to the class so that it can learn about itself. And here’s how you can use it.
$reflectionMethod = new ReflectionMethod('Foo', 'privateMethod'); $reflectionMethod->setAccessible(true); echo $reflectionMethod->invoke(new Foo); // Hogwarts
As you can see, the ReflectionMethod constructor accepts two parameters: “Class name” and “Method name”. In our case, we passed in Foo as the class name and privateMethod method name as we want to access this method.
Next, we’ll need to make the private method accessible outside of the class. For this, we have used the setAccessible method on the object and set it to true . This will allow protected and private methods to be invoked on-the-fly.
And lastly, we can invoke the method using the invoke method on the object and passing in the object of the class ( new Foo ) as its only parameter for which we’re accessing the method.
And that is how you can access a private method of the class.
Accessing private properties
Similarly, you can also access the private properties of the class but the only distinction here is instead of using ReflectionMethod , we’d need to used ReflectionProperty class like so.
class Foo private $privateProperty = 'Harry Potter!'; > $property = new ReflectionProperty('Foo', 'privateProperty'); $property->setAccessible(true); echo $property->getValue(new Foo); // Harry Potter!
As you can see, in this case, we’ve used getValue to fetch the value of the property.
Learn the fundamentals of PHP 8 (including 8.1 and 8.2), the latest version of PHP, and how to use it today with my new book PHP 8 in a Nutshell. It’s a no-fluff and easy-to-digest guide to the latest features and nitty-gritty details of PHP 8. So, if you’re looking for a quick and easy way to PHP 8, this is the book for you.
👋 Hi there! I’m Amit. I write articles about all things web development. If you like what I write and want me to continue doing the same, I would like you buy me some coffees. I’d highly appreciate that. Cheers!
Call private methods php
В комментариях к заметке о вызове private метода через Reflection Roman верно подметил, что можно использовать вместо Reflection анонимные функции. Вот только его вариант работает с PHP7.
Оказывается, можно проделать такое чуть иначе и в PHP 5.4:
class PrivacyViolator private $caller; function __construct() $this->caller = function ($method, $args) return call_user_func_array([$this, $method], $args); >; > function callPrivateMethod($object, $method, $args) return $this->caller->bindTo($object, $object)->__invoke($method, $args); > > $myObject = new MyClass(); $privacyViolator = new PrivacyViolator(); $privacyViolator->callPrivateMethod($myObject, 'hello', ['world']);
Комментарии RSS по email OK
Calling private/protected PHP methods
Generally, private or protected methods should not be accessible outside the class. But if you’re writing a unit test, you can break this rule.
Given PHP class with private method:
class Foo private function bar(): string return 'baz'; > >
Reflection
Use Reflection to call the method outside the class:
$reflectionClass = new ReflectionClass(Foo::class); $reflectionMethod = $reflectionClass->getMethod('bar'); $reflectionMethod->setAccessible(true); $reflectionMethod->invoke(new Foo()); // 'baz'
Reflection works for both private and protected methods.
Alternatively, this can be refactored and simplified to ReflectionMethod :
$reflectionMethod = new ReflectionMethod(Foo::class, 'bar'); $reflectionMethod->setAccessible(true); $reflectionMethod->invoke(new Foo()); // 'baz'
$args = [1, 2, 3]; $reflectionMethod->invokeArgs(new Foo(), $args);
Here’s a reusable function that calls a class instance method:
/** * Calls object method with arguments. * * @param object $object * @param string $method * @param array $args * @return mixed */ function callObjectMethod(object $object, string $method, array $args = []) $reflectionMethod = new ReflectionMethod(get_class($object), $method); $reflectionMethod->setAccessible(true); return $reflectionMethod->invokeArgs($object, $args); >
This means you can call Foo::bar :
callObjectMethod(new Foo(), 'bar'); // 'baz'
Demo
Please support this site and join our Discord!