Completed
Push — master ( 7595d7...36684b )
by Alexander
04:15
created

PointcutGrammar   C

Complexity

Total Complexity 6

Size/Duplication

Total Lines 354
Duplicated Lines 0 %

Coupling/Cohesion

Components 0
Dependencies 20

Test Coverage

Coverage 99.61%

Importance

Changes 0
Metric Value
wmc 6
lcom 0
cbo 20
dl 0
loc 354
ccs 258
cts 259
cp 0.9961
rs 6.4705
c 0
b 0
f 0

3 Methods

Rating   Name   Duplication   Size   Complexity  
A getNodeToStringConverter() 0 15 3
B __construct() 0 314 2
A getModifierConverter() 0 8 1
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\ReturnTypeFilter;
23
use Go\Aop\Support\SimpleNamespaceFilter;
24
use Go\Aop\Support\TruePointFilter;
25
use Go\Core\AspectContainer;
26
27
/**
28
 * Pointcut grammar defines general structure of pointcuts and rules of parsing
29
 */
30
class PointcutGrammar extends Grammar
31
{
32
    /**
33
     * Constructs a pointcut grammar with AST
34
     *
35
     * @param AspectContainer $container Instance of the container
36
     * @param Reader $annotationReader
37
     */
38 31
    public function __construct(AspectContainer $container, Reader $annotationReader)
39
    {
40 31
        $this('empty')
41 31
            ->is(/* empty */);
42
43 31
        $stringConverter = $this->getNodeToStringConverter();
44
45 31
        $this('pointcutExpression')
46 31
            ->is('pointcutExpression', '||', 'conjugatedExpression')
47 31
            ->call(function ($first, $_0, $second) {
48 2
                return new OrPointcut($first, $second);
49 31
            })
50 31
            ->is('conjugatedExpression');
51
52 31
        $this('conjugatedExpression')
53 31
            ->is('conjugatedExpression', '&&', 'negatedExpression')
54 31
            ->call(function ($first, $_0, $second) {
55 3
                return new AndPointcut($first, $second);
56 31
            })
57 31
            ->is('negatedExpression');
58
59 31
        $this('negatedExpression')
60 31
            ->is('!', 'brakedExpression')
61 31
            ->call(function ($_0, $item) {
62 2
                return new NotPointcut($item);
63 31
            })
64 31
            ->is('brakedExpression');
65
66 31
        $this('brakedExpression')
67 31
            ->is('(', 'pointcutExpression', ')')
68 31
            ->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...
69 1
                return $e;
70 31
            })
71 31
            ->is('singlePointcut');
72
73 31
        $this('singlePointcut')
74 31
            ->is('accessPointcut')
75 31
            ->is('annotatedAccessPointcut')
76 31
            ->is('executionPointcut')
77 31
            ->is('annotatedExecutionPointcut')
78 31
            ->is('withinPointcut')
79 31
            ->is('annotatedWithinPointcut')
80 31
            ->is('initializationPointcut')
81 31
            ->is('staticInitializationPointcut')
82 31
            ->is('cflowbelowPointcut')
83 31
            ->is('dynamicExecutionPointcut')
84 31
            ->is('matchInheritedPointcut')
85 31
            ->is('pointcutReference');
86
87 31
        $this('accessPointcut')
88 31
            ->is('access', '(', 'propertyAccessReference', ')')
89 31
            ->call(function ($_0, $_1, $propertyReference) {
90 2
                return $propertyReference;
91 31
            });
92
93 31
        $this('executionPointcut')
94 31
            ->is('execution', '(', 'methodExecutionReference', ')')
95 31
            ->call(function ($_0, $_1, $methodReference) {
96 7
                return $methodReference;
97 31
            })
98 31
            ->is('execution', '(', 'functionExecutionReference', ')')
99 31
            ->call(function ($_0, $_1, $functionReference) {
100 4
                return $functionReference;
101 31
            });
102
103 31
        $this('withinPointcut')
104 31
            ->is('within', '(', 'classFilter', ')')
