Skip to content

Instantly share code, notes, and snippets.

@fundon
Created July 6, 2011 09:40
Show Gist options
  • Save fundon/1066919 to your computer and use it in GitHub Desktop.
Save fundon/1066919 to your computer and use it in GitHub Desktop.
PHP, Aspect Oriented Programming(AOP). @author: wuzhenyu
<?php
function e($str){echo $str , "\n";}
class AOP
{
static function getInstance()
{
$args = func_get_args();
$class = array_shift($args);
$obj = new $class();
while ($advice = array_shift($args))
{
$obj = new self($obj, $advice);
}
return $obj;
}
private $instance;
private $func;
private $params;
/**
* @var Advice $advice
*/
private $advice;
function __construct($obj, $advice)
{
$this->instance = $obj;
$this->advice = $advice;
}
function __call($func, $params)
{
$this->func = $func;
$this->params = $params;
try
{
try
{
$ret = $this->advice->aroundAdvice($this);
return $ret;
}
catch(AdviceAroundException $e)
{
$this->advice->beforeAdvice();
$ret = $this->proceed();
$this->advice->afterAdvice();
return $ret;
}
}
catch(Exception $e)
{
return $this->advice->throwAdvice($e);
}
}
function proceed()
{
return call_user_func_array(array($this->instance, $this->func), $this->params);
}
}
class AdviceAroundException extends Exception{}
abstract class Advice
{
abstract function beforeAdvice();
abstract function afterAdvice();
function aroundAdvice(AOP $aop)
{
throw new AdviceAroundException();
}
function throwAdvice(Exception $e)
{
return $e;
}
}
class Log extends Advice
{
function beforeAdvice()
{
e('log before');
}
function afterAdvice()
{
e('log after');
}
function aroundAdvice(AOP $aop)
{
e('log around1');
$aop->proceed();
e('log around2');
}
function throwAdvice(Exception $e)
{
e('log throws');
}
}
class Cache extends Advice
{
function beforeAdvice()
{
e('cache before');
}
function afterAdvice()
{
e('cache after');
}
// function aroundAdvice(AOP $aop)
// {
// e('cache around1');
// $aop->proceed();
// e('cache around2');
// }
function throwAdvice(Exception $e)
{
e('cache throws');
}
}
class Permission extends Advice
{
function beforeAdvice()
{
e('permission before');
}
function afterAdvice()
{
e('permission after');
}
// function aroundAdvice(AOP $aop)
// {
// e('permission around1');
// $aop->proceed();
// e('permission around2');
//
//// if (false)
//// {
//// // pass
//// $aop->proceed();
//// }
//// else
//// {
//// throw new Exception('no permission');
//// }
// }
function throwAdvice(Exception $e)
{
return $e;
}
}
class User
{
function login()
{
e('login');
}
}
$user = AOP::getInstance('user', new Cache(), new Log(), new Permission());
$ret = $user->login();
//var_dump($ret);
<?php
function e($str){echo $str , "\n";}
class AOP
{
static function getInstance($class, $advice = null)
{
$obj = new $class();
/**
* @var Advice $advice
*/
do
{
$obj = new self($obj, $advice);
}while ($advice = $advice->getPreAdvice());
return $obj;
}
/**
* @var AOP
*/
private $instance;
private $func;
private $params;
/**
* @var Advice $advice
*/
private $advice;
function __construct($obj, $advice)
{
$this->instance = $obj;
$this->advice = $advice;
}
function __call($func, $params)
{
$this->func = $func;
$this->params = $params;
try
{
try
{
$ret = $this->advice->aroundAdvice($this);
return $ret;
}
catch(AdviceAroundException $e)
{
$this->advice->beforeAdvice();
$ret = $this->proceed();
$this->advice->afterAdvice();
return $ret;
}
}
catch(Exception $e)
{
return $this->advice->throwAdvice($e);
}
}
public $result;
function setAdviceResult($ar)
{
$this->result = $ar;
}
function getAdviceResult()
{
return $this->result;
}
function proceed()
{
if ($this->instance instanceof AOP)
{
$this->instance->setAdviceResult($this->getAdviceResult());
}
return call_user_func_array(array($this->instance, $this->func), $this->params);
}
}
class AdviceAroundException extends Exception{}
class Advice
{
private $pre_advice = null;
function __construct($advice = null)
{
if ($advice)
{
$this->pre_advice = $advice;
}
}
function getPreAdvice()
{
return $this->pre_advice;
}
function beforeAdvice(){}
function afterAdvice(){}
function aroundAdvice(AOP $aop)
{
throw new AdviceAroundException();
}
function throwAdvice(Exception $e)
{
return $e;
}
}
class Cache extends Advice
{
function aroundAdvice(AOP $aop)
{
$rs = $aop->getAdviceResult();
if ($rs['permission'])
{
$ret = $aop->proceed();
e('make cache');
return $ret;
}
else
{
throw new Exception('cache: no permission');
}
}
function throwAdvice(Exception $e)
{
e('cache exception');
throw new Exception($e->getMessage());
}
}
class Log extends Advice
{
function aroundAdvice(AOP $aop)
{
$rs = $aop->getAdviceResult();
if ($rs['permission'])
{
$ret = $aop->proceed();
return $ret;
}
else
{
e('no permission, do sth log');
throw new Exception('log: no permission');
}
}
function throwAdvice(Exception $e)
{
e('log exception');
throw new Exception($e->getMessage());
}
}
class Permission extends Advice
{
function aroundAdvice(AOP $aop)
{
$permission = doPermissionCheck();
$aop->setAdviceResult(array('permission'=>$permission));
e('permission: ' . var_export($permission, true));
$ret = $aop->proceed();
return $ret;
}
function throwAdvice(Exception $e)
{
e('permission exception');
throw new Exception($e->getMessage());
}
}
class User
{
function foobar()
{
e('foobar');
return 'hello result';
}
}
function doPermissionCheck()
{
return false;
}
/**
* @var User $user
*/
$user = AOP::getInstance('user', new Cache(new Log(new Permission())));
try
{
$ret = $user->foobar();
echo 'foobar function result:';
var_dump($ret);
}
catch(Exception $e)
{
echo $e->getMessage();
}
<?php
if (!function_exists('o'))
{
function o($str)
{
echo $str , "\n";
}
}
class NoAroundAdviceEventException extends Exception{}
class LazyAdviceException extends Exception{}
$aop_config = array(
array(
'event' => 'before',
'point' => array('class'=>'User', 'method'=>'foobar'),
'advice'=> array('class'=>'Permission', 'method'=>'thisBefore')
),
array(
'event' => 'before',
'point' => array('class'=>'User', 'method'=>'foobar'),
'advice'=> array('class'=>'Log', 'method'=>'thatBefore')
),
array(
'event' => 'after',
'point' => array('class'=>'User', 'method'=>'foobar'),
'advice'=> array('class'=>'Cache', 'method'=>'myAfter')
),
array(
'event' => 'around',
'point' => array('class'=>'*', 'method'=>'*'),
'advice'=> array('class'=>'Baaa', 'method'=>'hisAround')
),
//
// array(
// 'event' => 'before',
// 'point' => array('class'=>'User', 'method'=>'sayHello'),
// 'advice'=> array('class'=>'Permission', 'method'=>'thisBefore')
// ),
// array(
// 'event' => 'before',
// 'point' => array('class'=>'User', 'method'=>'sayHello'),
// 'advice'=> array('class'=>'Log', 'method'=>'thatBefore')
// ),
// array(
// 'event' => 'after',
// 'point' => array('class'=>'User', 'method'=>'sayHello'),
// 'advice'=> array('class'=>'Cache', 'method'=>'myAfter')
// ),
// array(
// 'event' => 'around',
// 'point' => array('class'=>'*', 'method'=>'*'),
// 'advice'=> array('class'=>'Baaa', 'method'=>'hisAround')
// ),
);
class AdviceContainer
{
/**
* @var AdviceContainer $target
*/
private $target;
/**
* @var Advice $advice
*/
private $advice;
private $method, $params;
private $advice_result = array();
private $lazy_advices = array();
private $last_lazy_advices = array();
private $original_method = true;
function __construct($target, $advice = null)
{
$this->target = $target;
$this->advice = $advice;
}
function addAdvice($advice)
{
return new self($this, $advice);
}
private function lazyAdvices()
{
if (method_exists($this->target, $this->method) && ! $this->target instanceof AdviceContainer)
{
if ($this->lazy_advices)
{
if ($this->last_lazy_advices != $this->lazy_advices)
{
$target = clone $this->target;
$container = new AdviceContainer($target);
$this->last_lazy_advices = array_reverse($this->lazy_advices);
$this->advice = array_pop($this->lazy_advices);
foreach ($this->lazy_advices as $lazy_advice)
{
$container = $container->addAdvice($lazy_advice);
}
$this->target = $container;
// 重置
$this->lazy_advices = array();
}
else
{
throw new Exception('bad code, loop advices');
}
}
}
}
private function call()
{
if (method_exists($this->target, $this->method) && ! $this->target instanceof AdviceContainer)
{
if ($this->original_method)
{
$ret = $this->proceed($this->method, $this->params);
return $ret;
}
else
{
return null;
}
}
else
{
if ($this->target instanceof AdviceContainer)
{
try
{
try
{
$ret = $this->advice->around($this);
return $ret;
}
catch(NoAroundAdviceEventException $e)
{
$this->advice->before($this);
$ret = $this->proceed();
$this->advice->after($this);
return $ret;
}
}
catch(LazyAdviceException $e)
{
$this->lazy_advices[] = $this->advice;
$ret = $this->proceed();
return $ret;
}
catch(Exception $e)
{
return $this->advice->exception($e);
}
}
else
{
throw new Exception('method ' . $this->method . ' is not defined');
}
}
}
function __call($method, $params)
{
$this->method = $method;
$this->params = $params;
$this->lazyAdvices();
return $this->call();
}
function proceed($method = null, $params = null)
{
if ($this->target instanceof AdviceContainer)
{
$this->target->setAdviceResults($this->getAdviceResults());
$this->target->setOriginalMethod($this->getOriginalMethod());
$this->target->setLazyAdvices($this->getLazyAdvices());
$this->target->setLastLazyAdvices($this->last_lazy_advices);
}
if ($method !== null)
{
$this->method = $method;
}
if ($params !== null)
{
$this->params = $params;
}
$ret = call_user_func_array(array($this->target, $this->method), $this->params);
if ($this->target instanceof AdviceContainer)
{
$this->setAdviceResults($this->target->getAdviceResults());
}
return $ret;
}
function setAdviceResult($key, $val)
{
$this->advice_result[$key] = $val;
}
function getAdviceResult($key)
{
return $this->advice_result[$key];
}
function getAdviceResults()
{
return $this->advice_result;
}
function setAdviceResults($result)
{
$this->advice_result = $result;
}
function issetAdviceResult($key)
{
return array_key_exists($key, $this->advice_result);
}
function setLastLazyAdvices($last_lazy_advices)
{
$this->last_lazy_advices = $last_lazy_advices;
}
/**
* @param Bool $bool
* @return void
* 设置original method是否运行开关
* 与原值与操作
*/
function setOriginalMethod($bool)
{
$this->original_method = $bool && $this->original_method;
}
function getOriginalMethod()
{
return $this->original_method;
}
function getLazyAdvices()
{
return $this->lazy_advices;
}
function setLazyAdvices($lazy_advices)
{
$this->lazy_advices = $lazy_advices;
}
function getMethod()
{
return $this->method;
}
function getParams()
{
return $this->params;
}
}
class AdviceProxy
{
/**
* 4 events
* @var Advice
*/
private $before, $after, $around, $exception;
private $method;
function __construct($event, $advice, $method)
{
$this->$event = $advice;
$this->method = $method;
}
function __call($event, $params)
{
if ($this->$event)
{
return $this->$event->{$this->method}($params[0]);
}
else
{
return null;
}
}
function around(AdviceContainer $container)
{
if ($this->around)
{
return $this->around->{$this->method}($container);
}
else
{
throw new NoAroundAdviceEventException();
}
}
function exception(Exception $e)
{
if ($this->exception)
{
return $this->exception->{$this->method}($e);
}
else
{
throw new Exception($e->getMessage());
}
}
}
class AOP
{
private static $instances = array();
private static $advice_containers = array();
public static function getInstance($class)
{
if (! isset(self::$instances[$class]))
{
self::$instances[$class] = new self($class);
}
return self::$instances[$class];
}
public static function getAdvices($class, $method)
{
global $aop_config;
$aop_config = array_reverse($aop_config);
$advice_configs = array();
foreach ($aop_config as $config)
{
if (($config['point']['class'] == $class || $config['point']['class'] == '*') && ($config['point']['method'] == $method || $config['point']['method'] == '*'))
{
$advice_configs[] = $config;
}
}
return $advice_configs;
}
function __construct($class)
{
$this->class = ucwords($class);
}
function __call($method, $params)
{
if (! isset(self::$advice_containers[$this->class][$method]))
{
$advice_configs = self::getAdvices($this->class, $method);
$advice_container = new AdviceContainer(new $this->class());
foreach ($advice_configs as $config)
{
$advice_proxy = new AdviceProxy($config['event'], new $config['advice']['class'], $config['advice']['method']);
$advice_container = $advice_container->addAdvice($advice_proxy);
}
self::$advice_containers[$this->class][$method] = $advice_container;
}
else
{
$advice_container = self::$advice_containers[$this->class][$method];
}
$ret = call_user_func_array(array($advice_container, $method), $params);
return $ret;
}
}
interface Advice
{
function before(AdviceContainer $container);
function after(AdviceContainer $container);
function around(AdviceContainer $container);
function exception(Exception $e);
}
class Permission
{
function thisBefore(AdviceContainer $container)
{
o('call permission before');
$permission = getPermissionResult();
$container->setAdviceResult('permission', $permission);
if (! $permission)
{
$container->setOriginalMethod(false);
}
}
}
class Log
{
function thatBefore(AdviceContainer $container)
{
o('call log before');
if ($container->issetAdviceResult('baaa'))
{
$baaa = $container->getAdviceResult('baaa');
o('baaa:' . $baaa);
o('do log here');
$container->setAdviceResult('log', 'sth');
}
else
{
o('log need baaa state');
throw new LazyAdviceException();
}
}
}
class Cache
{
function myAfter(AdviceContainer $container)
{
o('call cache after');
}
}
class Baaa
{
function hisAround(AdviceContainer $container)
{
o('call baaa around1');
if ($container->issetAdviceResult('permission'))
{
$permission = $container->getAdviceResult('permission');
o('permission:' . $permission);
$container->setAdviceResult('baaa', 'xxx');
$ret = $container->proceed();
// print_r($container->getAdviceResults());
o('call baaa around2');
return $ret;
}
else
{
o('baaa need permission state');
throw new LazyAdviceException();
}
}
}
class User
{
function foobar()
{
o('foobar');
return 'ok';
}
function sayHello()
{
o('hello');
return 'bye';
}
}
function getPermissionResult()
{
return false;
}
try
{
$user = AOP::getInstance('User');
/**
* @var User $user
*/
$ret = $user->foobar();
var_dump($ret);
// $ret = $user->sayHello();
// var_dump($ret);
}
catch(Exception $e)
{
echo $e->getMessage();
}
@md2perpe
Copy link

md2perpe commented Jul 6, 2011

Note: array_shift() returns the first element (the one which is shifted out of the array).

    $class = $args[0];
    array_shift($args);

can be changed to

    $class = array_shift($args);

@fundon
Copy link
Author

fundon commented Jul 7, 2011

Thanks,Your suggestion is very good

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment