Completed
Push — master ( 91676c...354384 )
by Alexander
02:23
created

PointcutGrammar::getModifierConverter()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

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