Completed
Pull Request — 1.x (#260)
by Alexander
03:43
created

AspectKernel   A

Complexity

Total Complexity 19

Size/Duplication

Total Lines 269
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 10

Test Coverage

Coverage 3.17%

Importance

Changes 18
Bugs 2 Features 2
Metric Value
wmc 19
c 18
b 2
f 2
lcom 2
cbo 10
dl 0
loc 269
ccs 2
cts 63
cp 0.0317
rs 10

12 Methods

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