Completed
Push — 1.x ( f0f518...ba191d )
by Alexander
03:02
created

AdviceMatcher   A

Complexity

Total Complexity 35

Size/Duplication

Total Lines 228
Duplicated Lines 2.63 %

Coupling/Cohesion

Components 1
Dependencies 5

Importance

Changes 8
Bugs 2 Features 1
Metric Value
wmc 35
c 8
b 2
f 1
lcom 1
cbo 5
dl 6
loc 228
rs 9

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
C getAdvicesForFunctions() 0 25 7
C getAdvicesForClass() 0 36 8
C getAdvicesFromAdvisor() 6 49 13
A getIntroductionFromAdvisor() 0 17 2
B getFunctionAdvicesFromAdvisor() 0 22 4

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

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) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
155
                    $classAdvices[AspectContainer::INIT_PREFIX]['root'][$advisorId] = $advisor->getAdvice();
156
                }
157
                // Static initalization
158 View Code Duplication
                if ($filterKind & Aop\PointFilter::KIND_STATIC_INIT) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
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