Completed
Push — master ( 06ef8e...568e3c )
by Alexander
03:25
created

PointcutGrammar::__construct()   B

Complexity

Conditions 2
Paths 1

Size

Total Lines 285
Code Lines 203

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 227
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 285
ccs 227
cts 228
cp 0.9956
rs 8.2857
c 0
b 0
f 0
cc 2
eloc 203
nc 1
nop 2
crap 2

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
declare(strict_types = 1);
3
/*
4
 * Go! AOP framework
5
 *
6
 * @copyright Copyright 2013, 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\Aop\Pointcut;
13
14
use Closure;
15
use Dissect\Lexer\Token;
16
use Dissect\Parser\Grammar;
17
use Doctrine\Common\Annotations\Reader;
18
use Go\Aop\PointFilter;
19
use Go\Aop\Support\AndPointFilter;
20
use Go\Aop\Support\InheritanceClassFilter;
21
use Go\Aop\Support\ModifierMatcherFilter;
22
use Go\Aop\Support\SimpleNamespaceFilter;
23
use Go\Aop\Support\TruePointFilter;
24
use Go\Core\AspectContainer;
25
26
/**
27
 * Pointcut grammar defines general structure of pointcuts and rules of parsing
28
 */
29
class PointcutGrammar extends Grammar
30
{
31
    /**
32
     * Constructs a pointcut grammar with AST
33
     *
34
     * @param AspectContainer $container Instance of the container
35
     * @param Reader $annotationReader
36
     */
37 29
    public function __construct(AspectContainer $container, Reader $annotationReader)
38
    {
39 29
        $this('empty')
40 29
            ->is(/* empty */);
41
42 29
        $stringConverter = $this->getNodeToStringConverter();
43
44 29
        $this('pointcutExpression')
45 29
            ->is('pointcutExpression', '||', 'conjugatedExpression')
46 29
            ->call(function ($first, $_0, $second) {
47 2
                return new OrPointcut($first, $second);
48 29
            })
49 29
            ->is('conjugatedExpression');
50
51 29
        $this('conjugatedExpression')
52 29
            ->is('conjugatedExpression', '&&', 'negatedExpression')
53 29
            ->call(function ($first, $_0, $second) {
54 3
                return new AndPointcut($first, $second);
55 29
            })
56 29
            ->is('negatedExpression');
57
58 29
        $this('negatedExpression')
59 29
            ->is('!', 'brakedExpression')
60 29
            ->call(function ($_0, $item) {
61 2
                return new NotPointcut($item);
62 29
            })
63 29
            ->is('brakedExpression');
64
65 29
        $this('brakedExpression')
66 29
            ->is('(', 'pointcutExpression', ')')
67 29
            ->call(function ($_0, $e, $_1) {
0 ignored issues
show
Unused Code introduced by
The parameter $_1 is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
68 1
                return $e;
69 29
            })
70 29
            ->is('singlePointcut');
71
72 29
        $this('singlePointcut')
73 29
            ->is('accessPointcut')
74 29
            ->is('annotatedAccessPointcut')
75 29
            ->is('executionPointcut')
76 29
            ->is('annotatedExecutionPointcut')
77 29
            ->is('withinPointcut')
78 29
            ->is('annotatedWithinPointcut')
79 29
            ->is('initializationPointcut')
80 29
            ->is('staticInitializationPointcut')
81 29
            ->is('cflowbelowPointcut')
82 29
            ->is('dynamicExecutionPointcut')
83 29
            ->is('matchInheritedPointcut')
84 29
            ->is('pointcutReference');
85
86 29
        $this('accessPointcut')
87 29
            ->is('access', '(', 'propertyAccessReference', ')')
88 29
            ->call(function ($_0, $_1, $propertyReference) {
89 2
                return $propertyReference;
90 29
            });
91
92 29
        $this('executionPointcut')
93 29
            ->is('execution', '(', 'methodExecutionReference', ')')
94 29
            ->call(function ($_0, $_1, $methodReference) {
95 6
                return $methodReference;
96 29
            })
97 29
            ->is('execution', '(', 'functionExecutionReference', ')')
98 29
            ->call(function ($_0, $_1, $functionReference) {
99 3
                return $functionReference;
100 29
            });
101
102 29
        $this('withinPointcut')
103 29
            ->is('within', '(', 'classFilter', ')')
104 29
            ->call(function ($_0, $_1, $classFilter) {
105 8
                $pointcut = new TruePointcut(PointFilter::KIND_ALL);
106 8
                $pointcut->setClassFilter($classFilter);
107
108 8
                return $pointcut;
109 29
            });
110
111 29
        $this('annotatedAccessPointcut')
112 29
            ->is('annotation', 'access', '(', 'namespaceName', ')')
113 29
            ->call(function ($_0, $_1, $_2, $annotationClassName) use ($annotationReader) {
114 1
                $kindProperty = PointFilter::KIND_PROPERTY;
115 1
                return new AnnotationPointcut($kindProperty, $annotationReader, $annotationClassName);
116 29
            });
117
118 29
        $this('annotatedExecutionPointcut')
119 29
            ->is('annotation', 'execution', '(', 'namespaceName', ')')
120 29
            ->call(function ($_0, $_1, $_2, $annotationClassName) use ($annotationReader) {
121 1
                $kindMethod = PointFilter::KIND_METHOD;
122 1
                return new AnnotationPointcut($kindMethod, $annotationReader, $annotationClassName);
123 29
            });
124
125 29
        $this('annotatedWithinPointcut')
126 29
            ->is('annotation', 'within', '(', 'namespaceName', ')')
127 29
            ->call(function ($_0, $_1, $_2, $annotationClassName) use ($annotationReader) {
128 1
                $pointcut    = new TruePointcut(PointFilter::KIND_ALL);
129 1
                $kindClass   = PointFilter::KIND_CLASS;
130 1
                $classFilter = new AnnotationPointcut($kindClass, $annotationReader, $annotationClassName);
131 1
                $pointcut->setClassFilter($classFilter);
132
133 1
                return $pointcut;
134 29
            });
135
136 29
        $this('initializationPointcut')
137 29
            ->is('initialization', '(', 'classFilter', ')')
138 29
            ->call(function ($_0, $_1, $classFilter) {
139 1
                $pointcut = new TruePointcut(PointFilter::KIND_INIT + PointFilter::KIND_CLASS);
140 1
                $pointcut->setClassFilter($classFilter);
141
142 1
                return $pointcut;
143 29
            });
144
145 29
        $this('staticInitializationPointcut')
146 29
            ->is('staticinitialization', '(', 'classFilter', ')')
147 29
            ->call(function ($_0, $_1, $classFilter) {
148 1
                $pointcut = new TruePointcut(PointFilter::KIND_STATIC_INIT + PointFilter::KIND_CLASS);
149 1
                $pointcut->setClassFilter($classFilter);
150
151 1
                return $pointcut;
152 29
            });
153
154 29
        $this('cflowbelowPointcut')
155 29
            ->is('cflowbelow', '(', 'executionPointcut', ')')
156 29
            ->call(function ($_0, $_1, $pointcut) {
157 1
                return new CFlowBelowMethodPointcut($pointcut);
158 29
            });
159
160 29
        $this('matchInheritedPointcut')
161 29
            ->is('matchInherited', '(', ')')
162 29
            ->call(function () {
163 1
                return new MatchInheritedPointcut();
164 29
            });
165
166 29
        $this('dynamicExecutionPointcut')
167
            // ideally, this should be 'dynamic', 'methodExecutionReference'
168 29
            ->is('dynamic', '(', 'memberReference', '(', 'argumentList', ')', ')')
169 29
            ->call(function ($_0, $_1, ClassMemberReference $reference) {
170 2
                $memberFilter = new AndPointFilter(
171 2
                    $reference->getVisibilityFilter(),
172 2
                    $reference->getAccessTypeFilter()
173
                );
174
175 2
                $pointcut = new MagicMethodPointcut(
176 2
                    $reference->getMemberNamePattern(),
177 2
                    $memberFilter);
178 2
                $pointcut->setClassFilter($reference->getClassFilter());
179
180 2
                return $pointcut;
181 29
            });
182
183 29
        $this('pointcutReference')
184 29
            ->is('namespaceName', '->', 'namePatternPart')
185 29
            ->call(function ($className, $_0, $name) use ($container) {
186
                return new PointcutReference($container, "{$className}->{$name}");
187 29
            });
188
189 29
        $this('propertyAccessReference')
190 29
            ->is('memberReference')
191 29
            ->call(function (ClassMemberReference $reference) {
192 2
                $memberFilter = new AndPointFilter(
193 2
                    $reference->getVisibilityFilter(),
194 2
                    $reference->getAccessTypeFilter()
195
                );
196
197 2
                $pointcut = new SignaturePointcut(
198 2
                    PointFilter::KIND_PROPERTY,
199 2
                    $reference->getMemberNamePattern(),
200 2
                    $memberFilter);
201
202 2
                $pointcut->setClassFilter($reference->getClassFilter());
203
204 2
                return $pointcut;
205 29
            });
206
207 29
        $this('methodExecutionReference')
208 29
            ->is('memberReference', '(', 'argumentList', ')')
209 29
            ->call(function (ClassMemberReference $reference) {
210 6
                $memberFilter = new AndPointFilter(
211 6
                    $reference->getVisibilityFilter(),
212 6
                    $reference->getAccessTypeFilter()
213
                );
214
215 6
                $pointcut = new SignaturePointcut(
216 6
                    PointFilter::KIND_METHOD,
217 6
                    $reference->getMemberNamePattern(),
218 6
                    $memberFilter);
219
220 6
                $pointcut->setClassFilter($reference->getClassFilter());
221
222 6
                return $pointcut;
223 29
            });
224
225 29
        $this('functionExecutionReference')
226 29
            ->is('namespacePattern', 'nsSeparator', 'namePatternPart', '(', 'argumentList', ')')
227 29
            ->call(function ($namespacePattern, $_0, $namePattern) {
228 3
                $nsFilter = new SimpleNamespaceFilter($namespacePattern);
229 3
                $pointcut = new FunctionPointcut($namePattern);
230 3
                $pointcut->setNamespaceFilter($nsFilter);
231
232 3
                return $pointcut;
233 29
            });
234
235 29
        $this('memberReference')
236 29
            ->is('memberModifiers', 'classFilter', 'memberAccessType', 'namePatternPart')
237 29
            ->call(function (
238
                ModifierMatcherFilter $memberModifiers,
239
                PointFilter $classFilter,
240
                ModifierMatcherFilter $memberAccessType,
241
                $namePattern
242
            ) {
243 10
                $reference = new ClassMemberReference(
244 10
                    $classFilter,
245 10
                    $memberModifiers,
246 10
                    $memberAccessType,
247 10
                    $namePattern);
248
249 10
                return $reference;
250 29
            });
251
252 29
        $this('classFilter')
253 29
            ->is('namespacePattern')
254 29
            ->call(function ($pattern) {
255 18
                $truePointFilter = TruePointFilter::getInstance();
256
257 18
                return $pattern === '**'
258 3
                    ? $truePointFilter
259 18
                    : new SignaturePointcut(PointFilter::KIND_CLASS, $pattern, $truePointFilter);
260 29
            })
261 29
            ->is('namespacePattern', '+')
262 29
            ->call(function ($parentClassName) {
263 5
                return new InheritanceClassFilter($parentClassName);
264 29
            });
265
266 29
        $this('argumentList')
267 29
            ->is('*');
268
269 29
        $this('memberAccessType')
270 29
            ->is('::')
271 29
            ->call(function () {
272 2
                return new ModifierMatcherFilter(\ReflectionMethod::IS_STATIC);
273 29
            })
274 29
            ->is('->')
275 29
            ->call(function () {
276 8
                $modifierMatcherFilter = new ModifierMatcherFilter();
277 8
                $modifierMatcherFilter->notMatch(\ReflectionMethod::IS_STATIC);
278
279 8
                return $modifierMatcherFilter;
280 29
            });
281
282 29
        $this('namespacePattern')
283 29
            ->is('**')->call($stringConverter)
284 29
            ->is('namePatternPart')
285 29
            ->is('namespacePattern', 'nsSeparator', 'namePatternPart')->call($stringConverter)
286 29
            ->is('namespacePattern', 'nsSeparator', '**')->call($stringConverter);
287
288 29
        $this('namePatternPart')
289 29
            ->is('*')->call($stringConverter)
290 29
            ->is('namePart')->call($stringConverter)
291 29
            ->is('namePatternPart', '*')->call($stringConverter)
292 29
            ->is('namePatternPart', 'namePart')->call($stringConverter)
293 29
            ->is('namePatternPart', '|', 'namePart')->call($stringConverter);
294
295 29
        $this('namespaceName')
296 29
            ->is('namePart')->call($stringConverter)
297 29
            ->is('namespaceName', 'nsSeparator', 'namePart')->call($stringConverter);
298
299 29
        $this('memberModifiers')
300 29
            ->is('memberModifier', '|', 'memberModifiers')
301 29
            ->call(function ($modifier, $_0, ModifierMatcherFilter $matcher) {
302 2
                return $matcher->orMatch($modifier);
303 29
            })
304 29
            ->is('memberModifier', 'memberModifiers')
305 29
            ->call(function ($modifier, ModifierMatcherFilter $matcher) {
306 1
                return $matcher->andMatch($modifier);
307 29
            })
308 29
            ->is('memberModifier')
309 29
            ->call(function ($modifier) {
310 10
                return new ModifierMatcherFilter($modifier);
311 29
            });
312
313 29
        $converter = $this->getModifierConverter();
314 29
        $this('memberModifier')
315 29
            ->is('public')->call($converter)
316 29
            ->is('protected')->call($converter)
317 29
            ->is('private')->call($converter)
318 29
            ->is('final')->call($converter);
319
320 29
        $this->start('pointcutExpression');
321 29
    }
322
323
    /**
324
     * Returns callable for converting node(s) to the string
325
     */
326
    private function getNodeToStringConverter(): Closure
327
    {
328 29
        return function(...$nodes) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space after FUNCTION keyword; 0 found
Loading history...
329 26
            $value = '';
330 26
            foreach ($nodes as $node) {
331 26
                if (is_scalar($node)) {
332 20
                    $value .= $node;
333
                } else {
334 26
                    $value .= $node->getValue();
335
                }
336
            }
337
338 26
            return $value;
339 29
        };
340
    }
341
342
    /**
343
     * Returns callable for converting node value for modifiers to the constant value
344
     */
345
    private function getModifierConverter(): Closure
346
    {
347 29
        return function (Token $token) {
348 10
            $name = strtoupper($token->getValue());
349
350 10
            return constant("ReflectionMethod::IS_{$name}");
351 29
        };
352
    }
353
}
354