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