Completed
Pull Request — 2.x (#349)
by Alexander
02:20
created

GoAspectContainer   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 218
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 15

Test Coverage

Coverage 76.14%

Importance

Changes 0
Metric Value
wmc 15
lcom 2
cbo 15
dl 0
loc 218
ccs 67
cts 88
cp 0.7614
rs 9.1666
c 0
b 0
f 0

10 Methods

Rating   Name   Duplication   Size   Complexity  
A getPointcut() 0 4 1
A registerPointcut() 0 4 1
A getAdvisor() 0 4 1
A registerAdvisor() 0 4 1
A getAspect() 0 4 1
A registerAspect() 0 6 1
A addResource() 0 5 1
A getResources() 0 4 1
A isFresh() 0 8 3
B __construct() 0 92 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\AnnotationReader;
15
use Doctrine\Common\Annotations\CachedReader;
16
use Doctrine\Common\Cache as DoctrineCache;
17
use Go\Aop\Advisor;
18
use Go\Aop\Aspect;
19
use Go\Aop\Pointcut;
20
use Go\Aop\Pointcut\PointcutGrammar;
21
use Go\Aop\Pointcut\PointcutLexer;
22
use Go\Aop\Pointcut\PointcutParser;
23
use Go\Instrument\ClassLoading\CachePathManager;
24
use ReflectionClass;
25
26
/**
27
 * Aspect container contains list of all pointcuts and advisors
28
 */
29
class GoAspectContainer extends Container
30
{
31
    /**
32
     * List of resources for application
33
     *
34
     * @var array
35
     */
36
    protected $resources = [];
37
38
    /**
39
     * Cached timestamp for resources
40
     *
41
     * @var integer
42
     */
43
    protected $maxTimestamp = 0;
44
45
    /**
46
     * Constructor for container
47
     */
48
    public function __construct()
49
    {
50
        // Register all services in the container
51 10
        $this->share('aspect.loader', function (Container $container) {
52 2
            $aspectLoader = new AspectLoader(
53 2
                $container,
54 2
                $container->get('aspect.annotation.reader')
55
            );
56 2
            $lexer  = $container->get('aspect.pointcut.lexer');
57 2
            $parser = $container->get('aspect.pointcut.parser');
58
59
            // Register general aspect loader extension
60 2
            $aspectLoader->registerLoaderExtension(new GeneralAspectLoaderExtension($lexer, $parser));
61 2
            $aspectLoader->registerLoaderExtension(new IntroductionAspectExtension($lexer, $parser));
62
63 2
            return $aspectLoader;
64 10
        });
65
66 10
        $this->share('aspect.cached.loader', function (Container $container) {
67
            $options = $container->get('kernel.options');
68
            if (!empty($options['cacheDir'])) {
69
                $loader = new CachedAspectLoader(
70
                    $container,
71
                    'aspect.loader',
72
                    $container->get('kernel.options')
73
                );
74
            } else {
75
                $loader = $container->get('aspect.loader');
76
            }
77
78
            return $loader;
79 10
        });
80
81 10
        $this->share('aspect.advisor.accessor', function (Container $container) {
82
            return new LazyAdvisorAccessor(
83
                $container,
84
                $container->get('aspect.cached.loader')
85
            );
86 10
        });
87
88 10
        $this->share('aspect.advice_matcher', function (Container $container) {
89 1
            return new AdviceMatcher(
90 1
                $container->get('aspect.loader'),
91 1
                $container->get('kernel.interceptFunctions')
92
            );
93 10
        });
94
95 10
        $this->share('aspect.annotation.cache', function (Container $container) {
96 5
            $options = $container->get('kernel.options');
97
98 5
            if (!empty($options['annotationCache'])) {
99
                return $options['annotationCache'];
100
            }
101
102 5
            if (!empty($options['cacheDir'])) {
103
                return new DoctrineCache\FilesystemCache(
104
                    $options['cacheDir'] . DIRECTORY_SEPARATOR . '_annotations' . DIRECTORY_SEPARATOR,
105
                    '.annotations.cache',
106
                    0777 & (~$options['cacheFileMode'])
107
                );
108
            }
109
110 5
            return new DoctrineCache\ArrayCache();
111 10
        });
112
113 10
        $this->share('aspect.annotation.reader', function (Container $container) {
114 4
            $options = $container->get('kernel.options');
115
116 4
            return new CachedReader(
117 4
                new AnnotationReader(),
118 4
                $container->get('aspect.annotation.cache'),
119 4
                $options['debug'] ?? false
120
            );
121 10
        });
122
123 10
        $this->share('aspect.cache.path.manager', function (Container $container) {
124
            return new CachePathManager($container->get('kernel'));
125 10
        });
126
127
        // Pointcut services
128 10
        $this->share('aspect.pointcut.lexer', function () {
129 3
            return new PointcutLexer();
130 10
        });
131 10
        $this->share('aspect.pointcut.parser', function (Container $container) {
132 3
            return new PointcutParser(
133 3
                new PointcutGrammar(
134 3
                    $container,
135 3
                    $container->get('aspect.annotation.reader')
136
                )
137
            );
138 10
        });
139 10
    }
140
141
    /**
142
     * Returns a pointcut by identifier
143
     *
144
     * @param string $id Pointcut identifier
145
     *
146
     * @return Pointcut
147
     */
148 1
    public function getPointcut(string $id): Pointcut
149
    {
150 1
        return $this->get("pointcut.{$id}");
151
    }
152
153
    /**
154
     * Store the pointcut in the container
155
     *
156
     * @param Pointcut $pointcut Instance
157
     * @param string $id Key for pointcut
158
     */
159 1
    public function registerPointcut(Pointcut $pointcut, string $id)
160
    {
161 1
        $this->set("pointcut.{$id}", $pointcut, ['pointcut']);
162 1
    }
163
164
    /**
165
     * Returns an advisor by identifier
166
     *
167
     * @param string $id Advisor identifier
168
     *
169
     * @return Advisor
170
     */
171
    public function getAdvisor(string $id): Advisor
172
    {
173
        return $this->get("advisor.{$id}");
174
    }
175
176
    /**
177
     * Store the advisor in the container
178
     *
179
     * @param Advisor $advisor Instance
180
     * @param string $id Key for advisor
181
     */
182 1
    public function registerAdvisor(Advisor $advisor, string $id)
183
    {
184 1
        $this->set("advisor.{$id}", $advisor, ['advisor']);
185 1
    }
186
187
    /**
188
     * Returns an aspect by id or class name
189
     *
190
     * @param string $aspectName Aspect name
191
     *
192
     * @return Aspect
193
     */
194 1
    public function getAspect(string $aspectName): Aspect
195
    {
196 1
        return $this->get("aspect.{$aspectName}");
197
    }
198
199
    /**
200
     * Register an aspect in the container
201
     *
202
     * @param Aspect $aspect Instance of concrete aspect
203
     */
204 1
    public function registerAspect(Aspect $aspect)
205
    {
206 1
        $refAspect = new ReflectionClass($aspect);
207 1
        $this->set("aspect.{$refAspect->name}", $aspect, ['aspect']);
208 1
        $this->addResource($refAspect->getFileName());
209 1
    }
210
211
    /**
212
     * Add an AOP resource to the container
213
     * Resources is used to check the freshness of AOP cache
214
     *
215
     * @param string $resource Path to the resource
216
     */
217 2
    public function addResource(string $resource)
218
    {
219 2
        $this->resources[]  = $resource;
220 2
        $this->maxTimestamp = 0;
221 2
    }
222
223
    /**
224
     * Returns list of AOP resources
225
     */
226
    public function getResources(): array
227
    {
228
        return $this->resources;
229
    }
230
231
    /**
232
     * Checks the freshness of AOP cache
233
     *
234
     * @param integer $timestamp
235
     *
236
     * @return bool Whether or not concrete file is fresh
237
     */
238 1
    public function isFresh(int $timestamp): bool
239
    {
240 1
        if (!$this->maxTimestamp && !empty($this->resources)) {
241 1
            $this->maxTimestamp = max(array_map('filemtime', $this->resources));
242
        }
243
244 1
        return $this->maxTimestamp <= $timestamp;
245
    }
246
}
247