Skip to content

Commit

Permalink
#10 support for serialization of interceptors was added. This give th…
Browse files Browse the repository at this point in the history
…e ability to cache an advices for class
  • Loading branch information
lisachenko committed Dec 11, 2012
1 parent b7eef1f commit 05c2794
Show file tree
Hide file tree
Showing 6 changed files with 145 additions and 29 deletions.
76 changes: 74 additions & 2 deletions src/Go/Aop/Framework/BaseAdvice.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@

namespace Go\Aop\Framework;

use ReflectionFunction;
use ReflectionMethod;

use Go\Aop\Advice;
use Go\Aop\Aspect;
use Go\Core\AspectKernel;

/**
* Base class for all framework advices implementations
Expand Down Expand Up @@ -37,18 +42,85 @@
*/
abstract class BaseAdvice implements Advice
{
/** Constant for undefined order */
/**
* Constant for undefined order
*/
const ORDER_NOT_SET = -1;

/** @var int Advice order */
/**
* Advice order
*
* @var int
*/
protected $order = self::ORDER_NOT_SET;

/**
* Returns the advice order
*
* @return int
*/
public function getAdviceOrder()
{
return $this->order;
}

/**
* Serialize advice method into array
*
* @param callable|\Closure $adviceMethod An advice for aspect
*
* @return array
*/
public static function serializeAdvice($adviceMethod)
{
$refAdvice = new ReflectionFunction($adviceMethod);
if (IS_MODERN_PHP) {
$method = $refAdvice;
$aspect = $refAdvice->getClosureThis();
} else {
$vars = $refAdvice->getStaticVariables();
$method = $vars['refMethod'];
$aspect = $vars['aspect'];
}
return array(
'method' => $method->name,
'aspect' => get_class($aspect)
);
}

/**
* Unserialize an advice
*
* @param array $adviceData Information about advice
*
* @return callable|\Closure
*/
public static function unserializeAdvice($adviceData)
{
$aspectName = $adviceData['aspect'];
$methodName = $adviceData['method'];

$refMethod = new ReflectionMethod($aspectName, $methodName);
$aspect = AspectKernel::getInstance()->getContainer()->getAspect($aspectName);
return static::fromAspectReflection($aspect, $refMethod);
}

/**
* Returns an advice from aspect method reflection
*
* @param Aspect $aspect Instance of aspect
* @param ReflectionMethod $refMethod Reflection method of aspect
*
* @return callable|object
*/
public static function fromAspectReflection(Aspect $aspect, ReflectionMethod $refMethod)
{
if (IS_MODERN_PHP) {
return $refMethod->getClosure($aspect);
} else {
return function () use ($aspect, $refMethod) {
return $refMethod->invokeArgs($aspect, func_get_args());
};
}
}
}
31 changes: 30 additions & 1 deletion src/Go/Aop/Framework/BaseInterceptor.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,16 @@

namespace Go\Aop\Framework;

use Serializable;

use Go\Aop\Pointcut;
use Go\Aop\Framework\BaseAdvice;
use Go\Aop\Intercept\Interceptor;

/**
* @package go
*/
class BaseInterceptor extends BaseAdvice implements Interceptor
class BaseInterceptor extends BaseAdvice implements Interceptor, Serializable
{
/**
* Name of the aspect
Expand Down Expand Up @@ -65,4 +67,31 @@ public function getRawAdvice()
{
return $this->adviceMethod;
}

/**
* Serializes an interceptor into string representation
*
* @return string the string representation of the object or null
*/
public function serialize()
{
$vars = get_object_vars($this);
$vars['adviceMethod'] = static::serializeAdvice($this->adviceMethod);
return serialize($vars);
}

/**
* Unserialize an interceptor from the string
*
* @param string $serialized The string representation of the object.
* @return void
*/
public function unserialize($serialized)
{
$vars = unserialize($serialized);
$vars['adviceMethod'] = static::unserializeAdvice($vars['adviceMethod']);
foreach ($vars as $key=>$value) {
$this->$key = $value;
}
}
}
6 changes: 0 additions & 6 deletions src/Go/Aop/Support/AopChildFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,6 @@
use TokenReflection\ReflectionParameter as ParsedReflectionParameter;
use TokenReflection\ReflectionProperty as ParsedReflectionProperty;

/**
* Whether or not we have a modern PHP
*/
define('IS_MODERN_PHP', version_compare(PHP_VERSION, '5.4.0') >= 0);


/**
* AOP Factory that is used to generate child code from joinpoints
*/
Expand Down
34 changes: 34 additions & 0 deletions src/Go/Core/AspectContainer.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,13 @@ class AspectContainer
*/
protected $services = array();

/**
* List of registered aspects
*
* @var array
*/
protected $aspects = array();

/**
* List of resources for application
*
Expand Down Expand Up @@ -165,16 +172,43 @@ public function registerAdvisor(Aop\Advisor $advisor, $id = null)
}
}

/**
* Returns an aspect by id or class name
*
* @param string $aspectName Aspect name
*
* @throws \OutOfBoundsException If aspect is unknown
*
* @return Aop\Aspect
*/
public function getAspect($aspectName)
{
if (!isset($this->aspects[$aspectName])) {
throw new \OutOfBoundsException("Unknown aspect {$aspectName}");
}
return $this->aspects[$aspectName];
}

/**
* Register an aspect in the container
*
* @param Aop\Aspect $aspect Instance of concrete aspect
* @param string $id Key for aspect
*
* @throws \LogicException if aspect was already registered
*/
public function registerAspect(Aop\Aspect $aspect)
{
$aspectName = get_class($aspect);
if (!empty($this->aspects[$aspectName])) {
throw new \LogicException("Only one instance of single aspect can be registered at once");
}

/** @var $loader AspectLoader */
$loader = $this->get('aspect.loader');
$loader->load($aspect);

$this->aspects[$aspectName] = $aspect;
}

/**
Expand Down
6 changes: 6 additions & 0 deletions src/Go/Core/AspectKernel.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@

use TokenReflection;

/**
* Whether or not we have a modern PHP
*/
define('IS_MODERN_PHP', version_compare(PHP_VERSION, '5.4.0') >= 0);


/**
* Abstract aspect kernel is used to prepare an application to work with aspects.
*
Expand Down
21 changes: 1 addition & 20 deletions src/Go/Core/GeneralAspectLoaderExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ public function supports(Aspect $aspect, $reflection, $metaInformation = null)
*/
public function load(AspectContainer $container, Aspect $aspect, $reflection, $metaInformation = null)
{
$adviceCallback = $this->getAdvice($aspect, $reflection);
$adviceCallback = Framework\BaseAdvice::fromAspectReflection($aspect, $reflection);

// TODO: use general pointcut parser here instead of hardcoded regular expressions
$pointcut = $this->parsePointcut($metaInformation);
Expand Down Expand Up @@ -167,25 +167,6 @@ protected function getPropertyInterceptor($metaInformation, $adviceCallback)
}
}

/**
* Returns an advice from aspect
*
* @param Aspect $aspect Aspect instance
* @param ReflectionMethod $refMethod Reflection method of aspect
*
* @return callable Advice to call
*/
private function getAdvice(Aspect $aspect, ReflectionMethod $refMethod)
{
if (version_compare(PHP_VERSION, '5.4.0') >= 0) {
return $refMethod->getClosure($aspect);
} else {
return function () use ($aspect, $refMethod) {
return $refMethod->invokeArgs($aspect, func_get_args());
};
}
}

/**
* Temporary method for parsing pointcuts
*
Expand Down

0 comments on commit 05c2794

Please sign in to comment.