Completed
Pull Request — master (#328)
by Nikola
03:49
created

AspectKernel   A

Complexity

Total Complexity 17

Size/Duplication

Total Lines 250
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 9

Test Coverage

Coverage 2.78%

Importance

Changes 0
Metric Value
wmc 17
lcom 2
cbo 9
dl 0
loc 250
ccs 2
cts 72
cp 0.0278
rs 10
c 0
b 0
f 0

11 Methods

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