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

AspectLoader   A

Complexity

Total Complexity 27

Size/Duplication

Total Lines 182
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 87.3%

Importance

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