Completed
Push — 2.x ( 56f1be...dc7414 )
by Nikola
03:08
created

AdviceMatcher   B

Complexity

Total Complexity 38

Size/Duplication

Total Lines 226
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 48.05%

Importance

Changes 0
Metric Value
wmc 38
lcom 1
cbo 6
dl 0
loc 226
ccs 37
cts 77
cp 0.4805
rs 8.3999
c 0
b 0
f 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A getIntroductionFromAdvisor() 0 17 2
B getFunctionAdvicesFromAdvisor() 0 22 4
C getAdvicesForFunctions() 0 23 7
C getAdvicesForClass() 0 33 8
C getAdvicesFromAdvisor() 0 52 16
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;
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) {
69
            if ($advisor instanceof Aop\PointcutAdvisor) {
70
                $pointcut = $advisor->getPointcut();
71
                $isFunctionAdvisor = $pointcut->getKind() & Aop\PointFilter::KIND_FUNCTION;
72
                if ($isFunctionAdvisor && $pointcut->getClassFilter()->matches($namespace)) {
73
                    $advices = array_merge_recursive(
74
                        $advices,
75
                        $this->getFunctionAdvicesFromAdvisor($namespace, $advisor, $advisorId, $pointcut)
76
                    );
77
                }
78
            }
79
        }
80
81
        return $advices;
82
    }
83
84
    /**
85
     * Return list of advices for class
86
     *
87
     * @param ReflectionClass $class Class to advise
88
     * @param array|Aop\Advisor[] $advisors List of advisor to match
89
     *
90
     * @return array|Aop\Advice[] List of advices for class
91
     */
92 3
    public function getAdvicesForClass(ReflectionClass $class, array $advisors)
93
    {
94 3
        $classAdvices = [];
95 3
        $parentClass  = $class->getParentClass();
96
97 3
        $originalClass = $class;
98 3
        if ($parentClass && preg_match('/' . AspectContainer::AOP_PROXIED_SUFFIX . '$/', $parentClass->name)) {
99
            $originalClass = $parentClass;
100
        }
101
102 3
        foreach ($advisors as $advisorId => $advisor) {
103 2
            if ($advisor instanceof Aop\PointcutAdvisor) {
104 2
                $pointcut = $advisor->getPointcut();
105 2
                if ($pointcut->getClassFilter()->matches($class)) {
106 2
                    $classAdvices = array_merge_recursive(
107 2
                        $classAdvices,
108 2
                        $this->getAdvicesFromAdvisor($originalClass, $advisor, $advisorId, $pointcut)
109
                    );
110
                }
111
            }
112
113 2
            if ($advisor instanceof Aop\IntroductionAdvisor) {
114
                if ($advisor->getClassFilter()->matches($class)) {
115
                    $classAdvices = array_merge_recursive(
116
                        $classAdvices,
117 2
                        $this->getIntroductionFromAdvisor($originalClass, $advisor, $advisorId)
118
                    );
119
                }
120
            }
121
        }
122
123 3
        return $classAdvices;
124
    }
125
126
    /**
127
     * Returns list of advices from advisor and point filter
128
     *
129
     * @param ReflectionClass $class Class to inject advices
130
     * @param Aop\PointcutAdvisor $advisor Advisor for class
131
     * @param string $advisorId Identifier of advisor
132
     * @param Aop\PointFilter $filter Filter for points
133
     *
134
     * @return array
135
     */
136 2
    private function getAdvicesFromAdvisor(
137
        ReflectionClass $class,
138
        Aop\PointcutAdvisor $advisor,
139
        $advisorId,
140
        Aop\PointFilter $filter
141
    ) {
142 2
        $classAdvices = [];
143 2
        $filterKind   = $filter->getKind();
144
145
        // Check class only for class filters
146 2
        if ($filterKind & Aop\PointFilter::KIND_CLASS) {
147
            if ($filter->matches($class)) {
148
                // Dynamic initialization
149
                if ($filterKind & Aop\PointFilter::KIND_INIT) {
150
                    $classAdvices[AspectContainer::INIT_PREFIX]['root'][$advisorId] = $advisor->getAdvice();
151
                }
152
                // Static initalization
153
                if ($filterKind & Aop\PointFilter::KIND_STATIC_INIT) {
154
                    $classAdvices[AspectContainer::STATIC_INIT_PREFIX]['root'][$advisorId] = $advisor->getAdvice();
155
                }
156
            }
157
        }
158
159
        // Check methods in class only for method filters
160 2
        if ($filterKind & Aop\PointFilter::KIND_METHOD) {
161 1
            $mask = ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED;
162 1
            foreach ($class->getMethods($mask) as $method) {
163
                // abstract and parent final methods could not be woven
164 1
                $isParentFinalMethod = ($method->getDeclaringClass()->name !== $class->name) && $method->isFinal();
165 1
                if ($isParentFinalMethod || $method->isAbstract()) {
166
                    continue;
167
                }
168
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
202
    ) {
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
231
    ) {
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) {
241
            if ($pointcut->matches($function, $namespace)) {
242
                $advices[AspectContainer::FUNCTION_PREFIX][$functionName][$advisorId] = $advisor->getAdvice();
243
            }
244
        }
245
246
        return $advices;
247
    }
248
}
249