Completed
Pull Request — 2.x (#117)
by Akihito
01:50 queued 30s
created

Bind::annotatedMethodMatch()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 4

Importance

Changes 0
Metric Value
dl 0
loc 17
ccs 10
cts 10
cp 1
rs 9.7
c 0
b 0
f 0
cc 4
nc 6
nop 3
crap 4
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Ray\Aop;
6
7
use Doctrine\Common\Annotations\AnnotationReader;
8
9
final class Bind implements BindInterface
10
{
11
    /**
12
     * @var array
13
     */
14
    private $bindings = [];
15
16
    /**
17
     * @var AnnotationReader
18
     */
19
    private $reader;
20
21
    /**
22
     * @throws \Doctrine\Common\Annotations\AnnotationException
23
     */
24
    public function __construct()
25 40
    {
26
        $this->reader = new AnnotationReader();
27 40
    }
28 40
29
    public function __sleep()
30
    {
31
        return ['bindings'];
32
    }
33 31
34
    /**
35 31
     * {@inheritdoc}
36 31
     *
37
     * @throws \ReflectionException
38 31
     */
39
    public function bind(string $class, array $pointcuts) : BindInterface
40
    {
41
        $pointcuts = $this->getAnnotationPointcuts($pointcuts);
42
        $this->annotatedMethodsMatch(new \ReflectionClass($class), $pointcuts);
43
44 37
        return $this;
45
    }
46 37
47 3
    /**
48 3
     * {@inheritdoc}
49
     */
50
    public function bindInterceptors(string $method, array $interceptors) : BindInterface
51 37
    {
52
        $this->bindings[$method] = ! array_key_exists($method, $this->bindings) ? $interceptors : array_merge(
53
            $this->bindings[$method],
54
            $interceptors
55
        );
56
57 34
        return $this;
58
    }
59 34
60
    /**
61
     * {@inheritdoc}
62
     */
63
    public function getBindings() : array
64
    {
65 18
        return $this->bindings;
66
    }
67 18
68
    /**
69 18
     * {@inheritdoc}
70
     */
71
    public function toString($salt) : string
72
    {
73
        unset($salt);
74
75
        return strtr(rtrim(base64_encode(pack('H*', sprintf('%u', crc32(serialize($this->bindings))))), '='), '+/', '-_');
76
    }
77 31
78
    /**
79 31
     * @param Pointcut[] $pointcuts
80 31
     *
81 31
     * @return Pointcut[]
82 2
     */
83
    public function getAnnotationPointcuts(array &$pointcuts) : array
84 31
    {
85
        $keyPointcuts = [];
86
        foreach ($pointcuts as $key => $pointcut) {
87 31
            if ($pointcut->methodMatcher instanceof AnnotatedMatcher) {
88
                $key = $pointcut->methodMatcher->annotation;
89
            }
90 31
            $keyPointcuts[$key] = $pointcut;
91
        }
92 31
93 31
        return $keyPointcuts;
94 31
    }
95
96 31
    private function annotatedMethodsMatch(\ReflectionClass $class, array &$pointcuts)
97
    {
98 31
        $methods = $class->getMethods(ReflectionMethod::IS_PUBLIC);
99
        foreach ($methods as $method) {
100 31
            $this->annotatedMethodMatch($class, $method, $pointcuts);
101
        }
102 31
    }
103 31
104 1
    private function annotatedMethodMatch(\ReflectionClass $class, \ReflectionMethod $method, array $pointcuts)
105 31
    {
106
        $annotations = $this->reader->getMethodAnnotations($method);
107
        // priority bind
108 31
        foreach ($pointcuts as $key => $pointcut) {
109
            if ($pointcut instanceof PriorityPointcut) {
110
                $this->annotatedMethodMatchBind($class, $method, $pointcut);
111 31
                unset($pointcuts[$key]);
112 31
            }
113
        }
114 31
        $onion = $this->onionOrderMatch($class, $method, $pointcuts, $annotations);
115
116 31
        // default binding
117
        foreach ($onion as $pointcut) {
118 31
            $this->annotatedMethodMatchBind($class, $method, $pointcut);
119 31
        }
120 26
    }
121
122 31
    private function annotatedMethodMatchBind(\ReflectionClass $class, \ReflectionMethod $method, Pointcut $pointCut)
123 31
    {
124 2
        $isMethodMatch = $pointCut->methodMatcher->matchesMethod($method, $pointCut->methodMatcher->getArguments());
125
        if (! $isMethodMatch) {
126 29
            return;
127 29
        }
128
        $isClassMatch = $pointCut->classMatcher->matchesClass($class, $pointCut->classMatcher->getArguments());
129 31
        if (! $isClassMatch) {
130
            return;
131
        }
132
        $this->bindInterceptors($method->name, $pointCut->interceptors);
133
    }
134
135
    private function onionOrderMatch(
136 31
        \ReflectionClass $class,
137 7
        \ReflectionMethod $method,
138 7
        array $pointcuts,
139 2
        array $annotations
140 7
    ) : array {
141
        // method bind in annotation order
142
        foreach ($annotations as $annotation) {
143
            $annotationIndex = get_class($annotation);
144 31
            if (array_key_exists($annotationIndex, $pointcuts)) {
145
                $this->annotatedMethodMatchBind($class, $method, $pointcuts[$annotationIndex]);
146
                unset($pointcuts[$annotationIndex]);
147
            }
148
        }
149
150
        return $pointcuts;
151
    }
152
}
153