Completed
Pull Request — master (#463)
by Alexander
30:17 queued 05:15
created

PointcutGrammar::__construct()   B

Complexity

Conditions 2
Paths 1

Size

Total Lines 314

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 216
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 314
ccs 216
cts 217
cp 0.9954
rs 8
c 0
b 0
f 0
cc 2
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
3
declare(strict_types=1);
4
/*
5
 * Go! AOP framework
6
 *
7
 * @copyright Copyright 2013, Lisachenko Alexander <[email protected]>
8
 *
9
 * This source file is subject to the license that is bundled
10
 * with this source code in the file LICENSE.
11
 */
12
13
namespace Go\Aop\Pointcut;
14
15
use Closure;
16
use Dissect\Lexer\Token;
17
use Dissect\Parser\Grammar;
18
use Doctrine\Common\Annotations\Reader;
19
use Go\Aop\PointFilter;
20
use Go\Aop\Support\AndPointFilter;
21
use Go\Aop\Support\InheritanceClassFilter;
22
use Go\Aop\Support\ModifierMatcherFilter;
23
use Go\Aop\Support\ReturnTypeFilter;
24
use Go\Aop\Support\SimpleNamespaceFilter;
25
use Go\Aop\Support\TruePointFilter;
26
use Go\Core\AspectContainer;
27
use ReflectionMethod;
28
29
use function constant;
30
31
/**
32
 * Pointcut grammar defines general structure of pointcuts and rules of parsing
33
 */
34
class PointcutGrammar extends Grammar
35
{
36 31
    /**
37
     * Constructs a pointcut grammar with AST
38 31
     */
39 31
    public function __construct(AspectContainer $container, Reader $annotationReader)
40
    {
41 31
        $this('empty')
42
            ->is(/* empty */);
43 31
44 31
        $stringConverter = $this->getNodeToStringConverter();
45
46 3
        $this('pointcutExpression')
47 31
            ->is('pointcutExpression', '||', 'conjugatedExpression')
48 31
            ->call(fn($first, $_0, $second) => new OrPointcut($first, $second))
0 ignored issues
show
Bug introduced by
This code did not parse for me. Apparently, there is an error somewhere around this line:

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