Skip to content

Instantly share code, notes, and snippets.

@johnkary
Last active November 18, 2022 23:58
Show Gist options
  • Select an option

  • Save johnkary/904de9171396909c1842 to your computer and use it in GitHub Desktop.

Select an option

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.
<?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
@zanbaldwin
Copy link

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