105 31
            ->call(function ($_0, $_1, $classFilter) {
106 8
                $pointcut = new TruePointcut(PointFilter::KIND_ALL);
107 8
                $pointcut->setClassFilter($classFilter);
108
109 8
                return $pointcut;
110 31
            });
111
112 31
        $this('annotatedAccessPointcut')
113 31
            ->is('annotation', 'access', '(', 'namespaceName', ')')
114 31
            ->call(function ($_0, $_1, $_2, $annotationClassName) use ($annotationReader) {
115 1
                $kindProperty = PointFilter::KIND_PROPERTY;
116
117 1
                return new AnnotationPointcut($kindProperty, $annotationReader, $annotationClassName);
118 31
            });
119
120 31
        $this('annotatedExecutionPointcut')
121 31
            ->is('annotation', 'execution', '(', 'namespaceName', ')')
122 31
            ->call(function ($_0, $_1, $_2, $annotationClassName) use ($annotationReader) {
123 1
                $kindMethod = PointFilter::KIND_METHOD;
124
125 1
                return new AnnotationPointcut($kindMethod, $annotationReader, $annotationClassName);
126 31
            });
127
128 31
        $this('annotatedWithinPointcut')
129 31
            ->is('annotation', 'within', '(', 'namespaceName', ')')
130 31
            ->call(function ($_0, $_1, $_2, $annotationClassName) use ($annotationReader) {
131 1
                $pointcut    = new TruePointcut(PointFilter::KIND_ALL);
132 1
                $kindClass   = PointFilter::KIND_CLASS;
133 1
                $classFilter = new AnnotationPointcut($kindClass, $annotationReader, $annotationClassName);
134 1
                $pointcut->setClassFilter($classFilter);
135
136 1
                return $pointcut;
137 31
            });
138
139 31
        $this('initializationPointcut')
140 31
            ->is('initialization', '(', 'classFilter', ')')
141 31
            ->call(function ($_0, $_1, $classFilter) {
142 1
                $pointcut = new TruePointcut(PointFilter::KIND_INIT + PointFilter::KIND_CLASS);
143 1
                $pointcut->setClassFilter($classFilter);
144
145 1
                return $pointcut;
146 31
            });
147
148 31
        $this('staticInitializationPointcut')
149 31
            ->is('staticinitialization', '(', 'classFilter', ')')
150 31
            ->call(function ($_0, $_1, $classFilter) {
151 1
                $pointcut = new TruePointcut(PointFilter::KIND_STATIC_INIT + PointFilter::KIND_CLASS);
152 1
                $pointcut->setClassFilter($classFilter);
153
154 1
                return $pointcut;
155 31
            });
156
157 31
        $this('cflowbelowPointcut')
158 31
            ->is('cflowbelow', '(', 'executionPointcut', ')')
159 31
            ->call(function ($_0, $_1, $pointcut) {
160 1
                return new CFlowBelowMethodPointcut($pointcut);
161 31
            });
162
163 31
        $this('matchInheritedPointcut')
164 31
            ->is('matchInherited', '(', ')')
165 31
            ->call(function () {
166 1
                return new MatchInheritedPointcut();
167 31
            });
168
169 31
        $this('dynamicExecutionPointcut')
170
            // ideally, this should be 'dynamic', 'methodExecutionReference'
171 31
            ->is('dynamic', '(', 'memberReference', '(', 'argumentList', ')', ')')
172 31
            ->call(function ($_0, $_1, ClassMemberReference $reference) {
173 2
                $memberFilter = new AndPointFilter(
174 2
                    $reference->getVisibilityFilter(),
175 2
                    $reference->getAccessTypeFilter()
176
                );
177
178 2
                $pointcut = new MagicMethodPointcut($reference->getMemberNamePattern(), $memberFilter);
179 2
                $pointcut->setClassFilter($reference->getClassFilter());
180
181 2
                return $pointcut;
182 31
            });
183
184 31
        $this('pointcutReference')
185 31
            ->is('namespaceName', '->', 'namePatternPart')
