Last active
November 18, 2022 23:58
-
-
Save johnkary/904de9171396909c1842 to your computer and use it in GitHub Desktop.
Exposing protected and private object methods using \Closure::bindTo(). Code below requires PHP 5.6+ due to argument unpacking (...$args). Could probably be written for PHP 5.4+ but didn't want to reinvent argument unpacking, else could also just use the Reflection implementation.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
class User { | |
private $firstname; | |
private $lastname; | |
public function __construct($f, $l) { | |
$this->firstname = $f; | |
$this->lastname = $l; | |
} | |
private function buildFullName() { | |
return sprintf('%s %s', $this->firstname, $this->lastname); | |
} | |
private function greeting($say) { | |
return sprintf('%s, %s', $say, $this->buildFullName()); | |
} | |
private function greetingToUser($say, User $user) { | |
return sprintf('%s from %s', $this->greeting($say), $user->buildFullName()); | |
} | |
} | |
/** | |
* Exposes result of a protected/private method call outside the | |
* object's scope. | |
* | |
* @param object $object Object on which to call method | |
* @param string $method Method to call | |
* @param array $args Arguments to be unpacked and passed to Method | |
* @return mixed Result of invoking Method on Object given Arguments | |
*/ | |
function xray($object, $methodToExpose, array $args = []) { | |
$fn = function () use ($methodToExpose, $args) { | |
return $this->$methodToExpose(...$args); | |
}; | |
$exposed = $fn->bindTo($object, $object); | |
return $exposed(); | |
}; | |
// The traditional way | |
function xray_reflection($object, $methodToExpose, array $args = []) { | |
$m = new \ReflectionMethod($object, $methodToExpose); | |
$m->setAccessible(true); | |
return $m->invoke($object, ...$args); | |
}; | |
$john = new User('John', 'Kary'); | |
$chris = new User('Chris', 'Escalante'); | |
echo implode("\n", [ | |
xray($john, 'greeting', ['Hello']), | |
xray_reflection($chris, 'greetingToUser', ['Hello', $john]), | |
]); | |
// Output: | |
// Hello, John Kary | |
// Hello, Chris Escalante from John Kary |
Sorry, I'm late to the party but this actually helped me solve a problem that Reflection didn't.
I have a class extending another and I needed to call a protected method that belongs to the parent's class using the child's object. Reflection only allows you to call methods using the object the method was declared in so it was not working.
Your method works also with the child object which solved my problem.
Thanks for this!
I ran into the problem of the (new ReflectionMethod($object, 'method'))->invoke(...)
method not respecting declare(strict_types=1)
whereas this Closure method does 👍
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
replace:
with:
and you have it.