-
-
Notifications
You must be signed in to change notification settings - Fork 164
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
Feature - refactor framework core to php82 #493
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,7 +13,9 @@ | |
namespace Go\Aop\Framework; | ||
|
||
use Closure; | ||
use Go\Aop\AspectException; | ||
use Go\Aop\Intercept\Interceptor; | ||
use Go\Aop\OrderedAdvice; | ||
use Go\Core\AspectKernel; | ||
use ReflectionFunction; | ||
use ReflectionMethod; | ||
|
@@ -31,7 +33,8 @@ | |
* | ||
* After and before interceptors are simple closures that will be invoked after and before main invocation. | ||
* | ||
* Framework models an interceptor as an PHP-closure, maintaining a chain of interceptors "around" the joinpoint: | ||
* Framework models an interceptor as an PHP {@see Closure}, maintaining a chain of interceptors "around" the joinpoint: | ||
* <pre> | ||
* public function (Joinpoint $joinPoint) | ||
* { | ||
* echo 'Before action'; | ||
|
@@ -41,98 +44,95 @@ | |
* | ||
* return $result; | ||
* } | ||
* </pre> | ||
*/ | ||
abstract class AbstractInterceptor implements Interceptor, OrderedAdvice | ||
{ | ||
/** | ||
* Local cache of advices for faster unserialization on big projects | ||
* | ||
* @var array<Closure> | ||
*/ | ||
protected static array $localAdvicesCache = []; | ||
|
||
/** | ||
* Pointcut expression string which was used for this interceptor | ||
*/ | ||
protected string $pointcutExpression; | ||
|
||
/** | ||
* Closure to call | ||
*/ | ||
protected Closure $adviceMethod; | ||
|
||
/** | ||
* Advice order | ||
* @var (array&array<string, Closure>) Local hashmap of advices for faster unserialization | ||
*/ | ||
private int $adviceOrder; | ||
private static array $localAdvicesCache = []; | ||
|
||
/** | ||
* Default constructor for interceptor | ||
*/ | ||
public function __construct(Closure $adviceMethod, int $adviceOrder = 0, string $pointcutExpression = '') | ||
{ | ||
$this->adviceMethod = $adviceMethod; | ||
$this->adviceOrder = $adviceOrder; | ||
$this->pointcutExpression = $pointcutExpression; | ||
} | ||
public function __construct( | ||
protected readonly Closure $adviceMethod, | ||
private readonly int $adviceOrder = 0, | ||
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. Line indented incorrectly; expected 4 spaces, found 8 |
||
protected readonly string $pointcutExpression = '' | ||
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. Line indented incorrectly; expected 4 spaces, found 8 |
||
) {} | ||
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. Closing brace must be on a line by itself |
||
|
||
/** | ||
* Serialize advice method into array | ||
* Serializes advice closure into array | ||
* | ||
* @return array{name: string, class?: string} | ||
*/ | ||
public static function serializeAdvice(Closure $adviceMethod): array | ||
{ | ||
$refAdvice = new ReflectionFunction($adviceMethod); | ||
$reflectionAdvice = new ReflectionFunction($adviceMethod); | ||
$scopeReflectionClass = $reflectionAdvice->getClosureScopeClass(); | ||
|
||
$packedAdvice = ['name' => $reflectionAdvice->name]; | ||
if (!isset($scopeReflectionClass)) { | ||
throw new AspectException('Could not pack an interceptor without aspect name'); | ||
} | ||
$packedAdvice['class'] = $scopeReflectionClass->name; | ||
|
||
return [ | ||
'method' => $refAdvice->name, | ||
'class' => $refAdvice->getClosureScopeClass()->name | ||
]; | ||
return $packedAdvice; | ||
} | ||
|
||
/** | ||
* Unserialize an advice | ||
* | ||
* @param array $adviceData Information about advice | ||
* @param array{name: string, class?: string} $adviceData Information about advice | ||
*/ | ||
public static function unserializeAdvice(array $adviceData): Closure | ||
{ | ||
// General unpacking supports only aspect's advices | ||
if (!isset($adviceData['class'])) { | ||
throw new AspectException('Could not unpack an interceptor without aspect name'); | ||
} | ||
$aspectName = $adviceData['class']; | ||
$methodName = $adviceData['method']; | ||
$methodName = $adviceData['name']; | ||
|
||
if (!isset(static::$localAdvicesCache["$aspectName->$methodName"])) { | ||
$aspect = AspectKernel::getInstance()->getContainer()->getAspect($aspectName); | ||
$refMethod = new ReflectionMethod($aspectName, $methodName); | ||
$advice = $refMethod->getClosure($aspect); | ||
// With aspect name and method name, we can restore back a closure for it | ||
if (!isset(self::$localAdvicesCache["$aspectName->$methodName"])) { | ||
$aspect = AspectKernel::getInstance()->getContainer()->getAspect($aspectName); | ||
$advice = (new ReflectionMethod($aspectName, $methodName))->getClosure($aspect); | ||
|
||
static::$localAdvicesCache["$aspectName->$methodName"] = $advice; | ||
assert(isset($advice), 'getClosure() can not be null on modern PHP versions'); | ||
self::$localAdvicesCache["$aspectName->$methodName"] = $advice; | ||
} | ||
|
||
return static::$localAdvicesCache["$aspectName->$methodName"]; | ||
return self::$localAdvicesCache["$aspectName->$methodName"]; | ||
} | ||
|
||
/** | ||
* Returns the advice order | ||
*/ | ||
public function getAdviceOrder(): int | ||
{ | ||
return $this->adviceOrder; | ||
} | ||
|
||
/** | ||
* Getter for extracting the advice closure from Interceptor | ||
* | ||
* @internal | ||
*/ | ||
public function getRawAdvice(): Closure | ||
{ | ||
return $this->adviceMethod; | ||
} | ||
|
||
/** | ||
* Serializes an interceptor into it's representation | ||
* Serializes an interceptor into it's array shape representation | ||
* | ||
* @return non-empty-array<string, mixed> | ||
*/ | ||
final public function __serialize(): array | ||
{ | ||
// Compressing state representation to avoid default values, eg pointcutExpression = '' or adviceOrder = 0 | ||
$state = array_filter(get_object_vars($this)); | ||
|
||
// Override closure with array representation to enable serialization | ||
$state['adviceMethod'] = static::serializeAdvice($this->adviceMethod); | ||
|
||
return $state; | ||
|
@@ -141,7 +141,7 @@ final public function __serialize(): array | |
/** | ||
* Un-serializes an interceptor from it's stored state | ||
* | ||
* @param array $state The stored representation of the interceptor. | ||
* @param array{adviceMethod: array{name: string, class?: string}} $state The stored representation of the interceptor. | ||
*/ | ||
final public function __unserialize(array $state): void | ||
{ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,8 +18,7 @@ | |
use Go\Aop\AdviceBefore; | ||
use Go\Aop\Intercept\Interceptor; | ||
use Go\Aop\Intercept\Joinpoint; | ||
|
||
use function is_array; | ||
use Go\Aop\OrderedAdvice; | ||
|
||
/** | ||
* Abstract joinpoint for framework | ||
|
@@ -33,26 +32,11 @@ | |
*/ | ||
abstract class AbstractJoinpoint implements Joinpoint | ||
{ | ||
/** | ||
* List of advices (interceptors) | ||
* | ||
* NB: All current children assume that each advice is Interceptor now. | ||
* Whereas, it isn't correct logically, this can be used to satisfy PHPStan check now. | ||
* | ||
* @var array<Interceptor> | ||
*/ | ||
protected array $advices = []; | ||
|
||
/** | ||
* Current advice index | ||
*/ | ||
protected int $current = 0; | ||
|
||
/** | ||
* Stack frames to work with recursive calls or with cross-calls inside object | ||
*/ | ||
protected array $stackFrames = []; | ||
|
||
/** | ||
* Recursion level for invocation | ||
*/ | ||
|
@@ -63,10 +47,7 @@ abstract class AbstractJoinpoint implements Joinpoint | |
* | ||
* @param array<Interceptor> $advices List of advices (interceptors) | ||
*/ | ||
public function __construct(array $advices) | ||
{ | ||
$this->advices = $advices; | ||
} | ||
public function __construct(protected readonly array $advices = []) {} | ||
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.
|
||
|
||
/** | ||
* Sorts advices by priority | ||
|
@@ -80,23 +61,12 @@ public static function sortAdvices(array $advices): array | |
$sortedAdvices = $advices; | ||
uasort( | ||
$sortedAdvices, | ||
function (Advice $first, Advice $second) { | ||
switch (true) { | ||
case $first instanceof AdviceBefore && !($second instanceof AdviceBefore): | ||
return -1; | ||
|
||
case $first instanceof AdviceAround && !($second instanceof AdviceAround): | ||
return 1; | ||
|
||
case $first instanceof AdviceAfter && !($second instanceof AdviceAfter): | ||
return $second instanceof AdviceBefore ? 1 : -1; | ||
|
||
case ($first instanceof OrderedAdvice && $second instanceof OrderedAdvice): | ||
return $first->getAdviceOrder() - $second->getAdviceOrder(); | ||
|
||
default: | ||
return 0; | ||
} | ||
fn(Advice $first, Advice $second) => match (true) { | ||
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. Space before opening parenthesis of function call prohibited |
||
$first instanceof AdviceBefore && !($second instanceof AdviceBefore) => -1, | ||
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. Multi-line function call not indented correctly; expected 12 spaces but found 16 |
||
$first instanceof AdviceAround && !($second instanceof AdviceAround) => 1, | ||
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. Multi-line function call not indented correctly; expected 12 spaces but found 16 |
||
$first instanceof AdviceAfter && !($second instanceof AdviceAfter) => $second instanceof AdviceBefore ? 1 : -1, | ||
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. Multi-line function call not indented correctly; expected 12 spaces but found 16 |
||
$first instanceof OrderedAdvice && $second instanceof OrderedAdvice => $first->getAdviceOrder() - $second->getAdviceOrder(), | ||
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. Multi-line function call not indented correctly; expected 12 spaces but found 16 |
||
default => 0, | ||
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.
|
||
} | ||
); | ||
|
||
|
@@ -106,7 +76,9 @@ function (Advice $first, Advice $second) { | |
/** | ||
* Replace concrete advices with list of ids | ||
* | ||
* @param Advice[][][] $advices List of advices | ||
* @param array<array<array<string, Advice|Interceptor>>> $advices List of advices | ||
* | ||
* @return array<array<array<string>>> Sorted identifier of advices/interceptors | ||
*/ | ||
public static function flatAndSortAdvices(array $advices): array | ||
{ | ||
|
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