Skip to content

Commit

Permalink
Merge pull request #429 from goaop/feature/provide-clean-scope-api
Browse files Browse the repository at this point in the history
[BC BREAK] Introduce new ClassJoinpoint->getScope() API
  • Loading branch information
lisachenko authored Aug 15, 2019
2 parents 28615ad + 02db999 commit 652b7c7
Show file tree
Hide file tree
Showing 20 changed files with 255 additions and 195 deletions.
14 changes: 5 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,15 +163,11 @@ class MonitorAspect implements Aspect
*/
public function beforeMethodExecution(MethodInvocation $invocation)
{
$obj = $invocation->getThis();
echo 'Calling Before Interceptor for method: ',
is_object($obj) ? get_class($obj) : $obj,
$invocation->getMethod()->isStatic() ? '::' : '->',
$invocation->getMethod()->getName(),
'()',
' with arguments: ',
json_encode($invocation->getArguments()),
"<br>\n";
echo 'Calling Before Interceptor for: ',
$invocation,
' with arguments: ',
json_encode($invocation->getArguments()),
"<br>\n";
}
}
```
Expand Down
18 changes: 11 additions & 7 deletions demos/Demo/Aspect/DynamicMethodsAspect.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,11 @@ class DynamicMethodsAspect implements Aspect
*/
public function beforeMagicMethodExecution(MethodInvocation $invocation)
{
$obj = $invocation->getThis();

// we need to unpack args from invocation args
list($methodName, $args) = $invocation->getArguments();
echo 'Calling Magic Interceptor for method: ',
is_object($obj) ? get_class($obj) : $obj,
$invocation->getMethod()->isStatic() ? '::' : '->',
$invocation->getScope(),
'->',
$methodName,
'()',
' with arguments: ',
Expand All @@ -56,8 +54,14 @@ public function beforeMagicMethodExecution(MethodInvocation $invocation)
public function beforeMagicStaticMethodExecution(MethodInvocation $invocation)
{
// we need to unpack args from invocation args
list($methodName) = $invocation->getArguments();

echo "Calling Magic Static Interceptor for method: ", $methodName, PHP_EOL;
list($methodName, $args) = $invocation->getArguments();
echo 'Calling Static Magic Interceptor for method: ',
$invocation->getScope(),
'::',
$methodName,
'()',
' with arguments: ',
json_encode($args),
PHP_EOL;
}
}
57 changes: 24 additions & 33 deletions src/Aop/Framework/AbstractMethodInvocation.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@
use Go\Aop\Intercept\MethodInvocation;
use Go\Aop\Support\AnnotatedReflectionMethod;
use ReflectionMethod;
use function is_object, get_class, array_pop, array_merge;
use function array_merge;
use function array_pop;
use function count;
use function is_string;

/**
* Abstract method invocation implementation
Expand All @@ -23,9 +26,9 @@ abstract class AbstractMethodInvocation extends AbstractInvocation implements Me
{

/**
* Instance of object for invoking or class name for static call
* Instance of object for invoking
*
* @var object|string
* @var object|null
*/
protected $instance;

Expand All @@ -37,11 +40,20 @@ abstract class AbstractMethodInvocation extends AbstractInvocation implements Me
protected $reflectionMethod;

/**
* Name of the invocation class
* Class name scope for static invocation
*
* @var string
*/
protected $className = '';
protected $scope;

/**
* This static variable holds the name of field to use to avoid extra "if" section in the __invoke method
*
* Overridden in children classes and initialized via LSB
*
* @var string
*/
protected static $propertyName;

/**
* Constructor for method invocation
Expand All @@ -51,8 +63,7 @@ abstract class AbstractMethodInvocation extends AbstractInvocation implements Me
public function __construct(string $className, string $methodName, array $advices)
{
parent::__construct($advices);
$this->className = $className;
$this->reflectionMethod = $method = new AnnotatedReflectionMethod($this->className, $methodName);
$this->reflectionMethod = $method = new AnnotatedReflectionMethod($className, $methodName);

// Give an access to call protected method
if ($method->isProtected()) {
Expand All @@ -72,26 +83,27 @@ public function __construct(string $className, string $methodName, array $advice
final public function __invoke($instance = null, array $arguments = [], array $variadicArguments = [])
{
if ($this->level > 0) {
$this->stackFrames[] = [$this->arguments, $this->instance, $this->current];
$this->stackFrames[] = [$this->arguments, $this->scope, $this->instance, $this->current];
}

if (!empty($variadicArguments)) {
if (count($variadicArguments) > 0) {
$arguments = array_merge($arguments, $variadicArguments);
}

try {
++$this->level;

$this->current = 0;
$this->instance = $instance;
$this->arguments = $arguments;

$this->{static::$propertyName} = $instance;

return $this->proceed();
} finally {
--$this->level;

if ($this->level > 0) {
[$this->arguments, $this->instance, $this->current] = array_pop($this->stackFrames);
[$this->arguments, $this->scope, $this->instance, $this->current] = array_pop($this->stackFrames);
} else {
$this->instance = null;
$this->arguments = [];
Expand All @@ -109,35 +121,14 @@ public function getMethod(): ReflectionMethod
return $this->reflectionMethod;
}

/**
* Returns the object that holds the current joinpoint's static
* part.
*
* @return object|string the object for dynamic call or string with name of scope
*/
public function getThis()
{
return $this->instance;
}

/**
* Returns the static part of this joinpoint.
*
* @return object
*/
public function getStaticPart()
{
return $this->getMethod();
}

/**
* Returns friendly description of this joinpoint
*/
final public function __toString(): string
{
return sprintf(
'execution(%s%s%s())',
is_object($this->instance) ? get_class($this->instance) : $this->instance,
$this->getScope(),
$this->reflectionMethod->isStatic() ? '::' : '->',
$this->reflectionMethod->name
);
Expand Down
29 changes: 19 additions & 10 deletions src/Aop/Framework/ClassFieldAccess.php
Original file line number Diff line number Diff line change
Expand Up @@ -196,25 +196,34 @@ final public function &__invoke(object $instance, int $accessType, &$originalVal
}

/**
* Returns the object that holds the current joinpoint's static
* part.
* Returns the object for which current joinpoint is invoked
*
* @return object|null the object (can be null if the accessible object is
* static).
* @return object|null Instance of object or null for static call/unavailable context
*/
final public function getThis()
final public function getThis(): ?object
{
return $this->instance;
}

/**
* Returns the static part of this joinpoint.
* Checks if the current joinpoint is dynamic or static
*
* @return object
* Dynamic joinpoint contains a reference to an object that can be received via getThis() method call
*
* @see ClassJoinpoint::getThis()
*/
final public function isDynamic(): bool
{
return true;
}

/**
* Returns the static scope name (class name) of this joinpoint.
*/
final public function getStaticPart()
final public function getScope(): string
{
return $this->getField();
// $this->instance always contains an object, so we can take it's name as a scope name
return get_class($this->instance);
}

/**
Expand All @@ -225,7 +234,7 @@ final public function __toString(): string
return sprintf(
'%s(%s->%s)',
$this->accessType === self::READ ? 'get' : 'set',
get_class($this->instance),
$this->getScope(),
$this->reflectionProperty->name
);
}
Expand Down
16 changes: 7 additions & 9 deletions src/Aop/Framework/DeclareErrorInterceptor.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
namespace Go\Aop\Framework;

use Closure;
use Go\Aop\Intercept\FieldAccess;
use Go\Aop\Intercept\Joinpoint;
use function get_class, is_string;

/**
* Interceptor to dynamically trigger an user notice/warning/error on method call
Expand Down Expand Up @@ -62,12 +62,11 @@ public static function unserializeAdvice(array $adviceData): Closure
*/
public function invoke(Joinpoint $joinpoint)
{
$reflection = $joinpoint->getStaticPart();
$reflectorName = 'unknown';
if ($reflection && method_exists($reflection, 'getName')) {
$reflectorName = $reflection->getName();
if ($joinpoint instanceof FieldAccess) {
$scope = $joinpoint->getScope();
$name = $joinpoint->getField()->getName();
($this->adviceMethod)($scope, $name, $this->message, $this->level);
}
($this->adviceMethod)($joinpoint->getThis(), $reflectorName, $this->message, $this->level);

return $joinpoint->proceed();
}
Expand All @@ -79,10 +78,9 @@ private static function getDeclareErrorAdvice(): Closure
{
static $adviceMethod;
if (!$adviceMethod) {
$adviceMethod = function ($object, $reflectorName, $message, $level = E_USER_NOTICE) {
$class = is_string($object) ? $object : get_class($object);
$adviceMethod = function (string $scope, string $property, string $message, int $level = E_USER_NOTICE) {
$message = vsprintf('[AOP Declare Error]: %s has an error: "%s"', [
$class . '->' . $reflectorName,
$scope . '->' . $property,
$message
]);
trigger_error($message, $level);
Expand Down
40 changes: 39 additions & 1 deletion src/Aop/Framework/DynamicClosureMethodInvocation.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

namespace Go\Aop\Framework;

use function get_class;

/**
* Dynamic closure method invocation is responsible to call dynamic methods via closure
*/
Expand All @@ -26,10 +28,15 @@ final class DynamicClosureMethodInvocation extends AbstractMethodInvocation
/**
* Previous instance of invocation
*
* @var null|object|string
* @var null|object
*/
protected $previousInstance;

/**
* For dynamic calls we store given argument as 'instance' property
*/
protected static $propertyName = 'instance';

/**
* Invokes original method and return result from it
*
Expand Down Expand Up @@ -57,4 +64,35 @@ public function proceed()

return ($this->closureToCall)(...$this->arguments);
}

/**
* Returns the object for which current joinpoint is invoked
*
* @return object Instance of object or null for static call/unavailable context
*/
final public function getThis(): ?object
{
return $this->instance;
}

/**
* Checks if the current joinpoint is dynamic or static
*
* Dynamic joinpoint contains a reference to an object that can be received via getThis() method call
* @see ClassJoinpoint::getThis()
*/
final public function isDynamic(): bool
{
return true;
}

/**
* Returns the static scope name (class name) of this joinpoint.
*/
final public function getScope(): string
{
// Due to optimization $this->scope won't be filled for each invocation
// However, $this->instance always contains an object, so we can take it's name as a scope name
return get_class($this->instance);
}
}
12 changes: 6 additions & 6 deletions src/Aop/Framework/DynamicInvocationMatcherInterceptor.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
namespace Go\Aop\Framework;

use Go\Aop\Intercept\Interceptor;
use Go\Aop\Intercept\Invocation;
use Go\Aop\Intercept\Joinpoint;
use Go\Aop\Intercept\MethodInvocation;
use Go\Aop\PointFilter;
use ReflectionClass;

Expand Down Expand Up @@ -53,11 +53,11 @@ public function __construct(PointFilter $pointFilter, Interceptor $interceptor)
*/
final public function invoke(Joinpoint $joinpoint)
{
if ($joinpoint instanceof Invocation) {
$point = $joinpoint->getStaticPart();
$instance = $joinpoint->getThis();
$context = new ReflectionClass($instance);
if ($this->pointFilter->matches($point, $context, $instance, $joinpoint->getArguments())) {
if ($joinpoint instanceof MethodInvocation) {
$method = $joinpoint->getMethod();
$context = $joinpoint->getThis() ?? $joinpoint->getScope();
$contextClass = new ReflectionClass($context);
if ($this->pointFilter->matches($method, $contextClass, $context, $joinpoint->getArguments())) {
return $this->interceptor->invoke($joinpoint);
}
}
Expand Down
Loading

0 comments on commit 652b7c7

Please sign in to comment.