Skip to content

Instantly share code, notes, and snippets.

@goodevilgenius
Last active September 2, 2021 21:27
Show Gist options
  • Select an option

  • Save goodevilgenius/c34b5027dc5d6fe6677d81b32bdd397d to your computer and use it in GitHub Desktop.

Select an option

Save goodevilgenius/c34b5027dc5d6fe6677d81b32bdd397d to your computer and use it in GitHub Desktop.
Class to access private/protected methods/properties without Reflections. Requires PHP 8 (could be rewritten to support 7)
<?php
declare(strict_types=1);
class ClassProxy
{
protected ?string $class;
protected ?object $object;
protected Closure $caller;
protected Closure $getter;
protected Closure $setter;
public function __construct($classOrObject)
{
[$this->object, $this->class] = is_object($classOrObject) ?
[$classOrObject, get_class($classOrObject)] :
(
is_string($classOrObject) && class_exists($classOrObject) ?
[null, $classOrObject] :
[null, null]
);
$this->initCaller();
$this->initGetter();
$this->initSetter();
}
protected function initCaller(): void
{
$this->caller = (
$this->object ?
fn (string $method, array $args) => $this->$method(...$args) :
fn (string $method, array $args) => static::$method($args)
)->bindTo($this->object, $this->class);
}
protected function initGetter(): void
{
$this->getter = (
$this->object ?
fn (string $key) => $this->$key :
fn (string $key) => static::$$key
)->bindTo($this->object, $this->class);
}
protected function initSetter(): void
{
$this->setter = (
$this->object ?
fn (string $key, $value) => $this->$key = $value :
fn (string $key, $value) => static::$$key = $value
)->bindTo($this->object, $this->class);
}
public function call(callable $cb, ...$args)
{
return (\Closure::fromCallable($cb)->bindTo($this->object, $this->class))(...$args);
}
public function __call(string $method, array $args)
{
return ($this->caller)($method, $args);
}
public function __get(string $key)
{
return ($this->getter)($key);
}
public function __set(string $key, $value)
{
return ($this->setter)($key, $value);
}
}
function spy($classOrObject): ClassProxy
{
return new ClassProxy($classOrObject);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment