Completed
Pull Request — master (#333)
by Nikola
04:41
created

AdviceMatcher   A

Complexity

Total Complexity 35

Size/Duplication

Total Lines 226
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 47.3%

Importance

Changes 0
Metric Value
wmc 35
c 0
b 0
f 0
lcom 1
cbo 6
dl 0
loc 226
ccs 35
cts 74
cp 0.473
rs 9

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
C getAdvicesFromAdvisor() 0 47 13
A getIntroductionFromAdvisor() 0 17 2
B getFunctionAdvicesFromAdvisor() 0 22 4
C getAdvicesForClass() 0 36 8
C getAdvicesForFunctions() 0 25 7
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 Go\Aop;
14
use Go\Aop\Support\NamespacedReflectionFunction;
15
use Go\ParserReflection\ReflectionFileNamespace;
16
use ReflectionClass;
17
use ReflectionMethod;
18
use ReflectionProperty;
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 4
    public function __construct(AspectLoader $loader, $isInterceptFunctions = false)
46
    {
47 4
        $this->loader = $loader;
48
49 4
        $this->isInterceptFunctions = $isInterceptFunctions;
50 4
    }
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) {
0 ignored issues
show
Coding Style introduced by
Blank line found at start of control structure
Loading history...
69
70
            if ($advisor instanceof Aop\PointcutAdvisor) {
0 ignored issues
show
Coding Style introduced by
Blank line found at start of control structure
Loading history...
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 ReflectionClass $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 3
    public function getAdvicesForClass(ReflectionClass $class, array $advisors)
95
    {
96 3
        $classAdvices = [];
97 3
        $parentClass  = $class->getParentClass();
98
99 3
        if ($parentClass && preg_match('/' . AspectContainer::AOP_PROXIED_SUFFIX . '$/', $parentClass->name)) {
100
            $originalClass = $parentClass;
101
        } else {
102 3
            $originalClass = $class;
103
        }
104
105 3
        foreach ($advisors as $advisorId => $advisor) {
0 ignored issues
show
Coding Style introduced by
Blank line found at start of control structure
Loading history...
106
107 2
            if ($advisor instanceof Aop\PointcutAdvisor) {
0 ignored issues
show
Coding Style introduced by
Blank line found at start of control structure
Loading history...
108
109 2
                $pointcut = $advisor->getPointcut();
110 2
                if ($pointcut->getClassFilter()->matches($class)) {
111 2
                    $classAdvices = array_merge_recursive(
112 2
                        $classAdvices,
113 2
                        $this->getAdvicesFromAdvisor($originalClass, $advisor, $advisorId, $pointcut)
114
                    );
115
                }
116
            }
117
118 2
            if ($advisor instanceof Aop\IntroductionAdvisor) {
119
                if ($advisor->getClassFilter()->matches($class)) {
120
                    $classAdvices = array_merge_recursive(
121
                        $classAdvices,
122 2
                        $this->getIntroductionFromAdvisor($originalClass, $advisor, $advisorId)
123
                    );
124
                }
125
            }
126
        }
127
128 3
        return $classAdvices;
129
    }
130
131
    /**
132
     * Returns list of advices from advisor and point filter
133
     *
134
     * @param ReflectionClass $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 2
    private function getAdvicesFromAdvisor(
142
        ReflectionClass $class,
143
        Aop\PointcutAdvisor $advisor,
144
        $advisorId,
145
        Aop\PointFilter $filter)
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...
146
    {
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...
147 2
        $classAdvices = [];
148 2
        $filterKind   = $filter->getKind();
149
150
        // Check class only for class filters
151 2
        if ($filterKind & Aop\PointFilter::KIND_CLASS) {
152
            if ($filter->matches($class)) {
153
                // Dynamic initialization
154
                if ($filterKind & Aop\PointFilter::KIND_INIT) {
155
                    $classAdvices[AspectContainer::INIT_PREFIX]['root'][$advisorId] = $advisor->getAdvice();
156
                }
157
                // Static initalization
158
                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 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...
166
167 1
            $mask = ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED;
168 1
            foreach ($class->getMethods($mask) as $method) {
169 1
                if ($filter->matches($method, $class)) {
170 1
                    $prefix = $method->isStatic() ? AspectContainer::STATIC_METHOD_PREFIX : AspectContainer::METHOD_PREFIX;
171 1
                    $classAdvices[$prefix][$method->name][$advisorId] = $advisor->getAdvice();
172
                }
173
            }
174
        }
175
176
        // Check properties in class only for property filters
177 2
        if ($filterKind & Aop\PointFilter::KIND_PROPERTY) {
178 1
            $mask = ReflectionProperty::IS_PUBLIC | ReflectionProperty::IS_PROTECTED;
179 1
            foreach ($class->getProperties($mask) as $property) {
180 1
                if ($filter->matches($property, $class) && !$property->isStatic()) {
181 1
                    $classAdvices[AspectContainer::PROPERTY_PREFIX][$property->name][$advisorId] = $advisor->getAdvice();
182
                }
183
            }
184
        }
185
186 2
        return $classAdvices;
187
    }
188
189
    /**
190
     * Returns list of introduction advices from advisor
191
     *
192
     * @param ReflectionClass $class Class to inject advices
193
     * @param Aop\IntroductionAdvisor $advisor Advisor for class
194
     * @param string $advisorId Identifier of advisor
195
     *
196
     * @return array
197
     */
198
    private function getIntroductionFromAdvisor(
199
        ReflectionClass $class,
200
        Aop\IntroductionAdvisor $advisor,
201
        $advisorId)
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...
202
    {
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...
203
        $classAdvices = [];
204
        // Do not make introduction for traits
205
        if ($class->isTrait()) {
206
            return $classAdvices;
207
        }
208
209
        $advice = $advisor->getAdvice();
210
211
        $classAdvices[AspectContainer::INTRODUCTION_TRAIT_PREFIX][$advisorId] = $advice;
212
213
        return $classAdvices;
214
    }
215
216
    /**
217
     * Returns list of function advices for specific namespace
218
     *
219
     * @param ReflectionFileNamespace $namespace
220
     * @param Aop\PointcutAdvisor $advisor Advisor for class
221
     * @param string $advisorId Identifier of advisor
222
     * @param Aop\PointFilter $pointcut Filter for points
223
     *
224
     * @return array
225
     */
226
    private function getFunctionAdvicesFromAdvisor(
227
        ReflectionFileNamespace $namespace,
228
        Aop\PointcutAdvisor $advisor,
229
        $advisorId,
230
        Aop\PointFilter $pointcut)
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...
231
    {
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...
232
        $functions = [];
233
        $advices   = [];
234
235
        $listOfGlobalFunctions = get_defined_functions();
236
        foreach ($listOfGlobalFunctions['internal'] as $functionName) {
237
            $functions[$functionName] = new NamespacedReflectionFunction($functionName, $namespace->getName());
238
        }
239
240
        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...
241
            if ($pointcut->matches($function, $namespace)) {
242
                $advices[AspectContainer::FUNCTION_PREFIX][$functionName][$advisorId] = $advisor->getAdvice();
243
            }
244
        }
245
246
        return $advices;
247
    }
248
}
249