Skip to content

Instantly share code, notes, and snippets.

@jm42
Created August 16, 2014 18:53
Show Gist options
  • Save jm42/e9fda32bc1ff0f818c30 to your computer and use it in GitHub Desktop.
Save jm42/e9fda32bc1ff0f818c30 to your computer and use it in GitHub Desktop.
Aspect Stub
<?php
class Wallet {
protected $balance;
public function __construct($initial=0.0) {
$this->balance = $initial;
}
public function add($money) {
$this->balance += $money;
}
public function getBalance() {
return $this->balance;
}
}
##############################################################################
class Invocation {
public $instance;
public $method;
public $args;
public function __construct($instance, \ReflectionMethod $method, array $args) {
$this->instance = $instance;
$this->method = $method;
$this->args = $args;
}
public function getParam($index) {
return $this->args[$index];
}
public function proceed() {
return $this->method->invokeArgs($this->instance, $this->args);
}
}
interface Pointcut {
function matchesClass(\ReflectionClass $class);
function matchesMethod(\ReflectionMethod $method);
}
interface Advice {
function before(Invocation $invocation);
function after(Invocation $invocation);
}
interface Aspect {
function getPointcut();
function getAdvice();
}
##############################################################################
class WalletPointcut implements Pointcut {
public function matchesClass(\ReflectionClass $class) {
return $class->getName() === 'Wallet';
}
public function matchesMethod(\ReflectionMethod $method) {
return $method->getName() === 'add';
}
}
class WalletAdvice implements Advice {
public function before(Invocation $invocation) {
echo 'ADD ' . $invocation->getParam(0) .
' to ' . $invocation->instance->getBalance() . PHP_EOL;
}
public function after(Invocation $invocation) {}
}
class WalletAspect implements Aspect {
public function getPointcut() {
return new WalletPointcut();
}
public function getAdvice() {
return new WalletAdvice();
}
}
##############################################################################
class Mock {
public $target;
public function __construct($target) {
$this->target = $target;
}
}
class Stub {
public $refl;
public function __construct($target) {
$this->refl = new \ReflectionClass($target);
$this->obj = $this->refl->newInstanceArgs(array_slice(func_get_args(), 1));
}
public function __call($name, $args) {
return $this->refl->getMethod($name)->invokeArgs($this->obj, $args);
}
}
class AspectStub extends Stub {
public $aspects = array();
public function addAspect(Aspect $aspect) {
if ($aspect->getPointcut()->matchesClass($this->refl)) {
$this->aspects[] = $aspect;
}
}
public function __call($name, $args) {
$method = $this->refl->getMethod($name);
foreach ($this->aspects as $aspect) {
if ($aspect->getPointcut()->matchesMethod($method)) {
$advice = $aspect->getAdvice();
$invocation = new Invocation($this->obj, $method, $args);
$advice->before($invocation);
$return = $invocation->proceed();
$advice->after($invocation);
return $return;
}
}
return $method->invokeArgs($this->obj, $args);
}
}
##############################################################################
$w = new AspectStub('Wallet', 0.33); // new Wallet(0.33);
$w->addAspect(new WalletAspect());
$w->add(1.66);
print($w->getBalance() . PHP_EOL);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment