-
-
Notifications
You must be signed in to change notification settings - Fork 163
[Feature] Pointcut namespace refactoring #500
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,17 +12,77 @@ | |
|
||
namespace Go\Aop; | ||
|
||
use Go\ParserReflection\ReflectionFileNamespace; | ||
use ReflectionClass; | ||
use ReflectionFunction; | ||
use ReflectionMethod; | ||
use ReflectionProperty; | ||
|
||
/** | ||
* Pointcut realization for PHP | ||
* Pointcut is responsible for matching any reflection items both statically and dynamically. | ||
* | ||
* Pointcut may be evaluated statically or at runtime (dynamically). | ||
* Matcher uses smart technique of matching elements, consisting of several stages described below. | ||
* | ||
* <b>Static matching</b> | ||
* | ||
* First stage of static matching involves context only (just one argument). This pre-stage is used to optimize | ||
* filtering on matcher side to avoid nested loops of checks. For example, if we have a method pointcut, but | ||
* it doesn't match first with class, then we don't need to scan all methods at all and can exit earlier. | ||
* | ||
* Here is a mapping of context for different static joinpoints: | ||
* - For any traits or classes, context will be `ReflectionClass` corresponding to the given class or trait. | ||
* - For any functions, context will be `ReflectionFileNamespace` where internal function is analyzed. | ||
* - For any methods or properties, context will be `ReflectionClass` which is currently analysed (even for inherited items) | ||
* | ||
* Second stage of static matching uses exactly two arguments (context and reflector). Filter then fully checks | ||
* static information from reflection to make a decision about matching of given point. | ||
* | ||
* At this stage we can verify names, attributes, signature, parameters, types, etc. | ||
* | ||
* Pointcuts are defined as a predicate over the syntax-tree of the program, and define an interface that constrains | ||
* which elements of the base program are exposed by the pointcut. A pointcut picks out certain join points and values | ||
* at those points | ||
* If point filter is not dynamic {@see self::KIND_DYNAMIC}, then evaluation ends here statically, | ||
* and generated code will not contain any runtime checks for given point filter, allowing for better performance. | ||
* | ||
* <b>Dynamic matching</b> | ||
* | ||
* If instance of filter is dynamic and uses {@see self::KIND_DYNAMIC} flag, then after static matching which has been | ||
* used to prepare a dynamic hook, framework will call our pointcut again in runtime for dynamic matching. | ||
* | ||
* This dynamic matching stage uses full information about given join point, including possible instance/scope and | ||
* arguments for a particular point. | ||
*/ | ||
interface Pointcut extends PointFilter | ||
interface Pointcut | ||
{ | ||
public const KIND_METHOD = 1; | ||
public const KIND_PROPERTY = 2; | ||
public const KIND_CLASS = 4; | ||
public const KIND_TRAIT = 8; | ||
public const KIND_FUNCTION = 16; | ||
public const KIND_INIT = 32; | ||
public const KIND_STATIC_INIT = 64; | ||
public const KIND_ALL = 127; | ||
public const KIND_DYNAMIC = 256; | ||
public const KIND_INTRODUCTION = 512; | ||
|
||
/** | ||
* Returns the kind of point filter | ||
*/ | ||
public function getKind(): int; | ||
|
||
/** | ||
* Return the class filter for this pointcut. | ||
* Performs matching of point of code, returns true if point matches | ||
* | ||
* @param ReflectionClass<T>|ReflectionFileNamespace $context Related context, can be class or file namespace | ||
* @param ReflectionMethod|ReflectionProperty|ReflectionFunction|null $reflector Specific part of code, can be any Reflection class | ||
* @param null|(string&class-string<T>)|(object&T) $instanceOrScope Invocation instance or string for static calls | ||
* @param null|array<mixed> $arguments Dynamic arguments for method | ||
* | ||
* @template T of object | ||
*/ | ||
public function getClassFilter(): PointFilter; | ||
} | ||
public function matches( | ||
ReflectionClass|ReflectionFileNamespace $context, | ||
ReflectionMethod|ReflectionProperty|ReflectionFunction $reflector = null, | ||
object|string $instanceOrScope = null, | ||
array $arguments = null | ||
): bool; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Expected 1 newline at end of file; 0 found |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
declare(strict_types = 1); | ||
/* | ||
* Go! AOP framework | ||
* | ||
|
@@ -13,87 +13,62 @@ | |
namespace Go\Aop\Pointcut; | ||
|
||
use Go\Aop\Pointcut; | ||
use Go\Aop\Support\AndPointFilter; | ||
use Go\ParserReflection\ReflectionFileNamespace; | ||
use ReflectionClass; | ||
use ReflectionFunction; | ||
use ReflectionMethod; | ||
use ReflectionProperty; | ||
|
||
/** | ||
* Logical "AND" pointcut that combines two simple pointcuts | ||
* Logical "and" pointcut filter. | ||
*/ | ||
class AndPointcut implements Pointcut | ||
final readonly class AndPointcut implements Pointcut | ||
{ | ||
use PointcutClassFilterTrait; | ||
|
||
/** | ||
* First pointcut | ||
*/ | ||
protected Pointcut $first; | ||
|
||
/** | ||
* Second pointcut | ||
* Kind of pointcut | ||
*/ | ||
protected Pointcut $second; | ||
private int $pointcutKind; | ||
|
||
/** | ||
* Returns pointcut kind | ||
* List of Pointcut to combine with "AND" | ||
* | ||
* @var array<Pointcut> | ||
*/ | ||
protected int $kind; | ||
private array $pointcuts; | ||
|
||
/** | ||
* "And" pointcut constructor | ||
* And constructor | ||
*/ | ||
public function __construct(Pointcut $first, Pointcut $second) | ||
public function __construct(int $pointcutKind = null, Pointcut ...$pointcuts) | ||
{ | ||
$this->first = $first; | ||
$this->second = $second; | ||
$this->kind = $first->getKind() & $second->getKind(); | ||
|
||
$this->classFilter = new AndPointFilter($first->getClassFilter(), $second->getClassFilter()); | ||
// If we don't have specified kind, it will be calculated as intersection then | ||
if (!isset($pointcutKind)) { | ||
$pointcutKind = -1; | ||
foreach ($pointcuts as $singlePointcut) { | ||
$pointcutKind &= $singlePointcut->getKind(); | ||
} | ||
} | ||
$this->pointcutKind = $pointcutKind; | ||
$this->pointcuts = $pointcuts; | ||
} | ||
|
||
/** | ||
* Performs matching of point of code | ||
* | ||
* @param mixed $point Specific part of code, can be any Reflection class | ||
* @param null|mixed $context Related context, can be class or namespace | ||
* @param null|string|object $instance Invocation instance or string for static calls | ||
* @param null|array $arguments Dynamic arguments for method | ||
*/ | ||
public function matches($point, $context = null, $instance = null, array $arguments = null): bool | ||
{ | ||
return $this->matchPart($this->first, $point, $context, $instance, $arguments) | ||
&& $this->matchPart($this->second, $point, $context, $instance, $arguments); | ||
public function matches( | ||
ReflectionClass|ReflectionFileNamespace $context, | ||
ReflectionMethod|ReflectionProperty|ReflectionFunction $reflector = null, | ||
object|string $instanceOrScope = null, | ||
array $arguments = null | ||
): bool { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There must be a single space between the closing parenthesis and the opening brace of a multi-line function declaration; found 0 spaces |
||
foreach ($this->pointcuts as $singlePointcut) { | ||
if (!$singlePointcut->matches($context, $reflector, $instanceOrScope, $arguments)) { | ||
return false; | ||
} | ||
} | ||
|
||
return true; | ||
} | ||
|
||
/** | ||
* Returns the kind of point filter | ||
*/ | ||
public function getKind(): int | ||
{ | ||
return $this->kind; | ||
} | ||
|
||
/** | ||
* Checks if point filter matches the point | ||
* | ||
* @param Pointcut $pointcut | ||
* @param ReflectionMethod|ReflectionProperty|ReflectionClass $point | ||
* @param mixed $context Related context, can be class or namespace | ||
* @param object|string|null $instance [Optional] Instance for dynamic matching | ||
* @param array|null $arguments [Optional] Extra arguments for dynamic | ||
* matching | ||
* | ||
* @return bool | ||
*/ | ||
protected function matchPart( | ||
Pointcut $pointcut, | ||
$point, | ||
$context = null, | ||
$instance = null, | ||
array $arguments = null | ||
): bool { | ||
return $pointcut->matches($point, $context, $instance, $arguments) | ||
&& $pointcut->getClassFilter()->matches($context); | ||
return $this->pointcutKind; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Line indented incorrectly; expected 4 spaces, found 8