Passed
Push — 4 ( 3b5c72...d8499a )
by Steve
07:17
created

BaseKernel   B

Complexity

Total Complexity 47

Size/Duplication

Total Lines 471
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 47
eloc 162
c 1
b 0
f 0
dl 0
loc 471
rs 8.64

29 Methods

Rating   Name   Duplication   Size   Complexity  
A bootPHP() 0 34 4
A __construct() 0 42 1
A bootConfigs() 0 10 3
A bootManifests() 0 36 4
A bootErrorHandling() 0 14 3
A getThemeResourceLoader() 0 3 1
A setInjectorLoader() 0 7 1
A getClassLoader() 0 3 1
A setModuleLoader() 0 4 1
A setThemeResourceLoader() 0 4 1
A setConfigLoader() 0 4 1
A detectLegacyEnvironment() 0 28 3
A getIgnoredCIConfigs() 0 3 1
A shutdown() 0 2 1
A getContainer() 0 3 1
A buildManifestCacheFactory() 0 5 1
A setBooted() 0 3 1
A getConfigLoader() 0 3 1
A setEnvironment() 0 9 2
A getNestedFrom() 0 3 1
A sessionEnvironment() 0 8 2
A nest() 0 8 1
A getEnvironment() 0 19 4
A getIncludeTests() 0 3 1
A getModuleLoader() 0 3 1
A getInjectorLoader() 0 3 1
A redirectToInstaller() 0 14 2
A activate() 0 10 1
A setClassLoader() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like BaseKernel often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use BaseKernel, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace SilverStripe\Core;
4
5
use InvalidArgumentException;
6
use Monolog\Handler\StreamHandler;
7
use Monolog\Logger;
8
use Psr\Log\LoggerInterface;
9
use SilverStripe\Config\Collections\CachedConfigCollection;
10
use SilverStripe\Control\Director;
11
use SilverStripe\Control\HTTPResponse;
12
use SilverStripe\Control\HTTPResponse_Exception;
13
use SilverStripe\Core\Cache\ManifestCacheFactory;
14
use SilverStripe\Core\Config\ConfigLoader;
15
use SilverStripe\Core\Config\CoreConfigFactory;
16
use SilverStripe\Core\Injector\Injector;
17
use SilverStripe\Core\Injector\InjectorLoader;
18
use SilverStripe\Core\Injector\SilverStripeServiceConfigurationLocator;
19
use SilverStripe\Core\Manifest\ClassLoader;
20
use SilverStripe\Core\Manifest\ClassManifest;
21
use SilverStripe\Core\Manifest\ModuleLoader;
22
use SilverStripe\Core\Manifest\ModuleManifest;
23
use SilverStripe\Dev\DebugView;
24
use SilverStripe\Dev\Deprecation;
25
use SilverStripe\Logging\ErrorHandler;
26
use SilverStripe\View\PublicThemes;
27
use SilverStripe\View\SSViewer;
28
use SilverStripe\View\ThemeManifest;
29
use SilverStripe\View\ThemeResourceLoader;
30
use Exception;
31
32
/**
33
 * Simple Kernel container
34
 */
