Private and protected methods should not be accessed outside the permissible scope of class or subclasses. But there are some situations where this is necessary, for me in some contexts of automated testing (unit or integration), or in others where I am implementing a feature and want to perform punctual executions to verify behaviors.
Regardless of why, and the good practice guideline in which I would claim, do not call private or protected methods outside of their scopes, several languages provide support for this possibility.
In PHP and Java, for example, we can use Reflections
(routines that allow us to explore classes structurally).
Example of access to protected methods in PHP
Let’s check out how to run a protected method with private or protected visibility in the PHP language (which can be used in frameworks like Laravel and Symphony).
Our example class is:
class Employee
{
private $salary = 1000;
private function calcBonus()
{
return $this->salary * 0.1;
}
}
An Employee
class with a private method capable of calculating the value of the employee’s bonus.
Using Reflection with PHP
With Reflection we will create a reflection class from the Employee
class to extract the calcBonus
private
method and execute it in sequence:
$reflectionClass = new ReflectionClass(Employee::class);
$reflectionMethod = $reflectionClass->getMethod('calcBonus');
$reflectionMethod->setAccessible(true);
$reflectionMethod->invoke(new Employee()); // '100'
This strategy will work with private or protected methods.
We can refactor this code and access with reflection the desired method without having to instantiate a reflection of the complete class:
$reflectionMethod = new ReflectionMethod(Employee::class, 'calcBonus');
$reflectionMethod->setAccessible(true);
$reflectionMethod->invoke(new Employee()); // '100'
If you need to pass arguments to the method, instead of invoke
use ‘ReflectionMethod::invokeArgs’.
Let’s refactor our class so that the calcBonus method
receives the calculation factor:
class Employee
{
private $salary = 1000;
private function calcBonus($factor)
{
return $this->salary * $factor;
}
}
Then we can execute the method by defining the arguments by passing them as an array, in this case even passing a single argument we should send inside the array:
$args = [0.1];
$reflectionMethod->invokeArgs(new Employee(), $args);
Helper funciton to execute protected methods
Finally, add this function as a helper function
in your toolbox, making it easier to run protected and private
methods in PHP in the future:
/**
* Calls object method with arguments.
*
* @param object $object
* @param string $method
* @param array $args
* @return mixed
*/
function callProtectedMethod(object $object, string $method, array $args = [])
{
$reflectionMethod = new ReflectionMethod(get_class($object), $method);
$reflectionMethod->setAccessible(true);
return $reflectionMethod->invokeArgs($object, $args);
}
Run the helper function as in the following example:
callProtectedMethod(new Employee(), 'calcBonus'); // without arguments
//or
callProtectedMethod(new Employee(), 'calcBonus', [0.2]); // with arguments
Final considerations
If you are testing to validate private methods in classes, it is likely that you have made some mistake in the architecture of this code. Still, this possibility is not ruled out in a strategy I use on a day-to-day basis: I build private methods being executed by direct calls to validate my logic in complexities not yet mastered of the business rule. By doing this I create the possibility to speed up development, and subsequently pull this test code out of my final codebase.
Think about the strategy of use and apply whenever the returned benefits are greater than the pure concept of the technique.
Comments