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