35
abstract class BaseKernel implements Kernel
36
{
37
    /**
38
     * @var Kernel
39
     */
40
    protected $nestedFrom = null;
41
42
    /**
43
     * @var Injector
44
     */
45
    protected $container = null;
46
47
    /**
48
     * @var string
49
     */
50
    protected $enviroment = null;
51
52
    /**
53
     * @var ClassLoader
54
     */
55
    protected $classLoader = null;
56
57
    /**
58
     * @var ModuleLoader
59
     */
60
    protected $moduleLoader = null;
61
62
    /**
63
     * @var ConfigLoader
64
     */
65
    protected $configLoader = null;
66
67
    /**
68
     * @var InjectorLoader
69
     */
70
    protected $injectorLoader = null;
71
72
    /**
73
     * @var ThemeResourceLoader
74
     */
75
    protected $themeResourceLoader = null;
76
77
    protected $basePath = null;
78
79
    /**
80
     * Indicates whether the Kernel has been booted already
81
     *
82
     * @var bool
83
     */
84
    private $booted = false;
85
86
87
    /**
88
     * Create a new kernel for this application
89
     *
90
     * @param string $basePath Path to base dir for this application
91
     */
92
    public function __construct($basePath)
93
    {
94
        $this->basePath = $basePath;
95
96
        // Initialise the dependency injector as soon as possible, as it is
97
        // subsequently used by some of the following code
98
        $injectorLoader = InjectorLoader::inst();
99
        $injector = new Injector(['locator' => SilverStripeServiceConfigurationLocator::class]);
100
        $injectorLoader->pushManifest($injector);
101
        $this->setInjectorLoader($injectorLoader);
102
103
        // Manifest cache factory
104
        $manifestCacheFactory = $this->buildManifestCacheFactory();
105
106
        // Class loader
107
        $classLoader = ClassLoader::inst();
108
        $classLoader->pushManifest(new ClassManifest($basePath, $manifestCacheFactory));
109
        $this->setClassLoader($classLoader);
110
111
        // Module loader
112
        $moduleLoader = ModuleLoader::inst();
113
        $moduleManifest = new ModuleManifest($basePath, $manifestCacheFactory);
114
        $moduleLoader->pushManifest($moduleManifest);
115
        $this->setModuleLoader($moduleLoader);
116
117
        // Config loader
118
        // @todo refactor CoreConfigFactory
119
        $configFactory = new CoreConfigFactory($manifestCacheFactory);
120
        $configManifest = $configFactory->createRoot();
121
        $configLoader = ConfigLoader::inst();
122
        $configLoader->pushManifest($configManifest);
123
        $this->setConfigLoader($configLoader);
124
125
        // Load template manifest
126
        $themeResourceLoader = ThemeResourceLoader::inst();
127
        $themeResourceLoader->addSet(SSViewer::PUBLIC_THEME, new PublicThemes());
128
        $themeResourceLoader->addSet(SSViewer::DEFAULT_THEME, new ThemeManifest(
129
            $basePath,
130
            null, // project is defined in config, and this argument is deprecated
131
            $manifestCacheFactory
132
        ));
133
        $this->setThemeResourceLoader($themeResourceLoader);
134
    }
135
136
    /**
137
     * Initialise PHP with default variables
138
     */
139
    protected function bootPHP()
140
    {
141
        if ($this->getEnvironment() === self::LIVE) {
0 ignored issues
show
Deprecated Code introduced by
The function SilverStripe\Core\BaseKernel::getEnvironment() has been deprecated: 5.0 use Director::get_environment_type() instead. Since 5.0 it should return only if kernel overrides. No checking SESSION or Environment. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

141
        if (/** @scrutinizer ignore-deprecated */ $this->getEnvironment() === self::LIVE) {

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
142
            // limited to fatal errors and warnings in live mode
143
            error_reporting(E_ALL & ~(E_DEPRECATED | E_STRICT | E_NOTICE));
144
        } else {
145
            // Report all errors in dev / test mode
146
            error_reporting(E_ALL | E_STRICT);
147
        }
148
149
        /**
150
         * Ensure we have enough memory
151
         */
152
        Environment::increaseMemoryLimitTo('64M');
153
154
        // Ensure we don't run into xdebug's fairly conservative infinite recursion protection limit
155
        if (function_exists('xdebug_enable')) {
156
            $current = ini_get('xdebug.max_nesting_level');
157
            if ((int)$current < 200) {
158
                ini_set('xdebug.max_nesting_level', 200);
159
            }
160
        }
161
162
        /**
163
         * Set default encoding
164
         */
165
        mb_http_output('UTF-8');
166
        mb_internal_encoding('UTF-8');
167
        mb_regex_encoding('UTF-8');
168
169
        /**
170
         * Enable better garbage collection
171
         */
172
        gc_enable();
173
    }
174
175
    /**
176
     * Boot all manifests
177
     *
178
     * @param bool $flush
179
     */
180
    protected function bootManifests($flush)
181
    {
182
        // Setup autoloader
183
        $this->getClassLoader()->init(
184
            $this->getIncludeTests(),
185
            $flush,
186
            $this->getIgnoredCIConfigs()
187
        );
188
189
        // Find modules
190
        $this->getModuleLoader()->init(
191
            $this->getIncludeTests(),
192
            $flush,
193
            $this->getIgnoredCIConfigs()
194
        );
195
196
        // Flush config
197
        if ($flush) {
198
            $config = $this->getConfigLoader()->getManifest();
199
            if ($config instanceof CachedConfigCollection) {
200
                $config->setFlush(true);
201
            }
202
        }
203
        // tell modules to sort, now that config is available
204
        $this->getModuleLoader()->getManifest()->sort();
205
206
        // Find default templates
207
        $defaultSet = $this->getThemeResourceLoader()->getSet('$default');
208
        if ($defaultSet instanceof ThemeManifest) {
209
            $defaultSet->setProject(
210
                ModuleManifest::config()->get('project')
211
            );
212
            $defaultSet->init(
213
                $this->getIncludeTests(),
214
                $flush,
215
                $this->getIgnoredCIConfigs()
216
            );
217
        }
218
    }
219
220
    /**
221
     * Include all _config.php files
222
     */
223
    protected function bootConfigs()
224
    {
225
        global $project;
226
        $projectBefore = $project;
227
        $config = ModuleManifest::config();
228
        // After loading all other app manifests, include _config.php files
229
        $this->getModuleLoader()->getManifest()->activateConfig();
230
        if ($project && $project !== $projectBefore) {
231
            Deprecation::notice('5.0', '$project global is deprecated');
232
            $config->set('project', $project);
233
        }
234
    }
235
236
    /**
237
     * Turn on error handling
238
     * @throws Exception
239
     */
240
    protected function bootErrorHandling()
241
    {
242
        // Register error handler
243
        $errorHandler = Injector::inst()->get(ErrorHandler::class);
244
        $errorHandler->start();
245
246
        // Register error log file
247
        $errorLog = Environment::getEnv('SS_ERROR_LOG');
248
        if ($errorLog) {
249
            $logger = Injector::inst()->get(LoggerInterface::class);
250
            if ($logger instanceof Logger) {
251
                $logger->pushHandler(new StreamHandler($this->basePath . '/' . $errorLog, Logger::WARNING));
252
            } else {
253
                user_error("SS_ERROR_LOG setting only works with Monolog, you are using another logger", E_USER_WARNING);
254
            }
255
        }
256
    }
257
258
    /**
259
     * Get the environment type
260
     *
261
     * @return string
262
     *
263
     * @deprecated 5.0 use Director::get_environment_type() instead. Since 5.0 it should return only if kernel overrides. No checking SESSION or Environment.
264
     */
265
    public function getEnvironment()
266
    {
267
        // Check set
268
        if ($this->enviroment) {
269
            return $this->enviroment;
270
        }
271
272
        // Check saved session
273
        $env = $this->sessionEnvironment();
0 ignored issues
show
Deprecated Code introduced by
The function SilverStripe\Core\BaseKernel::sessionEnvironment() has been deprecated: 5.0 Use Director::get_session_environment_type() instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

273
        $env = /** @scrutinizer ignore-deprecated */ $this->sessionEnvironment();

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
274
        if ($env) {
275
            return $env;
276
        }
277
278
        // Check getenv
279
        if ($env = Environment::getEnv('SS_ENVIRONMENT_TYPE')) {
280
            return $env;
281
        }
282
283
        return self::LIVE;
284
    }
285
286
    /**
287
     * Check or update any temporary environment specified in the session.
288
     *
289
     * @return null|string
290
     *
291
     * @deprecated 5.0 Use Director::get_session_environment_type() instead
292
     */
293
    protected function sessionEnvironment()
294
    {
295
        if (!$this->booted) {
296
            // session is not initialyzed yet, neither is manifest
297
            return null;
298
        }
299
300
        return Director::get_session_environment_type();
301
    }
302
303
    abstract public function boot($flush = false);
304
305
    abstract public function isFlushed();
306
307
    /**
308
     * Check if there's a legacy _ss_environment.php file
309
     *
310
     * @throws HTTPResponse_Exception
311
     */
312
    protected function detectLegacyEnvironment()
313
    {
314
        // Is there an _ss_environment.php file?
315
        if (!file_exists($this->basePath . '/_ss_environment.php') &&
316
            !file_exists(dirname($this->basePath) . '/_ss_environment.php')
317
        ) {
318
            return;
319
        }
320
321
        // Build error response
322
        $dv = new DebugView();
323
        $body = implode([
324
            $dv->renderHeader(),
325
            $dv->renderInfo(
326
                "Configuration Error",
327
                Director::absoluteBaseURL()
328
            ),
329
            $dv->renderParagraph(
330
                'You need to replace your _ss_environment.php file with a .env file, or with environment variables.<br><br>'
331
                . 'See the <a href="https://docs.silverstripe.org/en/4/getting_started/environment_management/">'
332
                . 'Environment Management</a> docs for more information.'
333
            ),
334
            $dv->renderFooter()
335
        ]);
336
337
        // Raise error
338
        $response = new HTTPResponse($body, 500);
339
        throw new HTTPResponse_Exception($response);
340
    }
341
342
    /**
343
     * If missing configuration, redirect to install.php if it exists.
344
     * Otherwise show a server error to the user.
345
     *
346
     * @param string $msg Optional message to show to the user on an installed project (install.php missing).
347
     */
348
    protected function redirectToInstaller($msg = '')
349
    {
350
        // Error if installer not available
351
        if (!file_exists(Director::publicFolder() . '/install.php')) {
352
            throw new HTTPResponse_Exception(
353
                $msg,
354
                500
355
            );
356
        }
357
358
        // Redirect to installer
359
        $response = new HTTPResponse();
360
        $response->redirect(Director::absoluteURL('install.php'));
361
        throw new HTTPResponse_Exception($response);
362
    }
363
364
    /**
365
     * @return ManifestCacheFactory
366
     */
367
    protected function buildManifestCacheFactory()
368
    {
369
        return new ManifestCacheFactory([
370
            'namespace' => 'manifestcache',
371
            'directory' => TEMP_PATH,
372
        ]);
373
    }
374
375
    /**
376
     * When manifests are discovering files, tests files in modules using the following CI library type will be ignored.
377
     *
378
     * The purpose of this method is to avoid loading PHPUnit test files with incompatible definitions.
379
     *
380
     * @return string[] List of CI types to ignore as defined by `Module`.
381
     */
382
    protected function getIgnoredCIConfigs(): array
383
    {
384
        return [];
385
    }
386
387
    /**
388
     * @return bool
389
     */
390
    protected function getIncludeTests()
391
    {
392
        return false;
393
    }
394
395
    /**
396
     * @param bool $bool
397
     */
398
    protected function setBooted(bool $bool): void
399
    {
400
        $this->booted = $bool;
401
    }
402
403
    public function shutdown()
404
    {
405
    }
406
407
    public function nest()
408
    {
409
        // Clone this kernel, nesting config / injector manifest containers
410
        $kernel = clone $this;
411
        $kernel->setConfigLoader($this->configLoader->nest());
412
        $kernel->setInjectorLoader($this->injectorLoader->nest());
413
        $kernel->nestedFrom = $this;
414
        return $kernel;
415
    }
416
417
    public function activate()
418
    {
419
        $this->configLoader->activate();
420
        $this->injectorLoader->activate();
421
422
        // Self register
423
        $this->getInjectorLoader()
424
            ->getManifest()
425
            ->registerService($this, Kernel::class);
426
        return $this;
427
    }
428
429
    public function getNestedFrom()
430
    {
431
        return $this->nestedFrom;
432
    }
433
434
    public function getContainer()
435
    {
436
        return $this->getInjectorLoader()->getManifest();
437
    }
438
439
    public function setInjectorLoader(InjectorLoader $injectorLoader)
440
    {
441
        $this->injectorLoader = $injectorLoader;
442
        $injectorLoader
443
            ->getManifest()
444
            ->registerService($this, Kernel::class);
445
        return $this;
446
    }
447
448
    public function getInjectorLoader()
449
    {
450
        return $this->injectorLoader;
451
    }
452
453
    public function getClassLoader()
454
    {
455
        return $this->classLoader;
456
    }
457
458
    public function setClassLoader(ClassLoader $classLoader)
459
    {
460
        $this->classLoader = $classLoader;
461
        return $this;
462
    }
463
464
    public function getModuleLoader()
465
    {
466
        return $this->moduleLoader;
467
    }
468
469
    public function setModuleLoader(ModuleLoader $moduleLoader)
470
    {
471
        $this->moduleLoader = $moduleLoader;
472
        return $this;
473
    }
474
475
    public function setEnvironment($environment)
476
    {
477
        if (!in_array($environment, [self::DEV, self::TEST, self::LIVE, null])) {
478
            throw new InvalidArgumentException(
479
                "Director::set_environment_type passed '$environment'.  It should be passed dev, test, or live"
480
            );
481
        }
482
        $this->enviroment = $environment;
483
        return $this;
484
    }
485
486
    public function getConfigLoader()
487
    {
488
        return $this->configLoader;
489
    }
490
491
    public function setConfigLoader($configLoader)
492
    {
493
        $this->configLoader = $configLoader;
494
        return $this;
495
    }
496
497
    public function getThemeResourceLoader()
498
    {
499
        return $this->themeResourceLoader;
500
    }
501
502
    public function setThemeResourceLoader($themeResourceLoader)
503
    {
504
        $this->themeResourceLoader = $themeResourceLoader;
505
        return $this;
506
    }
507
}
508