Completed
Pull Request — 2.x (#381)
by Alexander
02:35
created

AspectKernel::addKernelResourcesToContainer()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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