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

AspectKernel   A

Complexity

Total Complexity 18

Size/Duplication

Total Lines 248
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 10

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
wmc 18
lcom 2
cbo 10
dl 0
loc 248
ccs 0
cts 75
cp 0
rs 10
c 0
b 0
f 0

11 Methods

Rating   Name   Duplication   Size   Complexity  
A hasFeature() 0 4 1
A getInstance() 0 8 2
A addKernelResourcesToContainer() 0 8 1
A __construct() 0 1 1
B init() 0 35 4
A getContainer() 0 4 1
A getOptions() 0 4 1
A getDefaultOptions() 0 14 1
A normalizeOptions() 0 22 3
configureAop() 0 1 ?
B registerTransformers() 0 30 3
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 Go\Aop\Features;
15
use Go\Instrument\ClassLoading\AopComposerLoader;
16
use Go\Instrument\ClassLoading\AopComposerManipulator;
17
use Go\Instrument\ClassLoading\SourceTransformingLoader;
18
use Go\Instrument\PathResolver;
19
use Go\Instrument\Transformer\ConstructorExecutionTransformer;
20
use Go\Instrument\Transformer\SelfValueTransformer;
21
use Go\Instrument\Transformer\SourceTransformer;
22
use Go\Instrument\Transformer\WeavingTransformer;
23
use Go\Instrument\Transformer\CachingTransformer;
24
use Go\Instrument\Transformer\FilterInjectorTransformer;
25
use Go\Instrument\Transformer\MagicConstantTransformer;
26
27
/**
28
 * Abstract aspect kernel is used to prepare an application to work with aspects.
29
 */
