Completed
Push — 2.x ( 98b57b...8b2aa1 )
by Alexander
05:24
created

PointcutGrammar::getModifierConverter()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

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