Passed
Pull Request — 4 (#10016)
by Maxime
09:20
created

BaseKernel::setClassLoader()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 2
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 4
rs 10
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
260
    /**
261
     * Get the environment type
262
     *
263
     * @return string
264
     *
265
     * @deprecated 5.0 use Director::get_environment_type() instead. Since 5.0 it should return only if kernel overrides. No checking SESSION or Environment.
266
     */
267
    public function getEnvironment()
268
    {
269
        // Check set
270
        if ($this->enviroment) {
271
            return $this->enviroment;
272
        }
273
274
        // Check saved session
275
        $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

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