Completed
Push — master ( efe256...811ea7 )
by Alexander
11s
created

AspectKernel::getContainer()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 1
1
<?php
2
declare(strict_types = 1);
3
/*
4
 * Go! AOP framework
5
 *
6
 * @copyright Copyright 2012, Lisachenko Alexander <[email protected]>
7
 *
8
 * This source file is subject to the license that is bundled
9
 * with this source code in the file LICENSE.
10
 */
11
12
namespace Go\Core;
13
14
use Go\Aop\Features;
15
use Go\Instrument\ClassLoading\AopComposerLoader;
16
use Go\Instrument\ClassLoading\SourceTransformingLoader;
17
use Go\Instrument\PathResolver;
18
use Go\Instrument\Transformer\ConstructorExecutionTransformer;
19
use Go\Instrument\Transformer\SelfValueTransformer;
20
use Go\Instrument\Transformer\SourceTransformer;
21
use Go\Instrument\Transformer\WeavingTransformer;
22
use Go\Instrument\Transformer\CachingTransformer;
23
use Go\Instrument\Transformer\FilterInjectorTransformer;
24
use Go\Instrument\Transformer\MagicConstantTransformer;
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 = '2.1.0';
36
37
    /**
38
     * Kernel options
39
     *
40
     * @var array
41
     */
42
    protected $options = [
43
        'features' => 0
44
    ];
45
46
    /**
47
     * Single instance of kernel
48
     *
49
     * @var static
50
     */
51
    protected static $instance;
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 AspectContainer
71
     */
72
    protected $container;
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 1
    public static function getInstance(): self