186 31
            ->call(function ($className, $_0, $name) use ($container) {
187
                return new PointcutReference($container, "{$className}->{$name}");
188 31
            });
189
190 31
        $this('propertyAccessReference')
191 31
            ->is('memberReference')
192 31
            ->call(function (ClassMemberReference $reference) {
193 2
                $memberFilter = new AndPointFilter(
194 2
                    $reference->getVisibilityFilter(),
195 2
                    $reference->getAccessTypeFilter()
196
                );
197
198 2
                $pointcut = new SignaturePointcut(
199 2
                    PointFilter::KIND_PROPERTY,
200 2
                    $reference->getMemberNamePattern(),
201 2
                    $memberFilter);
202
203 2
                $pointcut->setClassFilter($reference->getClassFilter());
204
205 2
                return $pointcut;
206 31
            });
207
208 31
        $this('methodExecutionReference')
209 31
            ->is('memberReference', '(', 'argumentList', ')')
210 31
            ->call(function (ClassMemberReference $reference) {
211 6
                $memberFilter = new AndPointFilter(
212 6
                    $reference->getVisibilityFilter(),
213 6
                    $reference->getAccessTypeFilter()
214
                );
215
216 6
                $pointcut = new SignaturePointcut(
217 6
                    PointFilter::KIND_METHOD,
218 6
                    $reference->getMemberNamePattern(),
219 6
                    $memberFilter
220
                );
221
222 6
                $pointcut->setClassFilter($reference->getClassFilter());
223
224 6
                return $pointcut;
225 31
            })
226 31
            ->is('memberReference', '(', 'argumentList', ')', ':', 'namespaceName')
227 31
            ->call(function (ClassMemberReference $reference, $_0, $_1, $_2, $_3, $returnType) {
228 1
                $memberFilter = new AndPointFilter(
229 1
                    $reference->getVisibilityFilter(),
230 1
                    $reference->getAccessTypeFilter(),
231 1
                    new ReturnTypeFilter($returnType)
232
                );
233
234 1
                $pointcut = new SignaturePointcut(
235 1
                    PointFilter::KIND_METHOD,
236 1
                    $reference->getMemberNamePattern(),
237 1
                    $memberFilter
238
                );
239
240 1
                $pointcut->setClassFilter($reference->getClassFilter());
241
242 1
                return $pointcut;
243 31
            });
244
245 31
        $this('functionExecutionReference')
246 31
            ->is('namespacePattern', 'nsSeparator', 'namePatternPart', '(', 'argumentList', ')')
247 31
            ->call(function ($namespacePattern, $_0, $namePattern) {
248 3
                $nsFilter = new SimpleNamespaceFilter($namespacePattern);
249 3
                $pointcut = new FunctionPointcut($namePattern);
250 3
                $pointcut->setNamespaceFilter($nsFilter);
251
252 3
                return $pointcut;
253 31
            })
254 31
            ->is('namespacePattern', 'nsSeparator', 'namePatternPart', '(', 'argumentList', ')', ':', 'namespaceName')
255 31
            ->call(function ($namespacePattern, $_0, $namePattern, $_1, $_2, $_3, $_4, $returnType) {
256 1
                $nsFilter   = new SimpleNamespaceFilter($namespacePattern);
257 1
                $typeFilter = new ReturnTypeFilter($returnType);
258 1
                $pointcut   = new FunctionPointcut($namePattern, $typeFilter);
259 1
                $pointcut->setNamespaceFilter($nsFilter);
260
261 1
                return $pointcut;
262 31
            });
263
264 31
        $this('memberReference')
265 31
            ->is('memberModifiers', 'classFilter', 'memberAccessType', 'namePatternPart')
266 31
            ->call(function (
267
                ModifierMatcherFilter $memberModifiers,
268
                PointFilter $classFilter,
269
                ModifierMatcherFilter $memberAccessType,
270
                $namePattern
271
            ) {
272 11
                $reference = new ClassMemberReference(
273 11
                    $classFilter,
274 11
                    $memberModifiers,
275 11
                    $memberAccessType,
276 11
                    $namePattern
277
                );
278
279 11
                return $reference;
280 31
            });
