RayAspectKernel   A
last analyzed

Complexity

Total Complexity 24

Size/Duplication

Total Lines 183
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 9

Importance

Changes 0
Metric Value
wmc 24
lcom 1
cbo 9
dl 0
loc 183
rs 10
c 0
b 0
f 0

10 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 1
A register() 0 7 2
A weave() 0 16 4
A containerAdaptor() 0 4 1
A getCompiler() 0 4 1
A makeCompileDir() 0 5 1
A makeCacheableDir() 0 7 2
A makeDirectories() 0 8 2
A registerAspectModule() 0 8 3
B aspectConfiguration() 0 26 7
1
<?php
2
declare(strict_types=1);
3
4
/**
5
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
6
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
7
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
8
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
9
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
10
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
11
 * THE SOFTWARE.
12
 *
13
 * This software consists of voluntary contributions made by many individuals
14
 * and is licensed under the MIT license.
15
 *
16
 * Copyright (c) 2015-2020 Yuuki Takezawa
17
 *
18
 */
19
20
namespace Ytake\LaravelAspect;
21
22
use Illuminate\Contracts\Container\Container;
23
use Illuminate\Filesystem\Filesystem;
24
use Ray\Aop\Compiler;
25
use Ray\Aop\Weaver;
26
use Ytake\LaravelAspect\Exception\ClassNotFoundException;
27
use Ytake\LaravelAspect\Modules\AspectModule;
28
29
use function class_exists;
30
use function count;
31
use function strval;
32
use function serialize;
33
use function unserialize;
34
35
/**
36
 * Class RayAspectKernel
37
 */
38
class RayAspectKernel implements AspectDriverInterface
39
{
40
    /** @var Container|\Illuminate\Container\Container */
41
    protected $app;
42
43
    /** @var array */
44
    protected $configure;
45
46
    /** @var Compiler */
47
    protected $compiler;
48
49
    /** @var Filesystem */
50
    protected $filesystem;
51
52
    /** @var bool */
53
    protected $cacheable = false;
54
55
    /** @var bool */
56
    protected $forceCompile = false;
57
58
    /** @var AspectModule[] */
59
    protected $modules = [];
60
61
    /** @var string */
62
    private $mapFile = 'aspect.map.serialize';
63
64
    /**
65
     * RayAspectKernel constructor.
66
     *
67
     * @param Container  $app
68
     * @param Filesystem $filesystem
69
     * @param array      $configure
70
     *
71
     * @throws ClassNotFoundException
72
     */
73
    public function __construct(Container $app, Filesystem $filesystem, array $configure)
74
    {
75
        $this->app = $app;
76
        $this->filesystem = $filesystem;
77
        $this->configure = $configure;
78
        $this->makeCompileDir();
79
        $this->makeCacheableDir();
80
        $this->registerAspectModule();
81
    }
82
83
    /**
84
     * @param string $module
85
     *
86
     * @throws ClassNotFoundException
87
     */
88
    public function register(string $module = null): void
89
    {
90
        if (!class_exists($module)) {
91
            throw new ClassNotFoundException($module);
92
        }
93
        $this->modules[] = new $module;
94
    }
95
96
    /**
97
     * weaving
98
     *
99
     * @throws \Doctrine\Common\Annotations\AnnotationException
100
     * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
101
     * @throws \ReflectionException
102
     */
103
    public function weave(): void
104
    {
105
        if (!count($this->modules)) {
106
            return;
107
        }
108
        $compiler = $this->getCompiler();
109
        $container = $this->containerAdaptor($this->app);
110
        foreach ($this->aspectConfiguration() as $class => $pointcuts) {
111
            $bind = (new AspectBind($this->filesystem, strval($this->configure['cache_dir']), $this->cacheable))
112
                ->bind($class, $pointcuts);
113
            $weaved = $this->forceCompile
114
                ? $compiler->compile($class, $bind)
115
                : (new Weaver($bind, (string)$this->configure['compile_dir']))->weave($class);
116
            $container->intercept($class, $bind, $weaved);
117
        }
118
    }
119
120
    /**
121
     * @param Container $container
122
     *
123
     * @return ContainerInterceptor
124
     */
125
    protected function containerAdaptor(Container $container): ContainerInterceptor
126
    {
127
        return new ContainerInterceptor($container, new AnnotateClass());
128
    }
129
130
    /**
131
     * @return Compiler
132
     * @throws \Doctrine\Common\Annotations\AnnotationException
133
     */
134
    protected function getCompiler(): Compiler
135
    {
136
        return new Compiler((string)$this->configure['compile_dir']);
137
    }
138
139
    /**
140
     * make source compile file directory
141
     */
142
    protected function makeCompileDir()
143
    {
144
        $this->makeDirectories(strval($this->configure['compile_dir']), 0775);
145
        $this->forceCompile = (bool)($this->configure['force_compile'] ?? false);
146
    }
147
148
    /**
149
     * make aspect cache directory
150
     *
151
     * @codeCoverageIgnore
152
     */
153
    protected function makeCacheableDir()
154
    {
155
        if ($this->configure['cache']) {
156
            $this->makeDirectories(strval($this->configure['cache_dir']), 0775);
157
            $this->cacheable = true;
158
        }
159
    }
160
161
    /**
162
     * @param string $dir
163
     * @param int    $mode
164
     */
165
    private function makeDirectories(string $dir, int $mode = 0777)
166
    {
167
        // @codeCoverageIgnoreStart
168
        if (!$this->filesystem->exists($dir)) {
169
            $this->filesystem->makeDirectory($dir, $mode, true);
170
        }
171
        // @codeCoverageIgnoreEnd
172
    }
173
174
    /**
175
     * register Aspect Module
176
     *
177
     * @throws ClassNotFoundException
178
     */
179
    protected function registerAspectModule()
180
    {
181
        if (isset($this->configure['modules'])) {
182
            foreach ($this->configure['modules'] as $module) {
183
                $this->register($module);
184
            }
185
        }
186
    }
187
188
    /**
189
     * @return array
190
     * @codeCoverageIgnore
191
     *
192
     * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
193
     */
194
    protected function aspectConfiguration(): array
195
    {
196
        $map = [];
197
        $file = $this->configure['cache_dir'] . "/{$this->mapFile}";
198
        if ($this->configure['cache']) {
199
            if ($this->filesystem->exists($file)) {
200
                foreach ($this->modules as $module) {
201
                    $module->registerPointCut()->configure($this->app);
202
                }
203
204
                return unserialize($this->filesystem->get($file));
205
            }
206
        }
207
        foreach ($this->modules as $module) {
208
            $pointcut = $module->registerPointCut()->configure($this->app);
209
            foreach ($module->target() as $class) {
210
                $map[$class][] = $pointcut;
211
            }
212
        }
213
214
        if ($this->configure['cache']) {
215
            $this->filesystem->put($file, serialize($map));
216
        }
217
218
        return $map;
219
    }
220
}
221