Completed
Push — master ( b6d688...cba8ad )
by Alexander
11s
created

AdviceMatcher::getAdvicesForFunctions()   B

Complexity

Conditions 6
Paths 5

Size

Total Lines 25
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 22.384

Importance

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