Completed
Push — master ( efe256...811ea7 )
by Alexander
11s
created

AspectLoader   A

Complexity

Total Complexity 27

Size/Duplication

Total Lines 183
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 88.89%

Importance

Changes 0
Metric Value
wmc 27
lcom 1
cbo 3
dl 0
loc 183
ccs 56
cts 63
cp 0.8889
rs 10
c 0
b 0
f 0

7 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A registerLoaderExtension() 0 7 2
B load() 0 25 6
A loadAndRegister() 0 15 4
A getUnloadedAspects() 0 12 3
B loadFrom() 0 29 7
A getAnnotations() 0 16 4
1
<?php
2
declare(strict_types = 1);
3
/*
4
 * Go! AOP framework
5
 *
6
 * @copyright Copyright 2012, 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\Core;
13
14
use Doctrine\Common\Annotations\Reader;
15
use Go\Aop\Advisor;
16
use Go\Aop\Aspect;
17
use Go\Aop\Pointcut;
18
use Reflector;
19
20
/**
21
 * Loader of aspects into the container
22
 */
23
class AspectLoader
24
{
25
26
    /**
27
     * Aspect container instance
28
     */
29
    protected $container;
30
31
    /**
32
     * List of aspect loaders
33
     *
34
     * @var array
35
     */
36
    protected $loaders = [];
37
38
    /**
39
     * Annotation reader for aspects
40
     */
41
    protected $annotationReader;
42
43
    /**
44
     * List of aspects that was loaded
45
     *
46
     * @var array
47
     */
48
    protected $loadedAspects = [];
49
50
    /**
51
     * Loader constructor
52
     */
53 9
    public function __construct(AspectContainer $container, Reader $reader)
54
    {
55 9
        $this->container        = $container;
56 9
        $this->annotationReader = $reader;
57 9
    }
58
59
    /**
60
     * Register an aspect loader extension
61
     *
62
     * This method allows to extend the logic of aspect loading by registering an extension for loader.
63
     */
64 2
    public function registerLoaderExtension(AspectLoaderExtension $loader): void
65
    {
66 2
        $targets = $loader->getTargets();
67 2
        foreach ($targets as $target) {
68 2
            $this->loaders[$target][] = $loader;
69
        }
70 2
    }
71
72
    /**
73
     * Loads an aspect with the help of aspect loaders, but don't register it in the container
74
     *
75
     * @see loadAndRegister() method for registration
76
     *
77
     * @return Pointcut[]|Advisor[]
78
     */
79 1
    public function load(Aspect $aspect): array
80
    {
81 1
        $loadedItems = [];
82 1
        $refAspect   = new \ReflectionClass($aspect);
83
84 1
        if (!empty($this->loaders[AspectLoaderExtension::TARGET_CLASS])) {
85
            $loadedItems += $this->loadFrom($aspect, $refAspect, $this->loaders[AspectLoaderExtension::TARGET_CLASS]);
86
        }
87
88 1
        if (!empty($this->loaders[AspectLoaderExtension::TARGET_METHOD])) {
89 1
            $refMethods = $refAspect->getMethods();
90 1
            foreach ($refMethods as $refMethod) {
91 1
                $loadedItems += $this->loadFrom($aspect, $refMethod, $this->loaders[AspectLoaderExtension::TARGET_METHOD]);
92
            }
93
        }
94
95 1
        if (!empty($this->loaders[AspectLoaderExtension::TARGET_PROPERTY])) {
96 1
            $refProperties = $refAspect->getProperties();
97 1
            foreach ($refProperties as $refProperty) {
98 1
                $loadedItems += $this->loadFrom($aspect, $refProperty, $this->loaders[AspectLoaderExtension::TARGET_PROPERTY]);
99
            }
100
        }
101
102 1
        return $loadedItems;
103
    }
104
105
    /**
106
     * Loads and register all items of aspect in the container
107
     */
108 1
    public function loadAndRegister(Aspect $aspect): void
109
    {
110 1
        $loadedItems = $this->load($aspect);
111 1
        foreach ($loadedItems as $itemId => $item) {
112 1
            if ($item instanceof Pointcut) {
113
                $this->container->registerPointcut($item, $itemId);
114
            }
115 1
            if ($item instanceof Advisor) {
116 1
                $this->container->registerAdvisor($item, $itemId);
117
            }
118
        }
119
120 1
        $aspectClass = get_class($aspect);
121 1
        $this->loadedAspects[$aspectClass] = $aspectClass;
122 1
    }
123
124
    /**
125
     * Returns list of unloaded aspects in the container
126
     *
127
     * @return Aspect[]
128
     */
129 1
    public function getUnloadedAspects(): array
130
    {
131 1
        $unloadedAspects = [];
132
133 1
        foreach ($this->container->getByTag('aspect') as $aspect) {
134 1
            if (!isset($this->loadedAspects[get_class($aspect)])) {
135 1
                $unloadedAspects[] = $aspect;
136
            }
137
        }
138
139 1
        return $unloadedAspects;
140
    }
141
142
    /**
143
     * Load definitions from specific aspect part into the aspect container
144
     *
145
     * @param Aspect $aspect Aspect instance
146
     * @param Reflector $reflector Reflection instance
147
     * @param array|AspectLoaderExtension[] $loaders List of loaders that can produce advisors from aspect class
148
     *
149
     * @throws \InvalidArgumentException If kind of loader isn't supported
150
     *
151
     * @return array|Pointcut[]|Advisor[]
152
     */
153 1
    protected function loadFrom(Aspect $aspect, Reflector $reflector, array $loaders): array
154
    {
155 1
        $loadedItems = [];
156
157 1
        foreach ($loaders as $loader) {
158 1
            $loaderKind = $loader->getKind();
159
            switch ($loaderKind) {
160 1
                case AspectLoaderExtension::KIND_REFLECTION:
161
                    if ($loader->supports($aspect, $reflector)) {
162
                        $loadedItems += $loader->load($aspect, $reflector);
163
                    }
164
                    break;
165
166 1
                case AspectLoaderExtension::KIND_ANNOTATION:
167 1
                    $annotations = $this->getAnnotations($reflector);
168 1
                    foreach ($annotations as $annotation) {
169 1
                        if ($loader->supports($aspect, $reflector, $annotation)) {
170 1
                            $loadedItems += $loader->load($aspect, $reflector, $annotation);
171
                        }
172
                    }
173 1
                    break;
174
175
                default:
176 1
                    throw new \InvalidArgumentException("Unsupported loader kind {$loaderKind}");
177
            }
178
        }
179
180 1
        return $loadedItems;
181
    }
182
183
    /**
184
     * Return list of annotations for reflection point
185
     *
186
     * @return array list of annotations
187
     * @throws \InvalidArgumentException if $reflector is unsupported
188
     */
189 1
    protected function getAnnotations(Reflector $reflector): array
190
    {
191
        switch (true) {
192 1
            case ($reflector instanceof \ReflectionClass):
193
                return $this->annotationReader->getClassAnnotations($reflector);
194
195 1
            case ($reflector instanceof \ReflectionMethod):
196 1
                return $this->annotationReader->getMethodAnnotations($reflector);
197
198 1
            case ($reflector instanceof \ReflectionProperty):
199 1
                return $this->annotationReader->getPropertyAnnotations($reflector);
200
201
            default:
202
                throw new \InvalidArgumentException('Unsupported reflection point ' . get_class($reflector));
203
        }
204
    }
205
}
206