83
    {
84 1
        if (!self::$instance) {
85 1
            self::$instance = new static();
86
        }
87
88 1
        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 1
    public function init(array $options = [])
97
    {
98 1
        if ($this->wasInitialized) {
99
            return;
100
        }
101
102 1
        $this->options = $this->normalizeOptions($options);
103 1
        define('AOP_ROOT_DIR', $this->options['appDir']);
104 1
        define('AOP_CACHE_DIR', $this->options['cacheDir']);
105
106
        /** @var $container AspectContainer */
107 1
        $container = $this->container = new $this->options['containerClass'];
108 1
        $container->set('kernel', $this);
109 1
        $container->set('kernel.interceptFunctions', $this->hasFeature(Features::INTERCEPT_FUNCTIONS));
110 1
        $container->set('kernel.options', $this->options);
111
112 1
        SourceTransformingLoader::register();
113
114 1
        foreach ($this->registerTransformers() as $sourceTransformer) {
115 1
            SourceTransformingLoader::addTransformer($sourceTransformer);
116
        }
117
118
        // Register kernel resources in the container for debug mode
119 1
        if ($this->options['debug']) {
120 1
            $this->addKernelResourcesToContainer($container);
121
        }
122
123 1
        AopComposerLoader::init($this->options, $container);
124
125
        // Register all AOP configuration in the container
126 1
        $this->configureAop($container);
127
128 1
        $this->wasInitialized = true;
129 1
    }
130
131
    /**
132
     * Returns an aspect container
133
     */
134 1
    public function getContainer(): AspectContainer
135
    {
136 1
        return $this->container;
137
    }
138
139
    /**
140
     * Checks if kernel configuration has enabled specific feature
141
     *
142
     * @see \Go\Aop\Features enumeration class for features
143
     */
144 1
    public function hasFeature(int $featureToCheck): bool
145
    {
146 1
        return ($this->options['features'] & $featureToCheck) !== 0;
147
    }
148
149
    /**
150
     * Returns list of kernel options
151
     */
152 1
    public function getOptions(): array
153
    {
154 1
        return $this->options;
155
    }
156
157
    /**
158
     * Returns default options for kernel. Available options:
159
     *
160
     *   debug    - boolean Determines whether or not kernel is in debug mode
161
     *   appDir   - string Path to the application root directory.
162
     *   cacheDir - string Path to the cache directory where compiled classes will be stored
163
     *   cacheFileMode - integer Binary mask of permission bits that is set to cache files
164
     *   annotationCache - Doctrine\Common\Cache\Cache. If not provided, Doctrine\Common\Cache\PhpFileCache is used.
165
     *   features - integer Binary mask of features
166
     *   includePaths - array Whitelist of directories where aspects should be applied. Empty for everywhere.
167
     *   excludePaths - array Blacklist of directories or files where aspects shouldn't be applied.
168
     */
169 1
    protected function getDefaultOptions(): array
170
    {
171
        return [
172 1
            'debug'           => false,
173
            'appDir'          => __DIR__ . '/../../../../../',
174
            'cacheDir'        => null,
175 1
            'cacheFileMode'   => 0770 & ~umask(), // Respect user umask() policy
176 1
            'features'        => 0,
177
            'annotationCache' => null,
178
            'includePaths'    => [],
179
            'excludePaths'    => [],
180 1
            'containerClass'  => static::$containerClass,
181
        ];
182
    }
183
184
185
    /**
186
     * Normalizes options for the kernel
187
     *
188
     * @param array $options List of options
189
     */
190 1
    protected function normalizeOptions(array $options): array
191
    {
192 1
        $options = array_replace($this->getDefaultOptions(), $options);
193
194 1
        $options['cacheDir'] = PathResolver::realpath($options['cacheDir']);
195
196 1
        if (!$options['cacheDir']) {
197
            throw new \RuntimeException('You need to provide valid cache directory for Go! AOP framework.');
198
        }
199
200 1
        $options['excludePaths'][] = $options['cacheDir'];
201 1
        $options['excludePaths'][] = __DIR__ . '/../';
202 1
        $options['appDir']         = PathResolver::realpath($options['appDir']);
203 1
        $options['cacheFileMode']  = (int) $options['cacheFileMode'];
204 1
        $options['includePaths']   = PathResolver::realpath($options['includePaths']);
205 1
        $options['excludePaths']   = PathResolver::realpath($options['excludePaths']);
206
207 1
        return $options;
208
    }
209
210
    /**
211
     * Configures an AspectContainer with advisors, aspects and pointcuts
212
     */
213
    abstract protected function configureAop(AspectContainer $container);
214
215
    /**
216
     * Returns list of source transformers, that will be applied to the PHP source
217
     *
218
     * @return SourceTransformer[]
219
     * @internal This method is internal and should not be used outside this project
220
     */
221 1
    protected function registerTransformers(): array
222
    {
223 1
        $cacheManager     = $this->getContainer()->get('aspect.cache.path.manager');
224 1
        $filterInjector   = new FilterInjectorTransformer($this, SourceTransformingLoader::getId(), $cacheManager);
225 1
        $magicTransformer = new MagicConstantTransformer($this);
226
227 1
        $sourceTransformers = function () use ($filterInjector, $magicTransformer, $cacheManager) {
228 1
            $transformers = [];
229 1
            if ($this->hasFeature(Features::INTERCEPT_INITIALIZATIONS)) {
230
                $transformers[] = new ConstructorExecutionTransformer();
231
            }
232 1
            if ($this->hasFeature(Features::INTERCEPT_INCLUDES)) {
233
                $transformers[] = $filterInjector;
234
            }
235 1
            $transformers[]  = new SelfValueTransformer($this);
236 1
            $transformers[]  = new WeavingTransformer(
237 1
                $this,
238 1
                $this->container->get('aspect.advice_matcher'),
239 1
                $cacheManager,
240 1
                $this->container->get('aspect.cached.loader')
241
            );
242 1
            $transformers[] = $magicTransformer;
243
244 1
            return $transformers;
245 1
        };
246
247
        return [
248 1
            new CachingTransformer($this, $sourceTransformers, $cacheManager)
249
        ];
250
    }
251
252
    /**
253
     * Add resources of kernel to the container
254
     */
255 1
    protected function addKernelResourcesToContainer(AspectContainer $container): void
256
    {
257 1
        $trace    = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
258 1
        $refClass = new \ReflectionObject($this);
259
260 1
        $container->addResource($trace[1]['file']);
261 1
        $container->addResource($refClass->getFileName());
262 1
    }
263
}
264