9158a021db4b474840ce0dd2812121ffb6367a5a
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Core / Bootstrap.php
1 <?php
2 namespace TYPO3\CMS\Core\Core;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
18 use TYPO3\CMS\Core\Utility\GeneralUtility;
19 use TYPO3\CMS\Core\Utility\MathUtility;
20
21 /**
22 * This class encapsulates bootstrap related methods.
23 * It is required directly as the very first thing in entry scripts and
24 * used to define all base things like constants and paths and so on.
25 *
26 * Most methods in this class have dependencies to each other. They can
27 * not be called in arbitrary order. The methods are ordered top down, so
28 * a method at the beginning has lower dependencies than a method further
29 * down. Do not fiddle with the load order in own scripts except you know
30 * exactly what you are doing!
31 */
32 class Bootstrap
33 {
34 /**
35 * @var \TYPO3\CMS\Core\Core\Bootstrap
36 */
37 protected static $instance = null;
38
39 /**
40 * Unique Request ID
41 *
42 * @var string
43 */
44 protected $requestId;
45
46 /**
47 * The application context
48 *
49 * @var \TYPO3\CMS\Core\Core\ApplicationContext
50 */
51 protected $applicationContext;
52
53 /**
54 * @var array List of early instances
55 */
56 protected $earlyInstances = [];
57
58 /**
59 * @var string Path to install tool
60 */
61 protected $installToolPath;
62
63 /**
64 * A list of all registered request handlers, see the Application class / entry points for the registration
65 * @var \TYPO3\CMS\Core\Http\RequestHandlerInterface[]|\TYPO3\CMS\Core\Console\RequestHandlerInterface[]
66 */
67 protected $availableRequestHandlers = [];
68
69 /**
70 * The Response object when using Request/Response logic
71 * @var \Psr\Http\Message\ResponseInterface
72 * @see shutdown()
73 */
74 protected $response;
75
76 /**
77 * @var bool
78 */
79 protected static $usesComposerClassLoading = false;
80
81 /**
82 * Disable direct creation of this object.
83 * Set unique requestId and the application context
84 *
85 * @var string Application context
86 */
87 protected function __construct($applicationContext)
88 {
89 $this->requestId = substr(md5(uniqid('', true)), 0, 13);
90 $this->applicationContext = new ApplicationContext($applicationContext);
91 }
92
93 /**
94 * @return bool
95 */
96 public static function usesComposerClassLoading()
97 {
98 return self::$usesComposerClassLoading;
99 }
100
101 /**
102 * Disable direct cloning of this object.
103 */
104 protected function __clone()
105 {
106 }
107
108 /**
109 * Return 'this' as singleton
110 *
111 * @return Bootstrap
112 * @internal This is not a public API method, do not use in own extensions
113 */
114 public static function getInstance()
115 {
116 if (is_null(static::$instance)) {
117 $applicationContext = getenv('TYPO3_CONTEXT') ?: (getenv('REDIRECT_TYPO3_CONTEXT') ?: 'Production');
118 self::$instance = new static($applicationContext);
119 self::$instance->defineTypo3RequestTypes();
120 }
121 return static::$instance;
122 }
123
124 /**
125 * Gets the request's unique ID
126 *
127 * @return string Unique request ID
128 * @internal This is not a public API method, do not use in own extensions
129 */
130 public function getRequestId()
131 {
132 return $this->requestId;
133 }
134
135 /**
136 * Returns the application context this bootstrap was started in.
137 *
138 * @return \TYPO3\CMS\Core\Core\ApplicationContext The application context encapsulated in an object
139 * @internal This is not a public API method, do not use in own extensions.
140 * Use \TYPO3\CMS\Core\Utility\GeneralUtility::getApplicationContext() instead
141 */
142 public function getApplicationContext()
143 {
144 return $this->applicationContext;
145 }
146
147 /**
148 * Prevent any unwanted output that may corrupt AJAX/compression.
149 * This does not interfere with "die()" or "echo"+"exit()" messages!
150 *
151 * @return Bootstrap
152 * @internal This is not a public API method, do not use in own extensions
153 */
154 public function startOutputBuffering()
155 {
156 ob_start();
157 return $this;
158 }
159
160 /**
161 * Main entry point called at every request usually from Global scope. Checks if everything is correct,
162 * and loads the Configuration.
163 *
164 * Make sure that the baseSetup() is called before and the class loader is present
165 *
166 * @return Bootstrap
167 */
168 public function configure()
169 {
170 $this->startOutputBuffering()
171 ->loadConfigurationAndInitialize()
172 ->loadTypo3LoadedExtAndExtLocalconf(true)
173 ->setFinalCachingFrameworkCacheConfiguration()
174 ->defineLoggingAndExceptionConstants()
175 ->unsetReservedGlobalVariables();
176
177 return $this;
178 }
179
180 /**
181 * Run the base setup that checks server environment, determines paths,
182 * populates base files and sets common configuration.
183 *
184 * Script execution will be aborted if something fails here.
185 *
186 * @param int $entryPointLevel Number of subdirectories where the entry script is located under the document root
187 * @return Bootstrap
188 * @throws \RuntimeException when TYPO3_REQUESTTYPE was not set before, setRequestType() needs to be called before
189 * @internal This is not a public API method, do not use in own extensions
190 */
191 public function baseSetup($entryPointLevel = 0)
192 {
193 if (!defined('TYPO3_REQUESTTYPE')) {
194 throw new \RuntimeException('No Request Type was set, TYPO3 does not know in which context it is run.', 1450561838);
195 }
196 SystemEnvironmentBuilder::run($entryPointLevel);
197 if (!self::$usesComposerClassLoading && ClassLoadingInformation::isClassLoadingInformationAvailable()) {
198 ClassLoadingInformation::registerClassLoadingInformation();
199 }
200 GeneralUtility::presetApplicationContext($this->applicationContext);
201 return $this;
202 }
203
204 /**
205 * Sets the class loader to the bootstrap
206 *
207 * @param \Composer\Autoload\ClassLoader $classLoader an instance of the class loader
208 * @return Bootstrap
209 * @internal This is not a public API method, do not use in own extensions
210 */
211 public function initializeClassLoader($classLoader)
212 {
213 $this->setEarlyInstance(\Composer\Autoload\ClassLoader::class, $classLoader);
214 if (defined('TYPO3_COMPOSER_MODE') && TYPO3_COMPOSER_MODE) {
215 self::$usesComposerClassLoading = true;
216 }
217 return $this;
218 }
219
220 /**
221 * checks if LocalConfiguration.php or PackageStates.php is missing,
222 * used to see if a redirect to the install tool is needed
223 *
224 * @return bool TRUE when the essential configuration is available, otherwise FALSE
225 * @internal This is not a public API method, do not use in own extensions
226 */
227 public function checkIfEssentialConfigurationExists()
228 {
229 $configurationManager = new \TYPO3\CMS\Core\Configuration\ConfigurationManager;
230 $this->setEarlyInstance(\TYPO3\CMS\Core\Configuration\ConfigurationManager::class, $configurationManager);
231 return file_exists($configurationManager->getLocalConfigurationFileLocation()) && file_exists(PATH_typo3conf . 'PackageStates.php');
232 }
233
234 /**
235 * Redirect to install tool if LocalConfiguration.php is missing.
236 *
237 * @param int $entryPointLevel Number of subdirectories where the entry script is located under the document root
238 * @internal This is not a public API method, do not use in own extensions
239 */
240 public function redirectToInstallTool($entryPointLevel = 0)
241 {
242 $path = TYPO3_mainDir . 'sysext/install/Start/Install.php';
243 if ($entryPointLevel > 0) {
244 $path = str_repeat('../', $entryPointLevel) . $path;
245 }
246 header('Location: ' . $path);
247 die;
248 }
249
250 /**
251 * Adds available request handlers usually done via an application from the outside.
252 *
253 * @param string $requestHandler class which implements the request handler interface
254 * @return Bootstrap
255 * @internal This is not a public API method, do not use in own extensions
256 */
257 public function registerRequestHandlerImplementation($requestHandler)
258 {
259 $this->availableRequestHandlers[] = $requestHandler;
260 return $this;
261 }
262
263 /**
264 * Fetches the request handler that suits the best based on the priority and the interface
265 * Be sure to always have the constants that are defined in $this->defineTypo3RequestTypes() are set,
266 * so most RequestHandlers can check if they can handle the request.
267 *
268 * @param \Psr\Http\Message\ServerRequestInterface|\Symfony\Component\Console\Input\InputInterface $request
269 * @return \TYPO3\CMS\Core\Http\RequestHandlerInterface|\TYPO3\CMS\Core\Console\RequestHandlerInterface
270 * @throws \TYPO3\CMS\Core\Exception
271 * @internal This is not a public API method, do not use in own extensions
272 */
273 protected function resolveRequestHandler($request)
274 {
275 $suitableRequestHandlers = [];
276 foreach ($this->availableRequestHandlers as $requestHandlerClassName) {
277 /** @var \TYPO3\CMS\Core\Http\RequestHandlerInterface|\TYPO3\CMS\Core\Console\RequestHandlerInterface $requestHandler */
278 $requestHandler = GeneralUtility::makeInstance($requestHandlerClassName, $this);
279 if ($requestHandler->canHandleRequest($request)) {
280 $priority = $requestHandler->getPriority();
281 if (isset($suitableRequestHandlers[$priority])) {
282 throw new \TYPO3\CMS\Core\Exception('More than one request handler with the same priority can handle the request, but only one handler may be active at a time!', 1176471352);
283 }
284 $suitableRequestHandlers[$priority] = $requestHandler;
285 }
286 }
287 if (empty($suitableRequestHandlers)) {
288 throw new \TYPO3\CMS\Core\Exception('No suitable request handler found.', 1225418233);
289 }
290 ksort($suitableRequestHandlers);
291 return array_pop($suitableRequestHandlers);
292 }
293
294 /**
295 * Builds a Request instance from the current process, and then resolves the request
296 * through the request handlers depending on Frontend, Backend, CLI etc.
297 *
298 * @param \Psr\Http\Message\RequestInterface|\Symfony\Component\Console\Input\InputInterface $request
299 * @return Bootstrap
300 * @throws \TYPO3\CMS\Core\Exception
301 * @internal This is not a public API method, do not use in own extensions
302 */
303 public function handleRequest($request)
304 {
305 // Resolve request handler that were registered based on the Application
306 $requestHandler = $this->resolveRequestHandler($request);
307
308 // Execute the command which returns a Response object or NULL
309 $this->response = $requestHandler->handleRequest($request);
310 return $this;
311 }
312
313 /**
314 * Outputs content if there is a proper Response object.
315 *
316 * @return Bootstrap
317 */
318 protected function sendResponse()
319 {
320 if ($this->response instanceof \Psr\Http\Message\ResponseInterface) {
321 if (!headers_sent()) {
322 // If the response code was not changed by legacy code (still is 200)
323 // then allow the PSR-7 response object to explicitly set it.
324 // Otherwise let legacy code take precedence.
325 // This code path can be deprecated once we expose the response object to third party code
326 if (http_response_code() === 200) {
327 header('HTTP/' . $this->response->getProtocolVersion() . ' ' . $this->response->getStatusCode() . ' ' . $this->response->getReasonPhrase());
328 }
329
330 foreach ($this->response->getHeaders() as $name => $values) {
331 header($name . ': ' . implode(', ', $values));
332 }
333 }
334 echo $this->response->getBody()->__toString();
335 }
336 return $this;
337 }
338
339 /**
340 * Registers the instance of the specified object for an early boot stage.
341 * On finalizing the Object Manager initialization, all those instances will
342 * be transferred to the Object Manager's registry.
343 *
344 * @param string $objectName Object name, as later used by the Object Manager
345 * @param object $instance The instance to register
346 * @internal This is not a public API method, do not use in own extensions
347 */
348 public function setEarlyInstance($objectName, $instance)
349 {
350 $this->earlyInstances[$objectName] = $instance;
351 }
352
353 /**
354 * Returns an instance which was registered earlier through setEarlyInstance()
355 *
356 * @param string $objectName Object name of the registered instance
357 * @return object
358 * @throws \TYPO3\CMS\Core\Exception
359 * @internal This is not a public API method, do not use in own extensions
360 */
361 public function getEarlyInstance($objectName)
362 {
363 if (!isset($this->earlyInstances[$objectName])) {
364 throw new \TYPO3\CMS\Core\Exception('Unknown early instance "' . $objectName . '"', 1365167380);
365 }
366 return $this->earlyInstances[$objectName];
367 }
368
369 /**
370 * Returns all registered early instances indexed by object name
371 *
372 * @return array
373 * @internal This is not a public API method, do not use in own extensions
374 */
375 public function getEarlyInstances()
376 {
377 return $this->earlyInstances;
378 }
379
380 /**
381 * Includes LocalConfiguration.php and sets several
382 * global settings depending on configuration.
383 *
384 * @param bool $allowCaching Whether to allow caching - affects cache_core (autoloader)
385 * @param string $packageManagerClassName Define an alternative package manager implementation (usually for the installer)
386 * @return Bootstrap
387 * @internal This is not a public API method, do not use in own extensions
388 */
389 public function loadConfigurationAndInitialize($allowCaching = true, $packageManagerClassName = \TYPO3\CMS\Core\Package\PackageManager::class)
390 {
391 $this->populateLocalConfiguration()
392 ->initializeErrorHandling();
393 if (!$allowCaching) {
394 $this->disableCoreCache();
395 }
396 $this->initializeCachingFramework()
397 ->initializePackageManagement($packageManagerClassName)
398 ->initializeRuntimeActivatedPackagesFromConfiguration()
399 ->defineUserAgentConstant()
400 ->setCacheHashOptions()
401 ->setDefaultTimezone()
402 ->initializeL10nLocales()
403 ->setMemoryLimit();
404 if ($allowCaching) {
405 $this->ensureClassLoadingInformationExists();
406 }
407 return $this;
408 }
409
410 /**
411 * Initializes the package system and loads the package configuration and settings
412 * provided by the packages.
413 *
414 * @param string $packageManagerClassName Define an alternative package manager implementation (usually for the installer)
415 * @return Bootstrap
416 * @internal This is not a public API method, do not use in own extensions
417 */
418 public function initializePackageManagement($packageManagerClassName)
419 {
420 /** @var \TYPO3\CMS\Core\Package\PackageManager $packageManager */
421 $packageManager = new $packageManagerClassName();
422 $this->setEarlyInstance(\TYPO3\CMS\Core\Package\PackageManager::class, $packageManager);
423 ExtensionManagementUtility::setPackageManager($packageManager);
424 $packageManager->injectCoreCache($this->getEarlyInstance(\TYPO3\CMS\Core\Cache\CacheManager::class)->getCache('cache_core'));
425 $dependencyResolver = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Package\DependencyResolver::class);
426 $dependencyResolver->injectDependencyOrderingService(GeneralUtility::makeInstance(\TYPO3\CMS\Core\Service\DependencyOrderingService::class));
427 $packageManager->injectDependencyResolver($dependencyResolver);
428 $packageManager->initialize();
429 GeneralUtility::setSingletonInstance(\TYPO3\CMS\Core\Package\PackageManager::class, $packageManager);
430 return $this;
431 }
432
433 /**
434 * Writes class loading information if not yet present
435 *
436 * @return Bootstrap
437 * @internal This is not a public API method, do not use in own extensions
438 * @deprecated since TYPO3 v8, will be removed in TYPO3 v9. Use the Install Tool to dump autoload information.
439 */
440 public function ensureClassLoadingInformationExists()
441 {
442 if (!self::$usesComposerClassLoading && !ClassLoadingInformation::isClassLoadingInformationAvailable()) {
443 GeneralUtility::logDeprecatedFunction();
444 ClassLoadingInformation::dumpClassLoadingInformation();
445 ClassLoadingInformation::registerClassLoadingInformation();
446 }
447 return $this;
448 }
449
450 /**
451 * Activates a package during runtime. This is used in AdditionalConfiguration.php
452 * to enable extensions under conditions.
453 *
454 * @return Bootstrap
455 */
456 protected function initializeRuntimeActivatedPackagesFromConfiguration()
457 {
458 if (!empty($GLOBALS['TYPO3_CONF_VARS']['EXT']['runtimeActivatedPackages']) && is_array($GLOBALS['TYPO3_CONF_VARS']['EXT']['runtimeActivatedPackages'])) {
459 /** @var \TYPO3\CMS\Core\Package\PackageManager $packageManager */
460 $packageManager = $this->getEarlyInstance(\TYPO3\CMS\Core\Package\PackageManager::class);
461 foreach ($GLOBALS['TYPO3_CONF_VARS']['EXT']['runtimeActivatedPackages'] as $runtimeAddedPackageKey) {
462 $packageManager->activatePackageDuringRuntime($runtimeAddedPackageKey);
463 }
464 }
465 return $this;
466 }
467
468 /**
469 * Load ext_localconf of extensions
470 *
471 * @param bool $allowCaching
472 * @return Bootstrap
473 * @internal This is not a public API method, do not use in own extensions
474 */
475 public function loadTypo3LoadedExtAndExtLocalconf($allowCaching = true)
476 {
477 ExtensionManagementUtility::loadExtLocalconf($allowCaching);
478 return $this;
479 }
480
481 /**
482 * We need an early instance of the configuration manager.
483 * Since makeInstance relies on the object configuration, we create it here with new instead.
484 *
485 * @return Bootstrap
486 * @internal This is not a public API method, do not use in own extensions
487 */
488 public function populateLocalConfiguration()
489 {
490 try {
491 $configurationManager = $this->getEarlyInstance(\TYPO3\CMS\Core\Configuration\ConfigurationManager::class);
492 } catch (\TYPO3\CMS\Core\Exception $exception) {
493 $configurationManager = new \TYPO3\CMS\Core\Configuration\ConfigurationManager();
494 $this->setEarlyInstance(\TYPO3\CMS\Core\Configuration\ConfigurationManager::class, $configurationManager);
495 }
496 $configurationManager->exportConfiguration();
497 return $this;
498 }
499
500 /**
501 * Set cache_core to null backend, effectively disabling eg. the cache for ext_localconf and PackageManager etc.
502 *
503 * @return \TYPO3\CMS\Core\Core\Bootstrap
504 * @internal This is not a public API method, do not use in own extensions
505 */
506 public function disableCoreCache()
507 {
508 $GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['cache_core']['backend']
509 = \TYPO3\CMS\Core\Cache\Backend\NullBackend::class;
510 unset($GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['cache_core']['options']);
511 return $this;
512 }
513
514 /**
515 * Define user agent constant
516 *
517 * @return \TYPO3\CMS\Core\Core\Bootstrap
518 */
519 protected function defineUserAgentConstant()
520 {
521 define('TYPO3_user_agent', 'User-Agent: ' . $GLOBALS['TYPO3_CONF_VARS']['HTTP']['headers']['User-Agent']);
522 return $this;
523 }
524
525 /**
526 * Initialize caching framework, and re-initializes it (e.g. in the install tool) by recreating the instances
527 * again despite the Singleton instance
528 *
529 * @return Bootstrap
530 * @internal This is not a public API method, do not use in own extensions
531 */
532 public function initializeCachingFramework()
533 {
534 $cacheManager = new \TYPO3\CMS\Core\Cache\CacheManager();
535 $cacheManager->setCacheConfigurations($GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']);
536 GeneralUtility::setSingletonInstance(\TYPO3\CMS\Core\Cache\CacheManager::class, $cacheManager);
537 $this->setEarlyInstance(\TYPO3\CMS\Core\Cache\CacheManager::class, $cacheManager);
538 return $this;
539 }
540
541 /**
542 * Set cacheHash options
543 *
544 * @return Bootstrap
545 */
546 protected function setCacheHashOptions()
547 {
548 $GLOBALS['TYPO3_CONF_VARS']['FE']['cacheHash'] = [
549 'cachedParametersWhiteList' => GeneralUtility::trimExplode(',', $GLOBALS['TYPO3_CONF_VARS']['FE']['cHashOnlyForParameters'], true),
550 'excludedParameters' => GeneralUtility::trimExplode(',', $GLOBALS['TYPO3_CONF_VARS']['FE']['cHashExcludedParameters'], true),
551 'requireCacheHashPresenceParameters' => GeneralUtility::trimExplode(',', $GLOBALS['TYPO3_CONF_VARS']['FE']['cHashRequiredParameters'], true),
552 ];
553 if (trim($GLOBALS['TYPO3_CONF_VARS']['FE']['cHashExcludedParametersIfEmpty']) === '*') {
554 $GLOBALS['TYPO3_CONF_VARS']['FE']['cacheHash']['excludeAllEmptyParameters'] = true;
555 } else {
556 $GLOBALS['TYPO3_CONF_VARS']['FE']['cacheHash']['excludedParametersIfEmpty'] = GeneralUtility::trimExplode(',', $GLOBALS['TYPO3_CONF_VARS']['FE']['cHashExcludedParametersIfEmpty'], true);
557 }
558 return $this;
559 }
560
561 /**
562 * Set default timezone
563 *
564 * @return Bootstrap
565 */
566 protected function setDefaultTimezone()
567 {
568 $timeZone = $GLOBALS['TYPO3_CONF_VARS']['SYS']['phpTimeZone'];
569 if (empty($timeZone)) {
570 // Time zone from the server environment (TZ env or OS query)
571 $defaultTimeZone = @date_default_timezone_get();
572 if ($defaultTimeZone !== '') {
573 $timeZone = $defaultTimeZone;
574 } else {
575 $timeZone = 'UTC';
576 }
577 }
578 // Set default to avoid E_WARNINGs with PHP > 5.3
579 date_default_timezone_set($timeZone);
580 return $this;
581 }
582
583 /**
584 * Initialize the locales handled by TYPO3
585 *
586 * @return Bootstrap
587 */
588 protected function initializeL10nLocales()
589 {
590 \TYPO3\CMS\Core\Localization\Locales::initialize();
591 return $this;
592 }
593
594 /**
595 * Configure and set up exception and error handling
596 *
597 * @return Bootstrap
598 * @throws \RuntimeException
599 */
600 protected function initializeErrorHandling()
601 {
602 $productionExceptionHandlerClassName = $GLOBALS['TYPO3_CONF_VARS']['SYS']['productionExceptionHandler'];
603 $debugExceptionHandlerClassName = $GLOBALS['TYPO3_CONF_VARS']['SYS']['debugExceptionHandler'];
604
605 $errorHandlerClassName = $GLOBALS['TYPO3_CONF_VARS']['SYS']['errorHandler'];
606 $errorHandlerErrors = $GLOBALS['TYPO3_CONF_VARS']['SYS']['errorHandlerErrors'];
607 $exceptionalErrors = $GLOBALS['TYPO3_CONF_VARS']['SYS']['exceptionalErrors'];
608
609 $displayErrorsSetting = (int)$GLOBALS['TYPO3_CONF_VARS']['SYS']['displayErrors'];
610 switch ($displayErrorsSetting) {
611 case -1:
612 $ipMatchesDevelopmentSystem = GeneralUtility::cmpIP(GeneralUtility::getIndpEnv('REMOTE_ADDR'), $GLOBALS['TYPO3_CONF_VARS']['SYS']['devIPmask']);
613 $exceptionHandlerClassName = $ipMatchesDevelopmentSystem ? $debugExceptionHandlerClassName : $productionExceptionHandlerClassName;
614 $displayErrors = $ipMatchesDevelopmentSystem ? 1 : 0;
615 $exceptionalErrors = $ipMatchesDevelopmentSystem ? $exceptionalErrors : 0;
616 break;
617 case 0:
618 $exceptionHandlerClassName = $productionExceptionHandlerClassName;
619 $displayErrors = 0;
620 break;
621 case 1:
622 $exceptionHandlerClassName = $debugExceptionHandlerClassName;
623 $displayErrors = 1;
624 break;
625 default:
626 // Throw exception if an invalid option is set.
627 throw new \RuntimeException(
628 'The option $TYPO3_CONF_VARS[SYS][displayErrors] is not set to "-1", "0" or "1".',
629 1476046290
630 );
631 }
632 @ini_set('display_errors', $displayErrors);
633
634 if (!empty($errorHandlerClassName)) {
635 // Register an error handler for the given errorHandlerError
636 $errorHandler = GeneralUtility::makeInstance($errorHandlerClassName, $errorHandlerErrors);
637 $errorHandler->setExceptionalErrors($exceptionalErrors);
638 if (is_callable([$errorHandler, 'setDebugMode'])) {
639 $errorHandler->setDebugMode($displayErrors === 1);
640 }
641 }
642 if (!empty($exceptionHandlerClassName)) {
643 // Registering the exception handler is done in the constructor
644 GeneralUtility::makeInstance($exceptionHandlerClassName);
645 }
646 return $this;
647 }
648
649 /**
650 * Set PHP memory limit depending on value of
651 * $GLOBALS['TYPO3_CONF_VARS']['SYS']['setMemoryLimit']
652 *
653 * @return Bootstrap
654 */
655 protected function setMemoryLimit()
656 {
657 if ((int)$GLOBALS['TYPO3_CONF_VARS']['SYS']['setMemoryLimit'] > 16) {
658 @ini_set('memory_limit', ((int)$GLOBALS['TYPO3_CONF_VARS']['SYS']['setMemoryLimit'] . 'm'));
659 }
660 return $this;
661 }
662
663 /**
664 * Define TYPO3_REQUESTTYPE* constants that can be used for developers to see if any context has been hit
665 * also see setRequestType(). Is done at the very beginning so these parameters are always available.
666 *
667 * @return Bootstrap
668 */
669 protected function defineTypo3RequestTypes()
670 {
671 define('TYPO3_REQUESTTYPE_FE', 1);
672 define('TYPO3_REQUESTTYPE_BE', 2);
673 define('TYPO3_REQUESTTYPE_CLI', 4);
674 define('TYPO3_REQUESTTYPE_AJAX', 8);
675 define('TYPO3_REQUESTTYPE_INSTALL', 16);
676 }
677
678 /**
679 * Defines the TYPO3_REQUESTTYPE constant so the environment knows which context the request is running.
680 *
681 * @throws \RuntimeException if the method was already called during a request
682 * @return Bootstrap
683 */
684 public function setRequestType($requestType)
685 {
686 if (defined('TYPO3_REQUESTTYPE')) {
687 throw new \RuntimeException('TYPO3_REQUESTTYPE has already been set, cannot be called multiple times', 1450561878);
688 }
689 define('TYPO3_REQUESTTYPE', $requestType);
690 return $this;
691 }
692
693 /**
694 * Extensions may register new caches, so we set the
695 * global cache array to the manager again at this point
696 *
697 * @return Bootstrap
698 * @internal This is not a public API method, do not use in own extensions
699 */
700 public function setFinalCachingFrameworkCacheConfiguration()
701 {
702 $this->getEarlyInstance(\TYPO3\CMS\Core\Cache\CacheManager::class)->setCacheConfigurations($GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']);
703 return $this;
704 }
705
706 /**
707 * Define logging and exception constants
708 *
709 * @return Bootstrap
710 * @internal This is not a public API method, do not use in own extensions
711 */
712 public function defineLoggingAndExceptionConstants()
713 {
714 define('TYPO3_DLOG', $GLOBALS['TYPO3_CONF_VARS']['SYS']['enable_DLOG']);
715 define('TYPO3_ERROR_DLOG', $GLOBALS['TYPO3_CONF_VARS']['SYS']['enable_errorDLOG']);
716 define('TYPO3_EXCEPTION_DLOG', $GLOBALS['TYPO3_CONF_VARS']['SYS']['enable_exceptionDLOG']);
717 return $this;
718 }
719
720 /**
721 * Unsetting reserved global variables:
722 * Those are set in "ext:core/ext_tables.php" file:
723 *
724 * @return Bootstrap
725 * @internal This is not a public API method, do not use in own extensions
726 */
727 public function unsetReservedGlobalVariables()
728 {
729 unset($GLOBALS['PAGES_TYPES']);
730 unset($GLOBALS['TCA']);
731 unset($GLOBALS['TBE_MODULES']);
732 unset($GLOBALS['TBE_STYLES']);
733 unset($GLOBALS['BE_USER']);
734 // Those set otherwise:
735 unset($GLOBALS['TBE_MODULES_EXT']);
736 unset($GLOBALS['TCA_DESCR']);
737 unset($GLOBALS['LOCAL_LANG']);
738 return $this;
739 }
740
741 /**
742 * Check adminOnly configuration variable and redirects
743 * to an URL in file typo3conf/LOCK_BACKEND or exit the script
744 *
745 * @throws \RuntimeException
746 * @param bool $forceProceeding if this option is set, the bootstrap will proceed even if the user is logged in (usually only needed for special AJAX cases, see AjaxRequestHandler)
747 * @return Bootstrap
748 * @internal This is not a public API method, do not use in own extensions
749 */
750 public function checkLockedBackendAndRedirectOrDie($forceProceeding = false)
751 {
752 if ($GLOBALS['TYPO3_CONF_VARS']['BE']['adminOnly'] < 0) {
753 throw new \RuntimeException('TYPO3 Backend locked: Backend and Install Tool are locked for maintenance. [BE][adminOnly] is set to "' . (int)$GLOBALS['TYPO3_CONF_VARS']['BE']['adminOnly'] . '".', 1294586847);
754 }
755 if (@is_file(PATH_typo3conf . 'LOCK_BACKEND') && $forceProceeding === false) {
756 $fileContent = file_get_contents(PATH_typo3conf . 'LOCK_BACKEND');
757 if ($fileContent) {
758 header('Location: ' . $fileContent);
759 } else {
760 throw new \RuntimeException('TYPO3 Backend locked: Browser backend is locked for maintenance. Remove lock by removing the file "typo3conf/LOCK_BACKEND" or use CLI-scripts.', 1294586848);
761 }
762 die;
763 }
764 return $this;
765 }
766
767 /**
768 * Compare client IP with IPmaskList and exit the script run
769 * if the client is not allowed to access the backend
770 *
771 * @return Bootstrap
772 * @internal This is not a public API method, do not use in own extensions
773 * @throws \RuntimeException
774 */
775 public function checkBackendIpOrDie()
776 {
777 if (trim($GLOBALS['TYPO3_CONF_VARS']['BE']['IPmaskList'])) {
778 if (!GeneralUtility::cmpIP(GeneralUtility::getIndpEnv('REMOTE_ADDR'), $GLOBALS['TYPO3_CONF_VARS']['BE']['IPmaskList'])) {
779 throw new \RuntimeException('TYPO3 Backend access denied: The IP address of your client does not match the list of allowed IP addresses.', 1389265900);
780 }
781 }
782 return $this;
783 }
784
785 /**
786 * Check lockSSL configuration variable and redirect
787 * to https version of the backend if needed
788 *
789 * @return Bootstrap
790 * @internal This is not a public API method, do not use in own extensions
791 * @throws \RuntimeException
792 */
793 public function checkSslBackendAndRedirectIfNeeded()
794 {
795 if ((bool)$GLOBALS['TYPO3_CONF_VARS']['BE']['lockSSL'] && !GeneralUtility::getIndpEnv('TYPO3_SSL')) {
796 if ((int)$GLOBALS['TYPO3_CONF_VARS']['BE']['lockSSLPort']) {
797 $sslPortSuffix = ':' . (int)$GLOBALS['TYPO3_CONF_VARS']['BE']['lockSSLPort'];
798 } else {
799 $sslPortSuffix = '';
800 }
801 list(, $url) = explode('://', GeneralUtility::getIndpEnv('TYPO3_SITE_URL') . TYPO3_mainDir, 2);
802 list($server, $address) = explode('/', $url, 2);
803 header('Location: https://' . $server . $sslPortSuffix . '/' . $address);
804 die;
805 }
806 return $this;
807 }
808
809 /**
810 * Load $TCA
811 *
812 * This will mainly set up $TCA through extMgm API.
813 *
814 * @param bool $allowCaching True, if loading TCA from cache is allowed
815 * @return Bootstrap
816 * @internal This is not a public API method, do not use in own extensions
817 */
818 public function loadBaseTca(bool $allowCaching = true): Bootstrap
819 {
820 ExtensionManagementUtility::loadBaseTca($allowCaching);
821 return $this;
822 }
823
824 /**
825 * Load ext_tables and friends.
826 *
827 * This will mainly load and execute ext_tables.php files of loaded extensions
828 * or the according cache file if exists.
829 *
830 * @param bool $allowCaching True, if reading compiled ext_tables file from cache is allowed
831 * @return Bootstrap
832 * @internal This is not a public API method, do not use in own extensions
833 */
834 public function loadExtTables(bool $allowCaching = true): Bootstrap
835 {
836 ExtensionManagementUtility::loadExtTables($allowCaching);
837 $this->runExtTablesPostProcessingHooks();
838 return $this;
839 }
840
841 /**
842 * Check for registered ext tables hooks and run them
843 *
844 * @throws \UnexpectedValueException
845 */
846 protected function runExtTablesPostProcessingHooks()
847 {
848 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['GLOBAL']['extTablesInclusion-PostProcessing'])) {
849 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['GLOBAL']['extTablesInclusion-PostProcessing'] as $className) {
850 /** @var $hookObject \TYPO3\CMS\Core\Database\TableConfigurationPostProcessingHookInterface */
851 $hookObject = GeneralUtility::makeInstance($className);
852 if (!$hookObject instanceof \TYPO3\CMS\Core\Database\TableConfigurationPostProcessingHookInterface) {
853 throw new \UnexpectedValueException(
854 '$hookObject "' . $className . '" must implement interface TYPO3\\CMS\\Core\\Database\\TableConfigurationPostProcessingHookInterface',
855 1320585902
856 );
857 }
858 $hookObject->processData();
859 }
860 }
861 }
862
863 /**
864 * Initialize the Routing for the TYPO3 Backend
865 * Loads all routes registered inside all packages and stores them inside the Router
866 *
867 * @return Bootstrap
868 * @internal This is not a public API method, do not use in own extensions
869 */
870 public function initializeBackendRouter()
871 {
872 // See if the Routes.php from all active packages have been built together already
873 $cacheIdentifier = 'BackendRoutesFromPackages_' . sha1((TYPO3_version . PATH_site . 'BackendRoutesFromPackages'));
874
875 /** @var $codeCache \TYPO3\CMS\Core\Cache\Frontend\PhpFrontend */
876 $codeCache = $this->getEarlyInstance(\TYPO3\CMS\Core\Cache\CacheManager::class)->getCache('cache_core');
877 $routesFromPackages = [];
878 if ($codeCache->has($cacheIdentifier)) {
879 // substr is necessary, because the php frontend wraps php code around the cache value
880 $routesFromPackages = unserialize(substr($codeCache->get($cacheIdentifier), 6, -2));
881 } else {
882 // Loop over all packages and check for a Configuration/Backend/Routes.php file
883 $packageManager = $this->getEarlyInstance(\TYPO3\CMS\Core\Package\PackageManager::class);
884 $packages = $packageManager->getActivePackages();
885 foreach ($packages as $package) {
886 $routesFileNameForPackage = $package->getPackagePath() . 'Configuration/Backend/Routes.php';
887 if (file_exists($routesFileNameForPackage)) {
888 $definedRoutesInPackage = require $routesFileNameForPackage;
889 if (is_array($definedRoutesInPackage)) {
890 $routesFromPackages = array_merge($routesFromPackages, $definedRoutesInPackage);
891 }
892 }
893 $routesFileNameForPackage = $package->getPackagePath() . 'Configuration/Backend/AjaxRoutes.php';
894 if (file_exists($routesFileNameForPackage)) {
895 $definedRoutesInPackage = require $routesFileNameForPackage;
896 if (is_array($definedRoutesInPackage)) {
897 foreach ($definedRoutesInPackage as $routeIdentifier => $routeOptions) {
898 // prefix the route with "ajax_" as "namespace"
899 $routeOptions['path'] = '/ajax' . $routeOptions['path'];
900 $routesFromPackages['ajax_' . $routeIdentifier] = $routeOptions;
901 $routesFromPackages['ajax_' . $routeIdentifier]['ajax'] = true;
902 }
903 }
904 }
905 }
906 // Store the data from all packages in the cache
907 $codeCache->set($cacheIdentifier, serialize($routesFromPackages));
908 }
909
910 // Build Route objects from the data
911 $router = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\Router::class);
912 foreach ($routesFromPackages as $name => $options) {
913 $path = $options['path'];
914 unset($options['path']);
915 $route = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\Route::class, $path, $options);
916 $router->addRoute($name, $route);
917 }
918 return $this;
919 }
920
921 /**
922 * Initialize backend user object in globals
923 *
924 * @param string $className usually \TYPO3\CMS\Core\Authentication\BackendUserAuthentication::class but can be used for CLI
925 * @return Bootstrap
926 * @internal This is not a public API method, do not use in own extensions
927 */
928 public function initializeBackendUser($className = \TYPO3\CMS\Core\Authentication\BackendUserAuthentication::class)
929 {
930 /** @var $backendUser \TYPO3\CMS\Core\Authentication\BackendUserAuthentication */
931 $backendUser = GeneralUtility::makeInstance($className);
932 // The global must be available very early, because methods below
933 // might trigger code which relies on it. See: #45625
934 $GLOBALS['BE_USER'] = $backendUser;
935 $backendUser->start();
936 return $this;
937 }
938
939 /**
940 * Initializes and ensures authenticated access
941 *
942 * @internal This is not a public API method, do not use in own extensions
943 * @param bool $proceedIfNoUserIsLoggedIn if set to TRUE, no forced redirect to the login page will be done
944 * @return \TYPO3\CMS\Core\Core\Bootstrap
945 */
946 public function initializeBackendAuthentication($proceedIfNoUserIsLoggedIn = false)
947 {
948 $GLOBALS['BE_USER']->backendCheckLogin($proceedIfNoUserIsLoggedIn);
949 return $this;
950 }
951
952 /**
953 * Initialize language object
954 *
955 * @return Bootstrap
956 * @internal This is not a public API method, do not use in own extensions
957 */
958 public function initializeLanguageObject()
959 {
960 /** @var $GLOBALS['LANG'] \TYPO3\CMS\Core\Localization\LanguageService */
961 $GLOBALS['LANG'] = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Localization\LanguageService::class);
962 $GLOBALS['LANG']->init($GLOBALS['BE_USER']->uc['lang']);
963 return $this;
964 }
965
966 /**
967 * Throw away all output that may have happened during bootstrapping by weird extensions
968 *
969 * @return Bootstrap
970 * @internal This is not a public API method, do not use in own extensions
971 */
972 public function endOutputBufferingAndCleanPreviousOutput()
973 {
974 ob_clean();
975 return $this;
976 }
977
978 /**
979 * Initialize output compression if configured
980 *
981 * @return Bootstrap
982 * @internal This is not a public API method, do not use in own extensions
983 */
984 public function initializeOutputCompression()
985 {
986 if (extension_loaded('zlib') && $GLOBALS['TYPO3_CONF_VARS']['BE']['compressionLevel']) {
987 if (MathUtility::canBeInterpretedAsInteger($GLOBALS['TYPO3_CONF_VARS']['BE']['compressionLevel'])) {
988 @ini_set('zlib.output_compression_level', $GLOBALS['TYPO3_CONF_VARS']['BE']['compressionLevel']);
989 }
990 ob_start('ob_gzhandler');
991 }
992 return $this;
993 }
994
995 /**
996 * Send HTTP headers if configured
997 *
998 * @return Bootstrap
999 * @internal This is not a public API method, do not use in own extensions
1000 */
1001 public function sendHttpHeaders()
1002 {
1003 if (!empty($GLOBALS['TYPO3_CONF_VARS']['BE']['HTTP']['Response']['Headers']) && is_array($GLOBALS['TYPO3_CONF_VARS']['BE']['HTTP']['Response']['Headers'])) {
1004 foreach ($GLOBALS['TYPO3_CONF_VARS']['BE']['HTTP']['Response']['Headers'] as $header) {
1005 header($header);
1006 }
1007 }
1008 return $this;
1009 }
1010
1011 /**
1012 * Things that should be performed to shut down the framework.
1013 * This method is called in all important scripts for a clean
1014 * shut down of the system.
1015 *
1016 * @return Bootstrap
1017 * @internal This is not a public API method, do not use in own extensions
1018 */
1019 public function shutdown()
1020 {
1021 $this->sendResponse();
1022 return $this;
1023 }
1024
1025 /**
1026 * Provides an instance of "template" for backend-modules to
1027 * work with.
1028 *
1029 * @return Bootstrap
1030 * @internal This is not a public API method, do not use in own extensions
1031 */
1032 public function initializeBackendTemplate()
1033 {
1034 $GLOBALS['TBE_TEMPLATE'] = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Template\DocumentTemplate::class);
1035 return $this;
1036 }
1037 }