Completed
Push — master ( 509a2e...170574 )
by Alexander
07:07
created

AspectKernel::getDefaultFeatures()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 5
Bugs 1 Features 0
Metric Value
dl 0
loc 9
ccs 0
cts 5
cp 0
rs 9.6666
c 5
b 1
f 0
cc 2
eloc 5
nc 2
nop 0
crap 6

1 Method

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