Skip to content

Commit a0930b2

Browse files
authored
Merge pull request #493 from goaop/feature/refactor-core-to-php82
Feature - refactor framework core to php82
2 parents 91dbacb + 4117b7d commit a0930b2

35 files changed

+362
-518
lines changed

Diff for: demos/Demo/Aspect/PropertyInterceptorAspect.php

+2-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
use Go\Aop\Aspect;
1616
use Go\Aop\Intercept\FieldAccess;
17+
use Go\Aop\Intercept\FieldAccessType;
1718
use Go\Lang\Attribute\Around;
1819

1920
/**
@@ -29,7 +30,7 @@ class PropertyInterceptorAspect implements Aspect
2930
#[Around("access(public|protected|private Demo\Example\PropertyDemo->*)")]
3031
public function aroundFieldAccess(FieldAccess $fieldAccess): void
3132
{
32-
$isRead = $fieldAccess->getAccessType() == FieldAccess::READ;
33+
$isRead = $fieldAccess->getAccessType() === FieldAccessType::READ;
3334
// proceed all internal advices
3435
$fieldAccess->proceed();
3536

Diff for: src/Aop/Framework/AbstractInterceptor.php

+45-45
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@
1313
namespace Go\Aop\Framework;
1414

1515
use Closure;
16+
use Go\Aop\AspectException;
1617
use Go\Aop\Intercept\Interceptor;
18+
use Go\Aop\OrderedAdvice;
1719
use Go\Core\AspectKernel;
1820
use ReflectionFunction;
1921
use ReflectionMethod;
@@ -31,7 +33,8 @@
3133
*
3234
* After and before interceptors are simple closures that will be invoked after and before main invocation.
3335
*
34-
* Framework models an interceptor as an PHP-closure, maintaining a chain of interceptors "around" the joinpoint:
36+
* Framework models an interceptor as an PHP {@see Closure}, maintaining a chain of interceptors "around" the joinpoint:
37+
* <pre>
3538
* public function (Joinpoint $joinPoint)
3639
* {
3740
* echo 'Before action';
@@ -41,98 +44,95 @@
4144
*
4245
* return $result;
4346
* }
47+
* </pre>
4448
*/
4549
abstract class AbstractInterceptor implements Interceptor, OrderedAdvice
4650
{
4751
/**
48-
* Local cache of advices for faster unserialization on big projects
49-
*
50-
* @var array<Closure>
51-
*/
52-
protected static array $localAdvicesCache = [];
53-
54-
/**
55-
* Pointcut expression string which was used for this interceptor
56-
*/
57-
protected string $pointcutExpression;
58-
59-
/**
60-
* Closure to call
61-
*/
62-
protected Closure $adviceMethod;
63-
64-
/**
65-
* Advice order
52+
* @var (array&array<string, Closure>) Local hashmap of advices for faster unserialization
6653
*/
67-
private int $adviceOrder;
54+
private static array $localAdvicesCache = [];
6855

6956
/**
7057
* Default constructor for interceptor
7158
*/
72-
public function __construct(Closure $adviceMethod, int $adviceOrder = 0, string $pointcutExpression = '')
73-
{
74-
$this->adviceMethod = $adviceMethod;
75-
$this->adviceOrder = $adviceOrder;
76-
$this->pointcutExpression = $pointcutExpression;
77-
}
59+
public function __construct(
60+
protected readonly Closure $adviceMethod,
61+
private readonly int $adviceOrder = 0,
62+
protected readonly string $pointcutExpression = ''
63+
) {}
7864

7965
/**
80-
* Serialize advice method into array
66+
* Serializes advice closure into array
67+
*
68+
* @return array{name: string, class?: string}
8169
*/
8270
public static function serializeAdvice(Closure $adviceMethod): array
8371
{
84-
$refAdvice = new ReflectionFunction($adviceMethod);
72+
$reflectionAdvice = new ReflectionFunction($adviceMethod);
73+
$scopeReflectionClass = $reflectionAdvice->getClosureScopeClass();
74+
75+
$packedAdvice = ['name' => $reflectionAdvice->name];
76+
if (!isset($scopeReflectionClass)) {
77+
throw new AspectException('Could not pack an interceptor without aspect name');
78+
}
79+
$packedAdvice['class'] = $scopeReflectionClass->name;
8580

86-
return [
87-
'method' => $refAdvice->name,
88-
'class' => $refAdvice->getClosureScopeClass()->name
89-
];
81+
return $packedAdvice;
9082
}
9183

9284
/**
9385
* Unserialize an advice
9486
*
95-
* @param array $adviceData Information about advice
87+
* @param array{name: string, class?: string} $adviceData Information about advice
9688
*/
9789
public static function unserializeAdvice(array $adviceData): Closure
9890
{
91+
// General unpacking supports only aspect's advices
92+
if (!isset($adviceData['class'])) {
93+
throw new AspectException('Could not unpack an interceptor without aspect name');
94+
}
9995
$aspectName = $adviceData['class'];
100-
$methodName = $adviceData['method'];
96+
$methodName = $adviceData['name'];
10197

102-
if (!isset(static::$localAdvicesCache["$aspectName->$methodName"])) {
103-
$aspect = AspectKernel::getInstance()->getContainer()->getAspect($aspectName);
104-
$refMethod = new ReflectionMethod($aspectName, $methodName);
105-
$advice = $refMethod->getClosure($aspect);
98+
// With aspect name and method name, we can restore back a closure for it
99+
if (!isset(self::$localAdvicesCache["$aspectName->$methodName"])) {
100+
$aspect = AspectKernel::getInstance()->getContainer()->getAspect($aspectName);
101+
$advice = (new ReflectionMethod($aspectName, $methodName))->getClosure($aspect);
106102

107-
static::$localAdvicesCache["$aspectName->$methodName"] = $advice;
103+
assert(isset($advice), 'getClosure() can not be null on modern PHP versions');
104+
self::$localAdvicesCache["$aspectName->$methodName"] = $advice;
108105
}
109106

110-
return static::$localAdvicesCache["$aspectName->$methodName"];
107+
return self::$localAdvicesCache["$aspectName->$methodName"];
111108
}
112109

113-
/**
114-
* Returns the advice order
115-
*/
116110
public function getAdviceOrder(): int
117111
{
118112
return $this->adviceOrder;
119113
}
120114

121115
/**
122116
* Getter for extracting the advice closure from Interceptor
117+
*
118+
* @internal
123119
*/
124120
public function getRawAdvice(): Closure
125121
{
126122
return $this->adviceMethod;
127123
}
128124

129125
/**
130-
* Serializes an interceptor into it's representation
126+
* Serializes an interceptor into it's array shape representation
127+
*
128+
* @return non-empty-array<string, mixed>
131129
*/
132130
final public function __serialize(): array
133131
{
132+
// Compressing state representation to avoid default values, eg pointcutExpression = '' or adviceOrder = 0
134133
$state = array_filter(get_object_vars($this));
135134

135+
// Override closure with array representation to enable serialization
136136
$state['adviceMethod'] = static::serializeAdvice($this->adviceMethod);
137137

138138
return $state;
@@ -141,7 +141,7 @@ final public function __serialize(): array
141141
/**
142142
* Un-serializes an interceptor from it's stored state
143143
*
144-
* @param array $state The stored representation of the interceptor.
144+
* @param array{adviceMethod: array{name: string, class?: string}} $state The stored representation of the interceptor.
145145
*/
146146
final public function __unserialize(array $state): void
147147
{

Diff for: src/Aop/Framework/AbstractInvocation.php

+3-13
Original file line numberDiff line numberDiff line change
@@ -20,26 +20,16 @@
2020
abstract class AbstractInvocation extends AbstractJoinpoint implements Invocation
2121
{
2222
/**
23-
* Arguments for invocation
23+
* @var array<mixed> Arguments for invocation, can be mutated by the {@see setArguments()} method
2424
*/
2525
protected array $arguments = [];
2626

27-
/**
28-
* Gets arguments for current invocation
29-
*
30-
* @api
31-
*/
32-
public function getArguments(): array
27+
final public function getArguments(): array
3328
{
3429
return $this->arguments;
3530
}
3631

37-
/**
38-
* Sets arguments for current invocation
39-
*
40-
* @api
41-
*/
42-
public function setArguments(array $arguments): void
32+
final public function setArguments(array $arguments): void
4333
{
4434
$this->arguments = $arguments;
4535
}

Diff for: src/Aop/Framework/AbstractJoinpoint.php

+11-39
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,7 @@
1818
use Go\Aop\AdviceBefore;
1919
use Go\Aop\Intercept\Interceptor;
2020
use Go\Aop\Intercept\Joinpoint;
21-
22-
use function is_array;
21+
use Go\Aop\OrderedAdvice;
2322

2423
/**
2524
* Abstract joinpoint for framework
@@ -33,26 +32,11 @@
3332
*/
3433
abstract class AbstractJoinpoint implements Joinpoint
3534
{
36-
/**
37-
* List of advices (interceptors)
38-
*
39-
* NB: All current children assume that each advice is Interceptor now.
40-
* Whereas, it isn't correct logically, this can be used to satisfy PHPStan check now.
41-
*
42-
* @var array<Interceptor>
43-
*/
44-
protected array $advices = [];
45-
4635
/**
4736
* Current advice index
4837
*/
4938
protected int $current = 0;
5039

51-
/**
52-
* Stack frames to work with recursive calls or with cross-calls inside object
53-
*/
54-
protected array $stackFrames = [];
55-
5640
/**
5741
* Recursion level for invocation
5842
*/
@@ -63,10 +47,7 @@ abstract class AbstractJoinpoint implements Joinpoint
6347
*
6448
* @param array<Interceptor> $advices List of advices (interceptors)
6549
*/
66-
public function __construct(array $advices)
67-
{
68-
$this->advices = $advices;
69-
}
50+
public function __construct(protected readonly array $advices = []) {}
7051

7152
/**
7253
* Sorts advices by priority
@@ -80,23 +61,12 @@ public static function sortAdvices(array $advices): array
8061
$sortedAdvices = $advices;
8162
uasort(
8263
$sortedAdvices,
83-
function (Advice $first, Advice $second) {
84-
switch (true) {
85-
case $first instanceof AdviceBefore && !($second instanceof AdviceBefore):
86-
return -1;
87-
88-
case $first instanceof AdviceAround && !($second instanceof AdviceAround):
89-
return 1;
90-
91-
case $first instanceof AdviceAfter && !($second instanceof AdviceAfter):
92-
return $second instanceof AdviceBefore ? 1 : -1;
93-
94-
case ($first instanceof OrderedAdvice && $second instanceof OrderedAdvice):
95-
return $first->getAdviceOrder() - $second->getAdviceOrder();
96-
97-
default:
98-
return 0;
99-
}
64+
fn(Advice $first, Advice $second) => match (true) {
65+
$first instanceof AdviceBefore && !($second instanceof AdviceBefore) => -1,
66+
$first instanceof AdviceAround && !($second instanceof AdviceAround) => 1,
67+
$first instanceof AdviceAfter && !($second instanceof AdviceAfter) => $second instanceof AdviceBefore ? 1 : -1,
68+
$first instanceof OrderedAdvice && $second instanceof OrderedAdvice => $first->getAdviceOrder() - $second->getAdviceOrder(),
69+
default => 0,
10070
}
10171
);
10272

@@ -106,7 +76,9 @@ function (Advice $first, Advice $second) {
10676
/**
10777
* Replace concrete advices with list of ids
10878
*
109-
* @param Advice[][][] $advices List of advices
79+
* @param array<array<array<string, Advice|Interceptor>>> $advices List of advices
80+
*
81+
* @return array<array<array<string>>> Sorted identifier of advices/interceptors
11082
*/
11183
public static function flatAndSortAdvices(array $advices): array
11284
{

0 commit comments

Comments
 (0)