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