281
282 31
        $this('classFilter')
283 31
            ->is('namespacePattern')
284 31
            ->call(function ($pattern) {
285 19
                $truePointFilter = TruePointFilter::getInstance();
286
287 19
                return $pattern === '**'
288 3
                    ? $truePointFilter
289 19
                    : new SignaturePointcut(PointFilter::KIND_CLASS, $pattern, $truePointFilter);
290 31
            })
291 31
            ->is('namespacePattern', '+')
292 31
            ->call(function ($parentClassName) {
293 5
                return new InheritanceClassFilter($parentClassName);
294 31
            });
295
296 31
        $this('argumentList')
297 31
            ->is('*');
298
299 31
        $this('memberAccessType')
300 31
            ->is('::')
301 31
            ->call(function () {
302 2
                return new ModifierMatcherFilter(\ReflectionMethod::IS_STATIC);
303 31
            })
304 31
            ->is('->')
305 31
            ->call(function () {
306 9
                $modifierMatcherFilter = new ModifierMatcherFilter();
307 9
                $modifierMatcherFilter->notMatch(\ReflectionMethod::IS_STATIC);
308
309 9
                return $modifierMatcherFilter;
310 31
            });
311
312 31
        $this('namespacePattern')
313 31
            ->is('**')->call($stringConverter)
314 31
            ->is('namePatternPart')
315 31
            ->is('namespacePattern', 'nsSeparator', 'namePatternPart')->call($stringConverter)
316 31
            ->is('namespacePattern', 'nsSeparator', '**')->call($stringConverter);
317
318 31
        $this('namePatternPart')
319 31
            ->is('*')->call($stringConverter)
320 31
            ->is('namePart')->call($stringConverter)
321 31
            ->is('namePatternPart', '*')->call($stringConverter)
322 31
            ->is('namePatternPart', 'namePart')->call($stringConverter)
323 31
            ->is('namePatternPart', '|', 'namePart')->call($stringConverter);
324
325 31
        $this('namespaceName')
326 31
            ->is('namePart')->call($stringConverter)
327 31
            ->is('namespaceName', 'nsSeparator', 'namePart')->call($stringConverter);
328
329 31
        $this('memberModifiers')
330 31
            ->is('memberModifier', '|', 'memberModifiers')
331 31
            ->call(function ($modifier, $_0, ModifierMatcherFilter $matcher) {
332 2
                return $matcher->orMatch($modifier);
333 31
            })
334 31
            ->is('memberModifier', 'memberModifiers')
335 31
            ->call(function ($modifier, ModifierMatcherFilter $matcher) {
336 1
                return $matcher->andMatch($modifier);
337 31
            })
338 31
            ->is('memberModifier')
339 31
            ->call(function ($modifier) {
340 11
                return new ModifierMatcherFilter($modifier);
341 31
            });
342
343 31
        $converter = $this->getModifierConverter();
344 31
        $this('memberModifier')
345 31
            ->is('public')->call($converter)
346 31
            ->is('protected')->call($converter)
347 31
            ->is('private')->call($converter)
348 31
            ->is('final')->call($converter);
349
350 31
        $this->start('pointcutExpression');
351 31
    }
352
353
    /**
354
     * Returns callable for converting node(s) to the string
355
     */
356
    private function getNodeToStringConverter(): Closure
357
    {
358 31
        return function (...$nodes) {
359 28
            $value = '';
360 28
            foreach ($nodes as $node) {
361 28
                if (is_scalar($node)) {
362 21
                    $value .= $node;
363
                } else {
364 28
                    $value .= $node->getValue();
365
                }
366
            }
367
368 28
            return $value;
369 31
        };
370
    }
371
372
    /**
373
     * Returns callable for converting node value for modifiers to the constant value
374
     */
375
    private function getModifierConverter(): Closure
376
    {
377 31
        return function (Token $token) {
378 11
            $name = strtoupper($token->getValue());
379
380 11
            return constant("ReflectionMethod::IS_{$name}");
381 31
        };
382
    }
383
}
384