[CLEANUP] Remove/Replace non-standard phpdoc annotations
[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('cli');
240 AnnotationReader::addGlobalIgnoredName('flushesCashes');
241 AnnotationReader::addGlobalIgnoredName('uuid');
242 AnnotationReader::addGlobalIgnoredName('identity');
243
244 // Annotations that control the extension scanner
245 AnnotationReader::addGlobalIgnoredName('extensionScannerIgnoreFile');
246 AnnotationReader::addGlobalIgnoredName('extensionScannerIgnoreLine');
247
248 return $this;
249 }
250
251 /**
252 * checks if LocalConfiguration.php or PackageStates.php is missing,
253 * used to see if a redirect to the install tool is needed
254 *
255 * @return bool TRUE when the essential configuration is available, otherwise FALSE
256 * @internal This is not a public API method, do not use in own extensions
257 */
258 public function checkIfEssentialConfigurationExists()
259 {
260 $configurationManager = new \TYPO3\CMS\Core\Configuration\ConfigurationManager;
261 $this->setEarlyInstance(\TYPO3\CMS\Core\Configuration\ConfigurationManager::class, $configurationManager);
262 return file_exists($configurationManager->getLocalConfigurationFileLocation()) && file_exists(PATH_typo3conf . 'PackageStates.php');
263 }
264
265 /**
266 * Redirect to install tool if LocalConfiguration.php is missing.
267 *
268 * @param int $entryPointLevel Number of subdirectories where the entry script is located under the document root
269 * @internal This is not a public API method, do not use in own extensions
270 */
271 public function redirectToInstallTool($entryPointLevel = 0)
272 {
273 $path = TYPO3_mainDir . 'install.php';
274 if ($entryPointLevel > 0) {
275 $path = str_repeat('../', $entryPointLevel) . $path;
276 }
277 header('Location: ' . $path);
278 die;
279 }
280
281 /**
282 * Adds available request handlers usually done via an application from the outside.
283 *
284 * @param string $requestHandler class which implements the request handler interface
285 * @return Bootstrap
286 * @internal This is not a public API method, do not use in own extensions
287 */
288 public function registerRequestHandlerImplementation($requestHandler)
289 {
290 $this->availableRequestHandlers[] = $requestHandler;
291 return $this;
292 }
293
294 /**
295 * Fetches the request handler that suits the best based on the priority and the interface
296 * Be sure to always have the constants that are defined in $this->defineTypo3RequestTypes() are set,
297 * so most RequestHandlers can check if they can handle the request.
298 *
299 * @param \Psr\Http\Message\ServerRequestInterface|\Symfony\Component\Console\Input\InputInterface $request
300 * @return \TYPO3\CMS\Core\Http\RequestHandlerInterface|\TYPO3\CMS\Core\Console\RequestHandlerInterface
301 * @throws \TYPO3\CMS\Core\Exception
302 * @internal This is not a public API method, do not use in own extensions
303 */
304 protected function resolveRequestHandler($request)
305 {
306 $suitableRequestHandlers = [];
307 foreach ($this->availableRequestHandlers as $requestHandlerClassName) {
308 /** @var \TYPO3\CMS\Core\Http\RequestHandlerInterface|\TYPO3\CMS\Core\Console\RequestHandlerInterface $requestHandler */
309 $requestHandler = GeneralUtility::makeInstance($requestHandlerClassName, $this);
310 if ($requestHandler->canHandleRequest($request)) {
311 $priority = $requestHandler->getPriority();
312 if (isset($suitableRequestHandlers[$priority])) {
313 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);
314 }
315 $suitableRequestHandlers[$priority] = $requestHandler;
316 }
317 }
318 if (empty($suitableRequestHandlers)) {
319 throw new \TYPO3\CMS\Core\Exception('No suitable request handler found.', 1225418233);
320 }
321 ksort($suitableRequestHandlers);
322 return array_pop($suitableRequestHandlers);
323 }
324
325 /**
326 * Builds a Request instance from the current process, and then resolves the request
327 * through the request handlers depending on Frontend, Backend, CLI etc.
328 *
329 * @param \Psr\Http\Message\RequestInterface|\Symfony\Component\Console\Input\InputInterface $request
330 * @return Bootstrap
331 * @throws \TYPO3\CMS\Core\Exception
332 * @internal This is not a public API method, do not use in own extensions
333 */
334 public function handleRequest($request)
335 {
336 // Resolve request handler that were registered based on the Application
337 $requestHandler = $this->resolveRequestHandler($request);
338
339 // Execute the command which returns a Response object or NULL
340 $this->response = $requestHandler->handleRequest($request);
341 return $this;
342 }
343
344 /**
345 * Outputs content if there is a proper Response object.
346 *
347 * @return Bootstrap
348 */
349 protected function sendResponse()
350 {
351 if ($this->response instanceof \Psr\Http\Message\ResponseInterface) {
352 if (!headers_sent()) {
353 // If the response code was not changed by legacy code (still is 200)
354 // then allow the PSR-7 response object to explicitly set it.
355 // Otherwise let legacy code take precedence.
356 // This code path can be deprecated once we expose the response object to third party code
357 if (http_response_code() === 200) {
358 header('HTTP/' . $this->response->getProtocolVersion() . ' ' . $this->response->getStatusCode() . ' ' . $this->response->getReasonPhrase());
359 }
360
361 foreach ($this->response->getHeaders() as $name => $values) {
362 header($name . ': ' . implode(', ', $values));
363 }
364 }
365 echo $this->response->getBody()->__toString();
366 }
367 return $this;
368 }
369
370 /**
371 * Registers the instance of the specified object for an early boot stage.
372 * On finalizing the Object Manager initialization, all those instances will
373 * be transferred to the Object Manager's registry.
374 *
375 * @param string $objectName Object name, as later used by the Object Manager
376 * @param object $instance The instance to register
377 * @internal This is not a public API method, do not use in own extensions
378 */
379 public function setEarlyInstance($objectName, $instance)
380 {
381 $this->earlyInstances[$objectName] = $instance;
382 }
383
384 /**
385 * Returns an instance which was registered earlier through setEarlyInstance()
386 *
387 * @param string $objectName Object name of the registered instance
388 * @return object
389 * @throws \TYPO3\CMS\Core\Exception
390 * @internal This is not a public API method, do not use in own extensions
391 */
392 public function getEarlyInstance($objectName)
393 {
394 if (!isset($this->earlyInstances[$objectName])) {
395 throw new \TYPO3\CMS\Core\Exception('Unknown early instance "' . $objectName . '"', 1365167380);
396 }
397 return $this->earlyInstances[$objectName];
398 }
399
400 /**
401 * Returns all registered early instances indexed by object name
402 *
403 * @return array
404 * @internal This is not a public API method, do not use in own extensions
405 */
406 public function getEarlyInstances()
407 {
408 return $this->earlyInstances;
409 }
410
411 /**
412 * Includes LocalConfiguration.php and sets several
413 * global settings depending on configuration.
414 *
415 * @param bool $allowCaching Whether to allow caching - affects cache_core (autoloader)
416 * @param string $packageManagerClassName Define an alternative package manager implementation (usually for the installer)
417 * @return Bootstrap
418 * @internal This is not a public API method, do not use in own extensions
419 */
420 public function loadConfigurationAndInitialize($allowCaching = true, $packageManagerClassName = \TYPO3\CMS\Core\Package\PackageManager::class)
421 {
422 $this->populateLocalConfiguration()
423 ->initializeErrorHandling();
424 if (!$allowCaching) {
425 $this->disableCoreCache();
426 }
427 $this->initializeCachingFramework()
428 ->initializePackageManagement($packageManagerClassName)
429 ->initializeRuntimeActivatedPackagesFromConfiguration()
430 ->setDefaultTimezone()
431 ->initializeL10nLocales()
432 ->setMemoryLimit();
433 return $this;
434 }
435
436 /**
437 * Initializes the package system and loads the package configuration and settings
438 * provided by the packages.
439 *
440 * @param string $packageManagerClassName Define an alternative package manager implementation (usually for the installer)
441 * @return Bootstrap
442 * @internal This is not a public API method, do not use in own extensions
443 */
444 public function initializePackageManagement($packageManagerClassName)
445 {
446 /** @var \TYPO3\CMS\Core\Package\PackageManager $packageManager */
447 $packageManager = new $packageManagerClassName();
448 $this->setEarlyInstance(\TYPO3\CMS\Core\Package\PackageManager::class, $packageManager);
449 ExtensionManagementUtility::setPackageManager($packageManager);
450 $packageManager->injectCoreCache($this->getEarlyInstance(\TYPO3\CMS\Core\Cache\CacheManager::class)->getCache('cache_core'));
451 $dependencyResolver = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Package\DependencyResolver::class);
452 $dependencyResolver->injectDependencyOrderingService(GeneralUtility::makeInstance(\TYPO3\CMS\Core\Service\DependencyOrderingService::class));
453 $packageManager->injectDependencyResolver($dependencyResolver);
454 $packageManager->initialize();
455 GeneralUtility::setSingletonInstance(\TYPO3\CMS\Core\Package\PackageManager::class, $packageManager);
456 return $this;
457 }
458
459 /**
460 * Activates a package during runtime. This is used in AdditionalConfiguration.php
461 * to enable extensions under conditions.
462 *
463 * @return Bootstrap
464 */
465 protected function initializeRuntimeActivatedPackagesFromConfiguration()
466 {
467 if (!empty($GLOBALS['TYPO3_CONF_VARS']['EXT']['runtimeActivatedPackages']) && is_array($GLOBALS['TYPO3_CONF_VARS']['EXT']['runtimeActivatedPackages'])) {
468 /** @var \TYPO3\CMS\Core\Package\PackageManager $packageManager */
469 $packageManager = $this->getEarlyInstance(\TYPO3\CMS\Core\Package\PackageManager::class);
470 foreach ($GLOBALS['TYPO3_CONF_VARS']['EXT']['runtimeActivatedPackages'] as $runtimeAddedPackageKey) {
471 $packageManager->activatePackageDuringRuntime($runtimeAddedPackageKey);
472 }
473 }
474 return $this;
475 }
476
477 /**
478 * Load ext_localconf of extensions
479 *
480 * @param bool $allowCaching
481 * @return Bootstrap
482 * @internal This is not a public API method, do not use in own extensions
483 */
484 public function loadTypo3LoadedExtAndExtLocalconf($allowCaching = true)
485 {
486 ExtensionManagementUtility::loadExtLocalconf($allowCaching);
487 return $this;
488 }
489
490 /**
491 * We need an early instance of the configuration manager.
492 * Since makeInstance relies on the object configuration, we create it here with new instead.
493 *
494 * @return Bootstrap
495 * @internal This is not a public API method, do not use in own extensions
496 */
497 public function populateLocalConfiguration()
498 {
499 try {
500 $configurationManager = $this->getEarlyInstance(\TYPO3\CMS\Core\Configuration\ConfigurationManager::class);
501 } catch (\TYPO3\CMS\Core\Exception $exception) {
502 $configurationManager = new \TYPO3\CMS\Core\Configuration\ConfigurationManager();
503 $this->setEarlyInstance(\TYPO3\CMS\Core\Configuration\ConfigurationManager::class, $configurationManager);
504 }
505 $configurationManager->exportConfiguration();
506 return $this;
507 }
508
509 /**
510 * Set cache_core to null backend, effectively disabling eg. the cache for ext_localconf and PackageManager etc.
511 *
512 * @return \TYPO3\CMS\Core\Core\Bootstrap
513 * @internal This is not a public API method, do not use in own extensions
514 */
515 public function disableCoreCache()
516 {
517 $GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['cache_core']['backend']
518 = \TYPO3\CMS\Core\Cache\Backend\NullBackend::class;
519 unset($GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['cache_core']['options']);
520 return $this;
521 }
522
523 /**
524 * Initialize caching framework, and re-initializes it (e.g. in the install tool) by recreating the instances
525 * again despite the Singleton instance
526 *
527 * @return Bootstrap
528 * @internal This is not a public API method, do not use in own extensions
529 */
530 public function initializeCachingFramework()
531 {
532 $cacheManager = new \TYPO3\CMS\Core\Cache\CacheManager();
533 $cacheManager->setCacheConfigurations($GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']);
534 GeneralUtility::setSingletonInstance(\TYPO3\CMS\Core\Cache\CacheManager::class, $cacheManager);
535 $this->setEarlyInstance(\TYPO3\CMS\Core\Cache\CacheManager::class, $cacheManager);
536 return $this;
537 }
538
539 /**
540 * Set default timezone
541 *
542 * @return Bootstrap
543 */
544 protected function setDefaultTimezone()
545 {
546 $timeZone = $GLOBALS['TYPO3_CONF_VARS']['SYS']['phpTimeZone'];
547 if (empty($timeZone)) {
548 // Time zone from the server environment (TZ env or OS query)
549 $defaultTimeZone = @date_default_timezone_get();
550 if ($defaultTimeZone !== '') {
551 $timeZone = $defaultTimeZone;
552 } else {
553 $timeZone = 'UTC';
554 }
555 }
556 // Set default to avoid E_WARNINGs with PHP > 5.3
557 date_default_timezone_set($timeZone);
558 return $this;
559 }
560
561 /**
562 * Initialize the locales handled by TYPO3
563 *
564 * @return Bootstrap
565 */
566 protected function initializeL10nLocales()
567 {
568 \TYPO3\CMS\Core\Localization\Locales::initialize();
569 return $this;
570 }
571
572 /**
573 * Configure and set up exception and error handling
574 *
575 * @return Bootstrap
576 * @throws \RuntimeException
577 */
578 protected function initializeErrorHandling()
579 {
580 $productionExceptionHandlerClassName = $GLOBALS['TYPO3_CONF_VARS']['SYS']['productionExceptionHandler'];
581 $debugExceptionHandlerClassName = $GLOBALS['TYPO3_CONF_VARS']['SYS']['debugExceptionHandler'];
582
583 $errorHandlerClassName = $GLOBALS['TYPO3_CONF_VARS']['SYS']['errorHandler'];
584 $errorHandlerErrors = $GLOBALS['TYPO3_CONF_VARS']['SYS']['errorHandlerErrors'];
585 $exceptionalErrors = $GLOBALS['TYPO3_CONF_VARS']['SYS']['exceptionalErrors'];
586
587 $displayErrorsSetting = (int)$GLOBALS['TYPO3_CONF_VARS']['SYS']['displayErrors'];
588 switch ($displayErrorsSetting) {
589 case -1:
590 $ipMatchesDevelopmentSystem = GeneralUtility::cmpIP(GeneralUtility::getIndpEnv('REMOTE_ADDR'), $GLOBALS['TYPO3_CONF_VARS']['SYS']['devIPmask']);
591 $exceptionHandlerClassName = $ipMatchesDevelopmentSystem ? $debugExceptionHandlerClassName : $productionExceptionHandlerClassName;
592 $displayErrors = $ipMatchesDevelopmentSystem ? 1 : 0;
593 $exceptionalErrors = $ipMatchesDevelopmentSystem ? $exceptionalErrors : 0;
594 break;
595 case 0:
596 $exceptionHandlerClassName = $productionExceptionHandlerClassName;
597 $displayErrors = 0;
598 break;
599 case 1:
600 $exceptionHandlerClassName = $debugExceptionHandlerClassName;
601 $displayErrors = 1;
602 break;
603 default:
604 // Throw exception if an invalid option is set.
605 throw new \RuntimeException(
606 'The option $TYPO3_CONF_VARS[SYS][displayErrors] is not set to "-1", "0" or "1".',
607 1476046290
608 );
609 }
610 @ini_set('display_errors', $displayErrors);
611
612 if (!empty($errorHandlerClassName)) {
613 // Register an error handler for the given errorHandlerError
614 $errorHandler = GeneralUtility::makeInstance($errorHandlerClassName, $errorHandlerErrors);
615 $errorHandler->setExceptionalErrors($exceptionalErrors);
616 if (is_callable([$errorHandler, 'setDebugMode'])) {
617 $errorHandler->setDebugMode($displayErrors === 1);
618 }
619 }
620 if (!empty($exceptionHandlerClassName)) {
621 // Registering the exception handler is done in the constructor
622 GeneralUtility::makeInstance($exceptionHandlerClassName);
623 }
624 return $this;
625 }
626
627 /**
628 * Set PHP memory limit depending on value of
629 * $GLOBALS['TYPO3_CONF_VARS']['SYS']['setMemoryLimit']
630 *
631 * @return Bootstrap
632 */
633 protected function setMemoryLimit()
634 {
635 if ((int)$GLOBALS['TYPO3_CONF_VARS']['SYS']['setMemoryLimit'] > 16) {
636 @ini_set('memory_limit', ((int)$GLOBALS['TYPO3_CONF_VARS']['SYS']['setMemoryLimit'] . 'm'));
637 }
638 return $this;
639 }
640
641 /**
642 * Define TYPO3_REQUESTTYPE* constants that can be used for developers to see if any context has been hit
643 * also see setRequestType(). Is done at the very beginning so these parameters are always available.
644 *
645 * @return Bootstrap
646 */
647 protected function defineTypo3RequestTypes()
648 {
649 define('TYPO3_REQUESTTYPE_FE', 1);
650 define('TYPO3_REQUESTTYPE_BE', 2);
651 define('TYPO3_REQUESTTYPE_CLI', 4);
652 define('TYPO3_REQUESTTYPE_AJAX', 8);
653 define('TYPO3_REQUESTTYPE_INSTALL', 16);
654 }
655
656 /**
657 * Defines the TYPO3_REQUESTTYPE constant so the environment knows which context the request is running.
658 *
659 * @throws \RuntimeException if the method was already called during a request
660 * @return Bootstrap
661 */
662 public function setRequestType($requestType)
663 {
664 if (defined('TYPO3_REQUESTTYPE')) {
665 throw new \RuntimeException('TYPO3_REQUESTTYPE has already been set, cannot be called multiple times', 1450561878);
666 }
667 define('TYPO3_REQUESTTYPE', $requestType);
668 return $this;
669 }
670
671 /**
672 * Extensions may register new caches, so we set the
673 * global cache array to the manager again at this point
674 *
675 * @return Bootstrap
676 * @internal This is not a public API method, do not use in own extensions
677 */
678 public function setFinalCachingFrameworkCacheConfiguration()
679 {
680 $this->getEarlyInstance(\TYPO3\CMS\Core\Cache\CacheManager::class)->setCacheConfigurations($GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']);
681 return $this;
682 }
683
684 /**
685 * Unsetting reserved global variables:
686 * Those are set in "ext:core/ext_tables.php" file:
687 *
688 * @return Bootstrap
689 * @internal This is not a public API method, do not use in own extensions
690 */
691 public function unsetReservedGlobalVariables()
692 {
693 unset($GLOBALS['PAGES_TYPES']);
694 unset($GLOBALS['TCA']);
695 unset($GLOBALS['TBE_MODULES']);
696 unset($GLOBALS['TBE_STYLES']);
697 unset($GLOBALS['BE_USER']);
698 // Those set otherwise:
699 unset($GLOBALS['TBE_MODULES_EXT']);
700 unset($GLOBALS['TCA_DESCR']);
701 unset($GLOBALS['LOCAL_LANG']);
702 return $this;
703 }
704
705 /**
706 * Check adminOnly configuration variable and redirects
707 * to an URL in file typo3conf/LOCK_BACKEND or exit the script
708 *
709 * @throws \RuntimeException
710 * @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)
711 * @return Bootstrap
712 * @internal This is not a public API method, do not use in own extensions
713 */
714 public function checkLockedBackendAndRedirectOrDie($forceProceeding = false)
715 {
716 if ($GLOBALS['TYPO3_CONF_VARS']['BE']['adminOnly'] < 0) {
717 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);
718 }
719 if (@is_file(PATH_typo3conf . 'LOCK_BACKEND') && $forceProceeding === false) {
720 $fileContent = file_get_contents(PATH_typo3conf . 'LOCK_BACKEND');
721 if ($fileContent) {
722 header('Location: ' . $fileContent);
723 } else {
724 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);
725 }
726 die;
727 }
728 return $this;
729 }
730
731 /**
732 * Compare client IP with IPmaskList and exit the script run
733 * if the client is not allowed to access the backend
734 *
735 * @return Bootstrap
736 * @internal This is not a public API method, do not use in own extensions
737 * @throws \RuntimeException
738 */
739 public function checkBackendIpOrDie()
740 {
741 if (trim($GLOBALS['TYPO3_CONF_VARS']['BE']['IPmaskList'])) {
742 if (!GeneralUtility::cmpIP(GeneralUtility::getIndpEnv('REMOTE_ADDR'), $GLOBALS['TYPO3_CONF_VARS']['BE']['IPmaskList'])) {
743 throw new \RuntimeException('TYPO3 Backend access denied: The IP address of your client does not match the list of allowed IP addresses.', 1389265900);
744 }
745 }
746 return $this;
747 }
748
749 /**
750 * Check lockSSL configuration variable and redirect
751 * to https version of the backend if needed
752 *
753 * @return Bootstrap
754 * @internal This is not a public API method, do not use in own extensions
755 * @throws \RuntimeException
756 */
757 public function checkSslBackendAndRedirectIfNeeded()
758 {
759 if ((bool)$GLOBALS['TYPO3_CONF_VARS']['BE']['lockSSL'] && !GeneralUtility::getIndpEnv('TYPO3_SSL')) {
760 if ((int)$GLOBALS['TYPO3_CONF_VARS']['BE']['lockSSLPort']) {
761 $sslPortSuffix = ':' . (int)$GLOBALS['TYPO3_CONF_VARS']['BE']['lockSSLPort'];
762 } else {
763 $sslPortSuffix = '';
764 }
765 list(, $url) = explode('://', GeneralUtility::getIndpEnv('TYPO3_SITE_URL') . TYPO3_mainDir, 2);
766 list($server, $address) = explode('/', $url, 2);
767 header('Location: https://' . $server . $sslPortSuffix . '/' . $address);
768 die;
769 }
770 return $this;
771 }
772
773 /**
774 * Load $TCA
775 *
776 * This will mainly set up $TCA through extMgm API.
777 *
778 * @param bool $allowCaching True, if loading TCA from cache is allowed
779 * @return Bootstrap
780 * @internal This is not a public API method, do not use in own extensions
781 */
782 public function loadBaseTca(bool $allowCaching = true): Bootstrap
783 {
784 ExtensionManagementUtility::loadBaseTca($allowCaching);
785 return $this;
786 }
787
788 /**
789 * Load ext_tables and friends.
790 *
791 * This will mainly load and execute ext_tables.php files of loaded extensions
792 * or the according cache file if exists.
793 *
794 * @param bool $allowCaching True, if reading compiled ext_tables file from cache is allowed
795 * @return Bootstrap
796 * @internal This is not a public API method, do not use in own extensions
797 */
798 public function loadExtTables(bool $allowCaching = true): Bootstrap
799 {
800 ExtensionManagementUtility::loadExtTables($allowCaching);
801 $this->runExtTablesPostProcessingHooks();
802 return $this;
803 }
804
805 /**
806 * Check for registered ext tables hooks and run them
807 *
808 * @throws \UnexpectedValueException
809 */
810 protected function runExtTablesPostProcessingHooks()
811 {
812 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['GLOBAL']['extTablesInclusion-PostProcessing'])) {
813 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['GLOBAL']['extTablesInclusion-PostProcessing'] as $className) {
814 /** @var $hookObject \TYPO3\CMS\Core\Database\TableConfigurationPostProcessingHookInterface */
815 $hookObject = GeneralUtility::makeInstance($className);
816 if (!$hookObject instanceof \TYPO3\CMS\Core\Database\TableConfigurationPostProcessingHookInterface) {
817 throw new \UnexpectedValueException(
818 '$hookObject "' . $className . '" must implement interface TYPO3\\CMS\\Core\\Database\\TableConfigurationPostProcessingHookInterface',
819 1320585902
820 );
821 }
822 $hookObject->processData();
823 }
824 }
825 }
826
827 /**
828 * Initialize the Routing for the TYPO3 Backend
829 * Loads all routes registered inside all packages and stores them inside the Router
830 *
831 * @return Bootstrap
832 * @internal This is not a public API method, do not use in own extensions
833 */
834 public function initializeBackendRouter()
835 {
836 // See if the Routes.php from all active packages have been built together already
837 $cacheIdentifier = 'BackendRoutesFromPackages_' . sha1((TYPO3_version . PATH_site . 'BackendRoutesFromPackages'));
838
839 /** @var $codeCache \TYPO3\CMS\Core\Cache\Frontend\PhpFrontend */
840 $codeCache = $this->getEarlyInstance(\TYPO3\CMS\Core\Cache\CacheManager::class)->getCache('cache_core');
841 $routesFromPackages = [];
842 if ($codeCache->has($cacheIdentifier)) {
843 // substr is necessary, because the php frontend wraps php code around the cache value
844 $routesFromPackages = unserialize(substr($codeCache->get($cacheIdentifier), 6, -2));
845 } else {
846 // Loop over all packages and check for a Configuration/Backend/Routes.php file
847 $packageManager = $this->getEarlyInstance(\TYPO3\CMS\Core\Package\PackageManager::class);
848 $packages = $packageManager->getActivePackages();
849 foreach ($packages as $package) {
850 $routesFileNameForPackage = $package->getPackagePath() . 'Configuration/Backend/Routes.php';
851 if (file_exists($routesFileNameForPackage)) {
852 $definedRoutesInPackage = require $routesFileNameForPackage;
853 if (is_array($definedRoutesInPackage)) {
854 $routesFromPackages = array_merge($routesFromPackages, $definedRoutesInPackage);
855 }
856 }
857 $routesFileNameForPackage = $package->getPackagePath() . 'Configuration/Backend/AjaxRoutes.php';
858 if (file_exists($routesFileNameForPackage)) {
859 $definedRoutesInPackage = require $routesFileNameForPackage;
860 if (is_array($definedRoutesInPackage)) {
861 foreach ($definedRoutesInPackage as $routeIdentifier => $routeOptions) {
862 // prefix the route with "ajax_" as "namespace"
863 $routeOptions['path'] = '/ajax' . $routeOptions['path'];
864 $routesFromPackages['ajax_' . $routeIdentifier] = $routeOptions;
865 $routesFromPackages['ajax_' . $routeIdentifier]['ajax'] = true;
866 }
867 }
868 }
869 }
870 // Store the data from all packages in the cache
871 $codeCache->set($cacheIdentifier, serialize($routesFromPackages));
872 }
873
874 // Build Route objects from the data
875 $router = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\Router::class);
876 foreach ($routesFromPackages as $name => $options) {
877 $path = $options['path'];
878 unset($options['path']);
879 $route = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\Route::class, $path, $options);
880 $router->addRoute($name, $route);
881 }
882 return $this;
883 }
884
885 /**
886 * Initialize backend user object in globals
887 *
888 * @param string $className usually \TYPO3\CMS\Core\Authentication\BackendUserAuthentication::class but can be used for CLI
889 * @return Bootstrap
890 * @internal This is not a public API method, do not use in own extensions
891 */
892 public function initializeBackendUser($className = \TYPO3\CMS\Core\Authentication\BackendUserAuthentication::class)
893 {
894 /** @var $backendUser \TYPO3\CMS\Core\Authentication\BackendUserAuthentication */
895 $backendUser = GeneralUtility::makeInstance($className);
896 // The global must be available very early, because methods below
897 // might trigger code which relies on it. See: #45625
898 $GLOBALS['BE_USER'] = $backendUser;
899 $backendUser->start();
900 return $this;
901 }
902
903 /**
904 * Initializes and ensures authenticated access
905 *
906 * @internal This is not a public API method, do not use in own extensions
907 * @param bool $proceedIfNoUserIsLoggedIn if set to TRUE, no forced redirect to the login page will be done
908 * @return \TYPO3\CMS\Core\Core\Bootstrap
909 */
910 public function initializeBackendAuthentication($proceedIfNoUserIsLoggedIn = false)
911 {
912 $GLOBALS['BE_USER']->backendCheckLogin($proceedIfNoUserIsLoggedIn);
913 return $this;
914 }
915
916 /**
917 * Initialize language object
918 *
919 * @return Bootstrap
920 * @internal This is not a public API method, do not use in own extensions
921 */
922 public function initializeLanguageObject()
923 {
924 /** @var $GLOBALS['LANG'] \TYPO3\CMS\Core\Localization\LanguageService */
925 $GLOBALS['LANG'] = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Localization\LanguageService::class);
926 $GLOBALS['LANG']->init($GLOBALS['BE_USER']->uc['lang']);
927 return $this;
928 }
929
930 /**
931 * Throw away all output that may have happened during bootstrapping by weird extensions
932 *
933 * @return Bootstrap
934 * @internal This is not a public API method, do not use in own extensions
935 */
936 public function endOutputBufferingAndCleanPreviousOutput()
937 {
938 ob_clean();
939 return $this;
940 }
941
942 /**
943 * Initialize output compression if configured
944 *
945 * @return Bootstrap
946 * @internal This is not a public API method, do not use in own extensions
947 */
948 public function initializeOutputCompression()
949 {
950 if (extension_loaded('zlib') && $GLOBALS['TYPO3_CONF_VARS']['BE']['compressionLevel']) {
951 if (MathUtility::canBeInterpretedAsInteger($GLOBALS['TYPO3_CONF_VARS']['BE']['compressionLevel'])) {
952 @ini_set('zlib.output_compression_level', $GLOBALS['TYPO3_CONF_VARS']['BE']['compressionLevel']);
953 }
954 ob_start('ob_gzhandler');
955 }
956 return $this;
957 }
958
959 /**
960 * Send HTTP headers if configured
961 *
962 * @return Bootstrap
963 * @internal This is not a public API method, do not use in own extensions
964 */
965 public function sendHttpHeaders()
966 {
967 if (!empty($GLOBALS['TYPO3_CONF_VARS']['BE']['HTTP']['Response']['Headers']) && is_array($GLOBALS['TYPO3_CONF_VARS']['BE']['HTTP']['Response']['Headers'])) {
968 foreach ($GLOBALS['TYPO3_CONF_VARS']['BE']['HTTP']['Response']['Headers'] as $header) {
969 header($header);
970 }
971 }
972 return $this;
973 }
974
975 /**
976 * Things that should be performed to shut down the framework.
977 * This method is called in all important scripts for a clean
978 * shut down of the system.
979 *
980 * @return Bootstrap
981 * @internal This is not a public API method, do not use in own extensions
982 */
983 public function shutdown()
984 {
985 $this->sendResponse();
986 return $this;
987 }
988
989 /**
990 * Provides an instance of "template" for backend-modules to
991 * work with.
992 *
993 * @return Bootstrap
994 * @internal This is not a public API method, do not use in own extensions
995 */
996 public function initializeBackendTemplate()
997 {
998 $GLOBALS['TBE_TEMPLATE'] = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Template\DocumentTemplate::class);
999 return $this;
1000 }
1001 }