1
|
|
|
<?php |
2
|
|
|
/* |
3
|
|
|
* Go! AOP framework |
4
|
|
|
* |
5
|
|
|
* @copyright Copyright 2012, Lisachenko Alexander <[email protected]> |
6
|
|
|
* |
7
|
|
|
* This source file is subject to the license that is bundled |
8
|
|
|
* with this source code in the file LICENSE. |
9
|
|
|
*/ |
10
|
|
|
|
11
|
|
|
namespace Go\Core; |
12
|
|
|
|
13
|
|
|
use ReflectionMethod; |
14
|
|
|
use ReflectionProperty; |
15
|
|
|
use Go\Aop; |
16
|
|
|
use Go\Aop\Support\NamespacedReflectionFunction; |
17
|
|
|
use TokenReflection\ReflectionClass as ParsedReflectionClass; |
18
|
|
|
use TokenReflection\ReflectionFileNamespace; |
19
|
|
|
|
20
|
|
|
/** |
21
|
|
|
* Advice matcher returns the list of advices for the specific point of code |
22
|
|
|
*/ |
23
|
|
|
class AdviceMatcher |
24
|
|
|
{ |
25
|
|
|
/** |
26
|
|
|
* Loader of aspects |
27
|
|
|
* |
28
|
|
|
* @var AspectLoader |
29
|
|
|
*/ |
30
|
|
|
protected $loader; |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* Flag to enable/disable support of global function interception |
34
|
|
|
* |
35
|
|
|
* @var bool |
36
|
|
|
*/ |
37
|
|
|
private $isInterceptFunctions = false; |
38
|
|
|
|
39
|
|
|
/** |
40
|
|
|
* Constructor |
41
|
|
|
* |
42
|
|
|
* @param AspectLoader $loader Instance of aspect loader |
43
|
|
|
* @param bool $isInterceptFunctions Optional flag to enable function interception |
44
|
|
|
*/ |
45
|
|
|
public function __construct(AspectLoader $loader, $isInterceptFunctions = false) |
46
|
|
|
{ |
47
|
|
|
$this->loader = $loader; |
48
|
|
|
|
49
|
|
|
$this->isInterceptFunctions = $isInterceptFunctions; |
50
|
|
|
} |
51
|
|
|
|
52
|
|
|
/** |
53
|
|
|
* Returns list of function advices for namespace |
54
|
|
|
* |
55
|
|
|
* @param ReflectionFileNamespace $namespace |
56
|
|
|
* @param array|Aop\Advisor[] $advisors List of advisor to match |
57
|
|
|
* |
58
|
|
|
* @return array |
59
|
|
|
*/ |
60
|
|
|
public function getAdvicesForFunctions(ReflectionFileNamespace $namespace, array $advisors) |
61
|
|
|
{ |
62
|
|
|
if (!$this->isInterceptFunctions || $namespace->getName() == 'no-namespace') { |
63
|
|
|
return []; |
64
|
|
|
} |
65
|
|
|
|
66
|
|
|
$advices = []; |
67
|
|
|
|
68
|
|
|
foreach ($advisors as $advisorId => $advisor) { |
69
|
|
|
|
70
|
|
|
if ($advisor instanceof Aop\PointcutAdvisor) { |
71
|
|
|
|
72
|
|
|
$pointcut = $advisor->getPointcut(); |
73
|
|
|
$isFunctionAdvisor = $pointcut->getKind() & Aop\PointFilter::KIND_FUNCTION; |
74
|
|
|
if ($isFunctionAdvisor && $pointcut->getClassFilter()->matches($namespace)) { |
75
|
|
|
$advices = array_merge_recursive( |
76
|
|
|
$advices, |
77
|
|
|
$this->getFunctionAdvicesFromAdvisor($namespace, $advisor, $advisorId, $pointcut) |
78
|
|
|
); |
79
|
|
|
} |
80
|
|
|
} |
81
|
|
|
} |
82
|
|
|
|
83
|
|
|
return $advices; |
84
|
|
|
} |
85
|
|
|
|
86
|
|
|
/** |
87
|
|
|
* Return list of advices for class |
88
|
|
|
* |
89
|
|
|
* @param ParsedReflectionClass $class Class to advise |
90
|
|
|
* @param array|Aop\Advisor[] $advisors List of advisor to match |
91
|
|
|
* |
92
|
|
|
* @return array|Aop\Advice[] List of advices for class |
93
|
|
|
*/ |
94
|
|
|
public function getAdvicesForClass(ParsedReflectionClass $class, array $advisors) |
95
|
|
|
{ |
96
|
|
|
$classAdvices = []; |
97
|
|
|
$parentClass = $class->getParentClass(); |
98
|
|
|
|
99
|
|
|
if ($parentClass && preg_match('/' . AspectContainer::AOP_PROXIED_SUFFIX . '$/', $parentClass->name)) { |
100
|
|
|
$originalClass = $parentClass; |
101
|
|
|
} else { |
102
|
|
|
$originalClass = $class; |
103
|
|
|
} |
104
|
|
|
|
105
|
|
|
foreach ($advisors as $advisorId => $advisor) { |
106
|
|
|
|
107
|
|
|
if ($advisor instanceof Aop\PointcutAdvisor) { |
108
|
|
|
|
109
|
|
|
$pointcut = $advisor->getPointcut(); |
110
|
|
|
if ($pointcut->getClassFilter()->matches($class)) { |
111
|
|
|
$classAdvices = array_merge_recursive( |
112
|
|
|
$classAdvices, |
113
|
|
|
$this->getAdvicesFromAdvisor($originalClass, $advisor, $advisorId, $pointcut) |
114
|
|
|
); |
115
|
|
|
} |
116
|
|
|
} |
117
|
|
|
|
118
|
|
|
if ($advisor instanceof Aop\IntroductionAdvisor) { |
119
|
|
|
if ($advisor->getClassFilter()->matches($class)) { |
120
|
|
|
$classAdvices = array_merge_recursive( |
121
|
|
|
$classAdvices, |
122
|
|
|
$this->getIntroductionFromAdvisor($originalClass, $advisor, $advisorId) |
123
|
|
|
); |
124
|
|
|
} |
125
|
|
|
} |
126
|
|
|
} |
127
|
|
|
|
128
|
|
|
return $classAdvices; |
129
|
|
|
} |
130
|
|
|
|
131
|
|
|
/** |
132
|
|
|
* Returns list of advices from advisor and point filter |
133
|
|
|
* |
134
|
|
|
* @param ParsedReflectionClass $class Class to inject advices |
135
|
|
|
* @param Aop\PointcutAdvisor $advisor Advisor for class |
136
|
|
|
* @param string $advisorId Identifier of advisor |
137
|
|
|
* @param Aop\PointFilter $filter Filter for points |
138
|
|
|
* |
139
|
|
|
* @return array |
140
|
|
|
*/ |
141
|
|
|
private function getAdvicesFromAdvisor( |
142
|
|
|
ParsedReflectionClass $class, |
143
|
|
|
Aop\PointcutAdvisor $advisor, |
144
|
|
|
$advisorId, |
145
|
|
|
Aop\PointFilter $filter) |
146
|
|
|
{ |
147
|
|
|
$classAdvices = []; |
148
|
|
|
$filterKind = $filter->getKind(); |
149
|
|
|
|
150
|
|
|
// Check class only for class filters |
151
|
|
|
if ($filterKind & Aop\PointFilter::KIND_CLASS) { |
152
|
|
|
if ($filter->matches($class)) { |
153
|
|
|
// Dynamic initialization |
154
|
|
View Code Duplication |
if ($filterKind & Aop\PointFilter::KIND_INIT) { |
|
|
|
|
155
|
|
|
$classAdvices[AspectContainer::INIT_PREFIX]['root'][$advisorId] = $advisor->getAdvice(); |
156
|
|
|
} |
157
|
|
|
// Static initalization |
158
|
|
View Code Duplication |
if ($filterKind & Aop\PointFilter::KIND_STATIC_INIT) { |
|
|
|
|
159
|
|
|
$classAdvices[AspectContainer::STATIC_INIT_PREFIX]['root'][$advisorId] = $advisor->getAdvice(); |
160
|
|
|
} |
161
|
|
|
} |
162
|
|
|
} |
163
|
|
|
|
164
|
|
|
// Check methods in class only for method filters |
165
|
|
|
if ($filterKind & Aop\PointFilter::KIND_METHOD) { |
166
|
|
|
|
167
|
|
|
$mask = ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED; |
168
|
|
|
foreach ($class->getMethods($mask) as $method) { |
169
|
|
|
/** @var $method ReflectionMethod| */ |
170
|
|
|
if ($filter->matches($method)) { |
171
|
|
|
$prefix = $method->isStatic() ? AspectContainer::STATIC_METHOD_PREFIX : AspectContainer::METHOD_PREFIX; |
172
|
|
|
$classAdvices[$prefix][$method->name][$advisorId] = $advisor->getAdvice(); |
173
|
|
|
} |
174
|
|
|
} |
175
|
|
|
} |
176
|
|
|
|
177
|
|
|
// Check properties in class only for property filters |
178
|
|
|
if ($filterKind & Aop\PointFilter::KIND_PROPERTY) { |
179
|
|
|
$mask = ReflectionProperty::IS_PUBLIC | ReflectionProperty::IS_PROTECTED; |
180
|
|
|
foreach ($class->getProperties($mask) as $property) { |
181
|
|
|
/** @var $property ReflectionProperty */ |
182
|
|
|
if ($filter->matches($property) && !$property->isStatic()) { |
183
|
|
|
$classAdvices[AspectContainer::PROPERTY_PREFIX][$property->name][$advisorId] = $advisor->getAdvice(); |
184
|
|
|
} |
185
|
|
|
} |
186
|
|
|
} |
187
|
|
|
|
188
|
|
|
return $classAdvices; |
189
|
|
|
} |
190
|
|
|
|
191
|
|
|
/** |
192
|
|
|
* Returns list of introduction advices from advisor |
193
|
|
|
* |
194
|
|
|
* @param ParsedReflectionClass $class Class to inject advices |
195
|
|
|
* @param Aop\IntroductionAdvisor $advisor Advisor for class |
196
|
|
|
* @param string $advisorId Identifier of advisor |
197
|
|
|
* |
198
|
|
|
* @return array |
199
|
|
|
*/ |
200
|
|
|
private function getIntroductionFromAdvisor( |
201
|
|
|
ParsedReflectionClass $class, |
202
|
|
|
Aop\IntroductionAdvisor $advisor, |
203
|
|
|
$advisorId) |
204
|
|
|
{ |
205
|
|
|
$classAdvices = []; |
206
|
|
|
// Do not make introduction for traits |
207
|
|
|
if ($class->isTrait()) { |
208
|
|
|
return $classAdvices; |
209
|
|
|
} |
210
|
|
|
|
211
|
|
|
$advice = $advisor->getAdvice(); |
212
|
|
|
|
213
|
|
|
$classAdvices[AspectContainer::INTRODUCTION_TRAIT_PREFIX][$advisorId] = $advice; |
214
|
|
|
|
215
|
|
|
return $classAdvices; |
216
|
|
|
} |
217
|
|
|
|
218
|
|
|
/** |
219
|
|
|
* Returns list of function advices for specific namespace |
220
|
|
|
* |
221
|
|
|
* @param ReflectionFileNamespace $namespace |
222
|
|
|
* @param Aop\PointcutAdvisor $advisor Advisor for class |
223
|
|
|
* @param string $advisorId Identifier of advisor |
224
|
|
|
* @param Aop\PointFilter $pointcut Filter for points |
225
|
|
|
* |
226
|
|
|
* @return array |
227
|
|
|
*/ |
228
|
|
|
private function getFunctionAdvicesFromAdvisor( |
229
|
|
|
ReflectionFileNamespace $namespace, |
230
|
|
|
Aop\PointcutAdvisor $advisor, |
231
|
|
|
$advisorId, |
232
|
|
|
Aop\PointFilter $pointcut) |
233
|
|
|
{ |
234
|
|
|
$functions = []; |
235
|
|
|
$advices = []; |
236
|
|
|
|
237
|
|
|
$listOfGlobalFunctions = get_defined_functions(); |
238
|
|
|
foreach ($listOfGlobalFunctions['internal'] as $functionName) { |
239
|
|
|
$functions[$functionName] = new NamespacedReflectionFunction($functionName, $namespace->getName()); |
240
|
|
|
} |
241
|
|
|
|
242
|
|
|
foreach ($functions as $functionName=>$function) { |
243
|
|
|
if ($pointcut->matches($function)) { |
244
|
|
|
$advices[AspectContainer::FUNCTION_PREFIX][$functionName][$advisorId] = $advisor->getAdvice(); |
245
|
|
|
} |
246
|
|
|
} |
247
|
|
|
|
248
|
|
|
return $advices; |
249
|
|
|
} |
250
|
|
|
} |
251
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.