30
abstract class AspectKernel
31
{
32
33
    /**
34
     * Version of kernel
35
     */
36
    const VERSION = '2.1.0';
37
38
    /**
39
     * Kernel options
40
     *
41
     * @var array
42
     */
43
    protected $options = [
44
        'features' => 0
45
    ];
46
47
    /**
48
     * Single instance of kernel
49
     *
50
     * @var static
51
     */
52
    protected static $instance;
53
54
    /**
55
     * Default class name for container, can be redefined in children
56
     *
57
     * @var string
58
     */
59
    protected static $containerClass = GoAspectContainer::class;
60
61
    /**
62
     * Flag to determine if kernel was already initialized or not
63
     *
64
     * @var bool
65
     */
66
    protected $wasInitialized = false;
67
68
    /**
69
     * Aspect container instance
70
     *
71
     * @var AspectContainer
72
     */
73
    protected $container;
74
75
    /**
76
     * Protected constructor is used to prevent direct creation, but allows customization if needed
77
     */
78
    protected function __construct() {}
79
80
    /**
81
     * Returns the single instance of kernel
82
     */
83
    public static function getInstance(): self
84
    {
85
        if (!self::$instance) {
86
            self::$instance = new static();
87
        }
88
89
        return self::$instance;
90
    }
91
92
    /**
93
     * Init the kernel and make adjustments
94
     *
95
     * @param array $options Associative array of options for kernel
96
     */
97
    public function init(array $options = [])
98
    {
99
        if ($this->wasInitialized) {
100
            return;
101
        }
102
103
        $this->options = $this->normalizeOptions($options);
104
        define('AOP_ROOT_DIR', $this->options['appDir']);
105
        define('AOP_CACHE_DIR', $this->options['cacheDir']);
106
107
        /** @var $container AspectContainer */
108
        $container = $this->container = new $this->options['containerClass'];
109
        $container->set('kernel', $this);
110
        $container->set('kernel.interceptFunctions', $this->hasFeature(Features::INTERCEPT_FUNCTIONS));
111
        $container->set('kernel.options', $this->options);
112
113
        SourceTransformingLoader::register();
114
115
        foreach ($this->registerTransformers() as $sourceTransformer) {
116
            SourceTransformingLoader::addTransformer($sourceTransformer);
117
        }
118
119
        // Register kernel resources in the container for debug mode
120
        if ($this->options['debug']) {
121
            $this->addKernelResourcesToContainer($container);
122
        }
123
124
        //AopComposerLoader::init($this->options, $container);
125
        AopComposerManipulator::init($this->options, $container);
126
127
        // Register all AOP configuration in the container
128
        $this->configureAop($container);
129
130
        $this->wasInitialized = true;
131
    }
132
133
    /**
134
     * Returns an aspect container
135
     */
136
    public function getContainer(): AspectContainer
137
    {
138
        return $this->container;
139
    }
140
141
    /**
142
     * Checks if kernel configuration has enabled specific feature
143
     *
144
     * @param integer $featureToCheck See Go\Aop\Features enumeration class for features
145
     *
146
     * @return bool Whether specific feature enabled or not
147
     */
148
    public function hasFeature(int $featureToCheck): bool
149
    {
150
        return ($this->options['features'] & $featureToCheck) !== 0;
151
    }
152
153
    /**
154
     * Returns list of kernel options
155
     */
156
    public function getOptions(): array
157
    {
158
        return $this->options;
159
    }
160
161
    /**
162
     * Returns default options for kernel. Available options:
163
     *
164
     *   debug    - boolean Determines whether or not kernel is in debug mode
165
     *   appDir   - string Path to the application root directory.
166
     *   cacheDir - string Path to the cache directory where compiled classes will be stored
167
     *   cacheFileMode - integer Binary mask of permission bits that is set to cache files
168
     *   annotationCache - Doctrine\Common\Cache\Cache. If not provided, Doctrine\Common\Cache\PhpFileCache is used.
169
     *   features - integer Binary mask of features
170
     *   includePaths - array Whitelist of directories where aspects should be applied. Empty for everywhere.
171
     *   excludePaths - array Blacklist of directories or files where aspects shouldn't be applied.
172
     */
173
    protected function getDefaultOptions(): array
174
    {
175
        return [
176
            'debug'           => false,
177
            'appDir'          => __DIR__ . '/../../../../../',
178
            'cacheDir'        => null,
179
            'cacheFileMode'   => 0770 & ~umask(), // Respect user umask() policy
180
            'features'        => 0,
181
            'annotationCache' => null,
182
            'includePaths'    => [],
183
            'excludePaths'    => [],
184
            'containerClass'  => static::$containerClass,
185
        ];
186
    }
187
188
189
    /**
190
     * Normalizes options for the kernel
191
     *
192
     * @param array $options List of options
193
     *
194
     * @return array
195
     */
196
    protected function normalizeOptions(array $options): array
197
    {
198
        $options = array_replace($this->getDefaultOptions(), $options);
199
        if ($options['cacheDir']) {
200
            $options['excludePaths'][] = $options['cacheDir'];
201
        }
202
        $options['appDir']   = PathResolver::realpath($options['appDir']);
203
        $options['cacheDir'] = PathResolver::realpath($options['cacheDir']);
204
205
        if (!$options['cacheDir']) {
206
            throw new \RuntimeException('You need to provide valid cache directory for Go! AOP framework.');
207
        }
208
209
        $options['excludePaths'][] = $options['cacheDir'];
210
        $options['excludePaths'][] = __DIR__ . '/../';
211
        $options['appDir']         = PathResolver::realpath($options['appDir']);
212
        $options['cacheFileMode']  = (int) $options['cacheFileMode'];
213
        $options['includePaths']   = PathResolver::realpath($options['includePaths']);
214
        $options['excludePaths']   = PathResolver::realpath($options['excludePaths']);
215
216
        return $options;
217
    }
218
219
    /**
220
     * Configure an AspectContainer with advisors, aspects and pointcuts
221
     *
222
     * @param AspectContainer $container
223
     *
224
     * @return void
225
     */
226
    abstract protected function configureAop(AspectContainer $container);
227
228
    /**
229
     * Returns list of source transformers, that will be applied to the PHP source
230
     *
231
     * @return array|SourceTransformer[]
232
     */
233
    protected function registerTransformers()
234
    {
235
        $cacheManager     = $this->getContainer()->get('aspect.cache.path.manager');
236
        $filterInjector   = new FilterInjectorTransformer($this, SourceTransformingLoader::getId(), $cacheManager);
237
        $magicTransformer = new MagicConstantTransformer($this);
238
239
        $sourceTransformers = function () use ($filterInjector, $magicTransformer, $cacheManager) {
240
            $transformers = [];
241
            if ($this->hasFeature(Features::INTERCEPT_INITIALIZATIONS)) {
242
                $transformers[] = new ConstructorExecutionTransformer();
243
            }
244
            if ($this->hasFeature(Features::INTERCEPT_INCLUDES)) {
245
                $transformers[] = $filterInjector;
246
            }
247
            $transformers[]  = new SelfValueTransformer($this);
248
            $transformers[]  = new WeavingTransformer(
249
                $this,
250
                $this->container->get('aspect.advice_matcher'),
251
                $cacheManager,
252
                $this->container->get('aspect.cached.loader')
253
            );
254
            $transformers[] = $magicTransformer;
255
256
            return $transformers;
257
        };
258
259
        return [
260
            new CachingTransformer($this, $sourceTransformers, $cacheManager)
261
        ];
262
    }
263
264
    /**
265
     * Add resources for kernel
266
     *
267
     * @param AspectContainer $container
268
     */
269
    protected function addKernelResourcesToContainer(AspectContainer $container)
270
    {
271
        $trace    = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
272
        $refClass = new \ReflectionObject($this);
273
274
        $container->addResource($trace[1]['file']);
275
        $container->addResource($refClass->getFileName());
276
    }
277
}
278