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) { |
|
|
|
|
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($this->getIncludeTests(), $flush); |
184
|
|
|
|
185
|
|
|
// Find modules |
186
|
|
|
$this->getModuleLoader()->init($this->getIncludeTests(), $flush); |
187
|
|
|
|
188
|
|
|
// Flush config |
189
|
|
|
if ($flush) { |
190
|
|
|
$config = $this->getConfigLoader()->getManifest(); |
191
|
|
|
if ($config instanceof CachedConfigCollection) { |
192
|
|
|
$config->setFlush(true); |
193
|
|
|
} |
194
|
|
|
} |
195
|
|
|
// tell modules to sort, now that config is available |
196
|
|
|
$this->getModuleLoader()->getManifest()->sort(); |
197
|
|
|
|
198
|
|
|
// Find default templates |
199
|
|
|
$defaultSet = $this->getThemeResourceLoader()->getSet('$default'); |
200
|
|
|
if ($defaultSet instanceof ThemeManifest) { |
201
|
|
|
$defaultSet->setProject( |
202
|
|
|
ModuleManifest::config()->get('project') |
203
|
|
|
); |
204
|
|
|
$defaultSet->init($this->getIncludeTests(), $flush); |
205
|
|
|
} |
206
|
|
|
} |
207
|
|
|
|
208
|
|
|
/** |
209
|
|
|
* Include all _config.php files |
210
|
|
|
*/ |
211
|
|
|
protected function bootConfigs() |
212
|
|
|
{ |
213
|
|
|
global $project; |
214
|
|
|
$projectBefore = $project; |
215
|
|
|
$config = ModuleManifest::config(); |
216
|
|
|
// After loading all other app manifests, include _config.php files |
217
|
|
|
$this->getModuleLoader()->getManifest()->activateConfig(); |
218
|
|
|
if ($project && $project !== $projectBefore) { |
219
|
|
|
Deprecation::notice('5.0', '$project global is deprecated'); |
220
|
|
|
$config->set('project', $project); |
221
|
|
|
} |
222
|
|
|
} |
223
|
|
|
|
224
|
|
|
/** |
225
|
|
|
* Turn on error handling |
226
|
|
|
* @throws Exception |
227
|
|
|
*/ |
228
|
|
|
protected function bootErrorHandling() |
229
|
|
|
{ |
230
|
|
|
// Register error handler |
231
|
|
|
$errorHandler = Injector::inst()->get(ErrorHandler::class); |
232
|
|
|
$errorHandler->start(); |
233
|
|
|
|
234
|
|
|
// Register error log file |
235
|
|
|
$errorLog = Environment::getEnv('SS_ERROR_LOG'); |
236
|
|
|
if ($errorLog) { |
237
|
|
|
$logger = Injector::inst()->get(LoggerInterface::class); |
238
|
|
|
if ($logger instanceof Logger) { |
239
|
|
|
$logger->pushHandler(new StreamHandler($this->basePath . '/' . $errorLog, Logger::WARNING)); |
240
|
|
|
} else { |
241
|
|
|
user_error("SS_ERROR_LOG setting only works with Monolog, you are using another logger", E_USER_WARNING); |
242
|
|
|
} |
243
|
|
|
} |
244
|
|
|
} |
245
|
|
|
|
246
|
|
|
|
247
|
|
|
|
248
|
|
|
/** |
249
|
|
|
* Get the environment type |
250
|
|
|
* |
251
|
|
|
* @return string |
252
|
|
|
* |
253
|
|
|
* @deprecated 5.0 use Director::get_environment_type() instead. Since 5.0 it should return only if kernel overrides. No checking SESSION or Environment. |
254
|
|
|
*/ |
255
|
|
|
public function getEnvironment() |
256
|
|
|
{ |
257
|
|
|
// Check set |
258
|
|
|
if ($this->enviroment) { |
259
|
|
|
return $this->enviroment; |
260
|
|
|
} |
261
|
|
|
|
262
|
|
|
// Check saved session |
263
|
|
|
$env = $this->sessionEnvironment(); |
|
|
|
|
264
|
|
|
if ($env) { |
265
|
|
|
return $env; |
266
|
|
|
} |
267
|
|
|
|
268
|
|
|
// Check getenv |
269
|
|
|
if ($env = Environment::getEnv('SS_ENVIRONMENT_TYPE')) { |
270
|
|
|
return $env; |
271
|
|
|
} |
272
|
|
|
|
273
|
|
|
return self::LIVE; |
274
|
|
|
} |
275
|
|
|
|
276
|
|
|
/** |
277
|
|
|
* Check or update any temporary environment specified in the session. |
278
|
|
|
* |
279
|
|
|
* @return null|string |
280
|
|
|
* |
281
|
|
|
* @deprecated 5.0 Use Director::get_session_environment_type() instead |
282
|
|
|
*/ |
283
|
|
|
protected function sessionEnvironment() |
284
|
|
|
{ |
285
|
|
|
if (!$this->booted) { |
286
|
|
|
// session is not initialyzed yet, neither is manifest |
287
|
|
|
return null; |
288
|
|
|
} |
289
|
|
|
|
290
|
|
|
return Director::get_session_environment_type(); |
291
|
|
|
} |
292
|
|
|
|
293
|
|
|
abstract public function boot($flush = false); |
294
|
|
|
|
295
|
|
|
abstract public function isFlushed(); |
296
|
|
|
|
297
|
|
|
/** |
298
|
|
|
* Check if there's a legacy _ss_environment.php file |
299
|
|
|
* |
300
|
|
|
* @throws HTTPResponse_Exception |
301
|
|
|
*/ |
302
|
|
|
protected function detectLegacyEnvironment() |
303
|
|
|
{ |
304
|
|
|
// Is there an _ss_environment.php file? |
305
|
|
|
if (!file_exists($this->basePath . '/_ss_environment.php') && |
306
|
|
|
!file_exists(dirname($this->basePath) . '/_ss_environment.php') |
307
|
|
|
) { |
308
|
|
|
return; |
309
|
|
|
} |
310
|
|
|
|
311
|
|
|
// Build error response |
312
|
|
|
$dv = new DebugView(); |
313
|
|
|
$body = implode([ |
314
|
|
|
$dv->renderHeader(), |
315
|
|
|
$dv->renderInfo( |
316
|
|
|
"Configuration Error", |
317
|
|
|
Director::absoluteBaseURL() |
318
|
|
|
), |
319
|
|
|
$dv->renderParagraph( |
320
|
|
|
'You need to replace your _ss_environment.php file with a .env file, or with environment variables.<br><br>' |
321
|
|
|
. 'See the <a href="https://docs.silverstripe.org/en/4/getting_started/environment_management/">' |
322
|
|
|
. 'Environment Management</a> docs for more information.' |
323
|
|
|
), |
324
|
|
|
$dv->renderFooter() |
325
|
|
|
]); |
326
|
|
|
|
327
|
|
|
// Raise error |
328
|
|
|
$response = new HTTPResponse($body, 500); |
329
|
|
|
throw new HTTPResponse_Exception($response); |
330
|
|
|
} |
331
|
|
|
|
332
|
|
|
/** |
333
|
|
|
* If missing configuration, redirect to install.php if it exists. |
334
|
|
|
* Otherwise show a server error to the user. |
335
|
|
|
* |
336
|
|
|
* @param string $msg Optional message to show to the user on an installed project (install.php missing). |
337
|
|
|
*/ |
338
|
|
|
protected function redirectToInstaller($msg = '') |
339
|
|
|
{ |
340
|
|
|
// Error if installer not available |
341
|
|
|
if (!file_exists(Director::publicFolder() . '/install.php')) { |
342
|
|
|
throw new HTTPResponse_Exception( |
343
|
|
|
$msg, |
344
|
|
|
500 |
345
|
|
|
); |
346
|
|
|
} |
347
|
|
|
|
348
|
|
|
// Redirect to installer |
349
|
|
|
$response = new HTTPResponse(); |
350
|
|
|
$response->redirect(Director::absoluteURL('install.php')); |
351
|
|
|
throw new HTTPResponse_Exception($response); |
352
|
|
|
} |
353
|
|
|
|
354
|
|
|
/** |
355
|
|
|
* @return ManifestCacheFactory |
356
|
|
|
*/ |
357
|
|
|
protected function buildManifestCacheFactory() |
358
|
|
|
{ |
359
|
|
|
return new ManifestCacheFactory([ |
360
|
|
|
'namespace' => 'manifestcache', |
361
|
|
|
'directory' => TempFolder::getTempFolder($this->basePath), |
362
|
|
|
]); |
363
|
|
|
} |
364
|
|
|
|
365
|
|
|
/** |
366
|
|
|
* @return bool |
367
|
|
|
*/ |
368
|
|
|
protected function getIncludeTests() |
369
|
|
|
{ |
370
|
|
|
return false; |
371
|
|
|
} |
372
|
|
|
|
373
|
|
|
/** |
374
|
|
|
* @param bool $bool |
375
|
|
|
*/ |
376
|
|
|
protected function setBooted(bool $bool): void |
377
|
|
|
{ |
378
|
|
|
$this->booted = $bool; |
379
|
|
|
} |
380
|
|
|
|
381
|
|
|
public function shutdown() |
382
|
|
|
{ |
383
|
|
|
} |
384
|
|
|
|
385
|
|
|
public function nest() |
386
|
|
|
{ |
387
|
|
|
// Clone this kernel, nesting config / injector manifest containers |
388
|
|
|
$kernel = clone $this; |
389
|
|
|
$kernel->setConfigLoader($this->configLoader->nest()); |
390
|
|
|
$kernel->setInjectorLoader($this->injectorLoader->nest()); |
391
|
|
|
$kernel->nestedFrom = $this; |
392
|
|
|
return $kernel; |
393
|
|
|
} |
394
|
|
|
|
395
|
|
|
public function activate() |
396
|
|
|
{ |
397
|
|
|
$this->configLoader->activate(); |
398
|
|
|
$this->injectorLoader->activate(); |
399
|
|
|
|
400
|
|
|
// Self register |
401
|
|
|
$this->getInjectorLoader() |
402
|
|
|
->getManifest() |
403
|
|
|
->registerService($this, Kernel::class); |
404
|
|
|
return $this; |
405
|
|
|
} |
406
|
|
|
|
407
|
|
|
public function getNestedFrom() |
408
|
|
|
{ |
409
|
|
|
return $this->nestedFrom; |
410
|
|
|
} |
411
|
|
|
|
412
|
|
|
public function getContainer() |
413
|
|
|
{ |
414
|
|
|
return $this->getInjectorLoader()->getManifest(); |
415
|
|
|
} |
416
|
|
|
|
417
|
|
|
public function setInjectorLoader(InjectorLoader $injectorLoader) |
418
|
|
|
{ |
419
|
|
|
$this->injectorLoader = $injectorLoader; |
420
|
|
|
$injectorLoader |
421
|
|
|
->getManifest() |
422
|
|
|
->registerService($this, Kernel::class); |
423
|
|
|
return $this; |
424
|
|
|
} |
425
|
|
|
|
426
|
|
|
public function getInjectorLoader() |
427
|
|
|
{ |
428
|
|
|
return $this->injectorLoader; |
429
|
|
|
} |
430
|
|
|
|
431
|
|
|
public function getClassLoader() |
432
|
|
|
{ |
433
|
|
|
return $this->classLoader; |
434
|
|
|
} |
435
|
|
|
|
436
|
|
|
public function setClassLoader(ClassLoader $classLoader) |
437
|
|
|
{ |
438
|
|
|
$this->classLoader = $classLoader; |
439
|
|
|
return $this; |
440
|
|
|
} |
441
|
|
|
|
442
|
|
|
public function getModuleLoader() |
443
|
|
|
{ |
444
|
|
|
return $this->moduleLoader; |
445
|
|
|
} |
446
|
|
|
|
447
|
|
|
public function setModuleLoader(ModuleLoader $moduleLoader) |
448
|
|
|
{ |
449
|
|
|
$this->moduleLoader = $moduleLoader; |
450
|
|
|
return $this; |
451
|
|
|
} |
452
|
|
|
|
453
|
|
|
public function setEnvironment($environment) |
454
|
|
|
{ |
455
|
|
|
if (!in_array($environment, [self::DEV, self::TEST, self::LIVE, null])) { |
456
|
|
|
throw new InvalidArgumentException( |
457
|
|
|
"Director::set_environment_type passed '$environment'. It should be passed dev, test, or live" |
458
|
|
|
); |
459
|
|
|
} |
460
|
|
|
$this->enviroment = $environment; |
461
|
|
|
return $this; |
462
|
|
|
} |
463
|
|
|
|
464
|
|
|
public function getConfigLoader() |
465
|
|
|
{ |
466
|
|
|
return $this->configLoader; |
467
|
|
|
} |
468
|
|
|
|
469
|
|
|
public function setConfigLoader($configLoader) |
470
|
|
|
{ |
471
|
|
|
$this->configLoader = $configLoader; |
472
|
|
|
return $this; |
473
|
|
|
} |
474
|
|
|
|
475
|
|
|
public function getThemeResourceLoader() |
476
|
|
|
{ |
477
|
|
|
return $this->themeResourceLoader; |
478
|
|
|
} |
479
|
|
|
|
480
|
|
|
public function setThemeResourceLoader($themeResourceLoader) |
481
|
|
|
{ |
482
|
|
|
$this->themeResourceLoader = $themeResourceLoader; |
483
|
|
|
return $this; |
484
|
|
|
} |
485
|
|
|
} |
486
|
|
|
|
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.