Completed
Pull Request — master (#240)
by Alexander
05:40
created

AspectKernel::getDefaultOptions()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 14
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 4
Bugs 1 Features 1
Metric Value
c 4
b 1
f 1
dl 0
loc 14
ccs 0
cts 4
cp 0
rs 9.4285
cc 1
eloc 10
nc 1
nop 0
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\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 = '1.0.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
     * Aspect container instance
60
     *
61
     * @var null|AspectContainer
62
     */
63
    protected $container = null;
64
65
    /**
66
     * Protected constructor is used to prevent direct creation, but allows customization if needed
67
     */
68
    protected function __construct() {}
69
70
    /**
71
     * Returns the single instance of kernel
72
     *
73
     * @return AspectKernel
74
     */
75
    public static function getInstance()
76
    {
77
        if (!self::$instance) {
78
            self::$instance = new static();
79
        }
80
81
        return self::$instance;
82
    }
83
84
    /**
85
     * Init the kernel and make adjustments
86
     *
87
     * @param array $options Associative array of options for kernel
88
     */
89
    public function init(array $options = [])
90
    {
91
        $this->options = $this->normalizeOptions($options);
92
        define('AOP_ROOT_DIR', $this->options['appDir']);
93
        define('AOP_CACHE_DIR', $this->options['cacheDir']);
94
95
        /** @var $container AspectContainer */
96
        $container = $this->container = new $this->options['containerClass'];
97
        $container->set('kernel', $this);
98
        $container->set('kernel.interceptFunctions', $this->hasFeature(Features::INTERCEPT_FUNCTIONS));
99
        $container->set('kernel.options', $this->options);
100
101
        SourceTransformingLoader::register();
102
103
        foreach ($this->registerTransformers() as $sourceTransformer) {
104
            SourceTransformingLoader::addTransformer($sourceTransformer);
105
        }
106
107
        // Register kernel resources in the container for debug mode
108
        if ($this->options['debug']) {
109
            $this->addKernelResourcesToContainer($container);
110
        }
111
112
        AopComposerLoader::init($this->options, $container);
113
114
        // Register all AOP configuration in the container
115
        $this->configureAop($container);
116
    }
117
118
    /**
119
     * Returns an aspect container
120
     *
121
     * @return null|AspectContainer
122
     */
123 6
    public function getContainer()
124
    {
125 6
        return $this->container;
126
    }
127
128
    /**
129
     * Checks if kernel configuration has enabled specific feature
130
     *
131
     * @param integer $featureToCheck See Go\Aop\Features enumeration class for features
132
     *
133
     * @return bool Whether specific feature enabled or not
134
     */
135
    public function hasFeature($featureToCheck)
136
    {
137
        return ($this->options['features'] & $featureToCheck) !== 0;
138
    }
139
140
    /**
141
     * Returns list of kernel options
142
     *
143
     * @return array
144
     */
145
    public function getOptions()
146
    {
147
        return $this->options;
148
    }
149
150
    /**
151
     * Returns default options for kernel. Available options:
152
     *
153
     *   debug    - boolean Determines whether or not kernel is in debug mode
154
     *   appDir   - string Path to the application root directory.
155
     *   cacheDir - string Path to the cache directory where compiled classes will be stored
156
     *   cacheFileMode - integer Binary mask of permission bits that is set to cache files
157
     *   features - integer Binary mask of features
158
     *   includePaths - array Whitelist of directories where aspects should be applied. Empty for everywhere.
159
     *   excludePaths - array Blacklist of directories or files where aspects shouldn't be applied.
160
     *
161
     * @return array
162
     */
163
    protected function getDefaultOptions()
164
    {
165
        return array(
166
            'debug'                  => false,
167
            'appDir'                 => __DIR__ . '/../../../../../',
168
            'cacheDir'               => null,
169
            'cacheFileMode'          => null,
170
            'features'               => 0,
171
172
            'includePaths'           => [],
173
            'excludePaths'           => [],
174
            'containerClass'         => static::$containerClass,
175
        );
176
    }
177
178
179
    /**
180
     * Normalizes options for the kernel
181
     *
182
     * @param array $options List of options
183
     *
184
     * @return array
185
     */
186
    protected function normalizeOptions(array $options)
187
    {
188
        $options = array_replace($this->getDefaultOptions(), $options);
189
        if ($options['cacheDir']) {
190
            $options['excludePaths'][] = $options['cacheDir'];
191
        }
192
        $options['excludePaths'][] = __DIR__ . '/../';
193
194
        $options['appDir']   = PathResolver::realpath($options['appDir']);
195
        $options['cacheDir'] = PathResolver::realpath($options['cacheDir']);
196
        $options['includePaths'] = PathResolver::realpath($options['includePaths']);
197
        $options['excludePaths'] = PathResolver::realpath($options['excludePaths']);
198
199
        return $options;
200
    }
201
202
    /**
203
     * Configure an AspectContainer with advisors, aspects and pointcuts
204
     *
205
     * @param AspectContainer $container
206
     *
207
     * @return void
208
     */
209
    abstract protected function configureAop(AspectContainer $container);
210
211
    /**
212
     * Returns list of source transformers, that will be applied to the PHP source
213
     *
214
     * @return array|SourceTransformer[]
215
     */
216
    protected function registerTransformers()
217
    {
218
        $cacheManager     = $this->getContainer()->get('aspect.cache.path.manager');
219
        $filterInjector   = new FilterInjectorTransformer($this, SourceTransformingLoader::getId(), $cacheManager);
220
        $magicTransformer = new MagicConstantTransformer($this);
221
        $aspectKernel     = $this;
222
223
        $sourceTransformers = function () use ($filterInjector, $magicTransformer, $aspectKernel, $cacheManager) {
224
            $transformers    = [];
225
            $aspectContainer = $aspectKernel->getContainer();
226
            $transformers[]  = new WeavingTransformer(
227
                $aspectKernel,
228
                $aspectContainer->get('aspect.advice_matcher'),
229
                $cacheManager,
230
                $aspectContainer->get('aspect.cached.loader')
231
            );
232
            if ($aspectKernel->hasFeature(Features::INTERCEPT_INCLUDES)) {
233
                $transformers[] = $filterInjector;
234
            }
235
            $transformers[] = $magicTransformer;
236
237
            if ($aspectKernel->hasFeature(Features::INTERCEPT_INITIALIZATIONS)) {
238
                $transformers[] = new ConstructorExecutionTransformer();
239
            }
240
241
            return $transformers;
242
        };
243
244
        return array(
245
            new CachingTransformer($this, $sourceTransformers, $cacheManager)
246
        );
247
    }
248
249
    /**
250
     * Add resources for kernel
251
     *
252
     * @param AspectContainer $container
253
     */
254
    protected function addKernelResourcesToContainer(AspectContainer $container)
255
    {
256
        $trace    = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
257
        $refClass = new \ReflectionObject($this);
258
259
        $container->addResource($trace[1]['file']);
260
        $container->addResource($refClass->getFileName());
261
    }
262
}
263