vendor/contao/core-bundle/src/Framework/ContaoFramework.php line 155

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. /*
  4.  * This file is part of Contao.
  5.  *
  6.  * (c) Leo Feyer
  7.  *
  8.  * @license LGPL-3.0-or-later
  9.  */
  10. namespace Contao\CoreBundle\Framework;
  11. use Contao\ClassLoader;
  12. use Contao\Config;
  13. use Contao\CoreBundle\Exception\LegacyRoutingException;
  14. use Contao\CoreBundle\Exception\RedirectResponseException;
  15. use Contao\CoreBundle\Routing\ScopeMatcher;
  16. use Contao\CoreBundle\Security\Authentication\Token\TokenChecker;
  17. use Contao\CoreBundle\Session\LazySessionAccess;
  18. use Contao\CoreBundle\Util\LocaleUtil;
  19. use Contao\Environment;
  20. use Contao\Input;
  21. use Contao\InsertTags;
  22. use Contao\Model\Registry;
  23. use Contao\RequestToken;
  24. use Contao\System;
  25. use Contao\TemplateLoader;
  26. use Symfony\Component\DependencyInjection\ContainerAwareInterface;
  27. use Symfony\Component\DependencyInjection\ContainerAwareTrait;
  28. use Symfony\Component\Filesystem\Filesystem;
  29. use Symfony\Component\HttpFoundation\Request;
  30. use Symfony\Component\HttpFoundation\RequestStack;
  31. use Symfony\Component\HttpFoundation\Session\SessionInterface;
  32. use Symfony\Contracts\Service\ResetInterface;
  33. use Webmozart\PathUtil\Path;
  34. /**
  35.  * @internal Do not use this class in your code; use the "contao.framework" service instead
  36.  */
  37. class ContaoFramework implements ContaoFrameworkInterfaceContainerAwareInterfaceResetInterface
  38. {
  39.     use ContainerAwareTrait;
  40.     /**
  41.      * @var bool
  42.      */
  43.     private static $initialized false;
  44.     /**
  45.      * @var RequestStack
  46.      */
  47.     private $requestStack;
  48.     /**
  49.      * @var ScopeMatcher
  50.      */
  51.     private $scopeMatcher;
  52.     /**
  53.      * @var TokenChecker
  54.      */
  55.     private $tokenChecker;
  56.     /**
  57.      * @var Filesystem
  58.      */
  59.     private $filesystem;
  60.     /**
  61.      * @var string
  62.      */
  63.     private $projectDir;
  64.     /**
  65.      * @var int
  66.      */
  67.     private $errorLevel;
  68.     /**
  69.      * @var bool
  70.      */
  71.     private $legacyRouting;
  72.     /**
  73.      * @var Request|null
  74.      */
  75.     private $request;
  76.     /**
  77.      * @var bool
  78.      */
  79.     private $isFrontend false;
  80.     /**
  81.      * @var array
  82.      */
  83.     private $adapterCache = [];
  84.     /**
  85.      * @var array
  86.      */
  87.     private $hookListeners = [];
  88.     public function __construct(RequestStack $requestStackScopeMatcher $scopeMatcherTokenChecker $tokenCheckerFilesystem $filesystemstring $projectDirint $errorLevelbool $legacyRouting)
  89.     {
  90.         $this->requestStack $requestStack;
  91.         $this->scopeMatcher $scopeMatcher;
  92.         $this->tokenChecker $tokenChecker;
  93.         $this->filesystem $filesystem;
  94.         $this->projectDir $projectDir;
  95.         $this->errorLevel $errorLevel;
  96.         $this->legacyRouting $legacyRouting;
  97.     }
  98.     public function reset(): void
  99.     {
  100.         $this->adapterCache = [];
  101.         $this->isFrontend false;
  102.         if (!$this->isInitialized()) {
  103.             return;
  104.         }
  105.         Environment::reset();
  106.         Input::resetCache();
  107.         Input::resetUnusedGet();
  108.         InsertTags::reset();
  109.         Registry::getInstance()->reset();
  110.     }
  111.     public function isInitialized(): bool
  112.     {
  113.         return self::$initialized;
  114.     }
  115.     /**
  116.      * @throws \LogicException
  117.      */
  118.     public function initialize(bool $isFrontend false): void
  119.     {
  120.         if ($this->isInitialized()) {
  121.             return;
  122.         }
  123.         // Set before calling any methods to prevent recursion
  124.         self::$initialized true;
  125.         if (null === $this->container) {
  126.             throw new \LogicException('The service container has not been set.');
  127.         }
  128.         $this->isFrontend $isFrontend;
  129.         $this->request $this->requestStack->getMasterRequest();
  130.         $this->setConstants();
  131.         $this->initializeFramework();
  132.         if (!$this->legacyRouting) {
  133.             $this->throwOnLegacyRoutingHooks();
  134.         }
  135.     }
  136.     public function setHookListeners(array $hookListeners): void
  137.     {
  138.         $this->hookListeners $hookListeners;
  139.     }
  140.     public function createInstance($class$args = [])
  141.     {
  142.         if (\in_array('getInstance'get_class_methods($class), true)) {
  143.             return \call_user_func_array([$class'getInstance'], $args);
  144.         }
  145.         $reflection = new \ReflectionClass($class);
  146.         return $reflection->newInstanceArgs($args);
  147.     }
  148.     /**
  149.      * @template T
  150.      *
  151.      * @param class-string<T> $class
  152.      *
  153.      * @return Adapter<T>
  154.      */
  155.     public function getAdapter($class): Adapter
  156.     {
  157.         if (!isset($this->adapterCache[$class])) {
  158.             $this->adapterCache[$class] = new Adapter($class);
  159.         }
  160.         return $this->adapterCache[$class];
  161.     }
  162.     /**
  163.      * @deprecated Deprecated since Contao 4.0, to be removed in Contao 5.0
  164.      */
  165.     private function setConstants(): void
  166.     {
  167.         if (!\defined('TL_MODE')) {
  168.             \define('TL_MODE'$this->getMode());
  169.         }
  170.         \define('TL_START'microtime(true));
  171.         \define('TL_ROOT'$this->projectDir);
  172.         \define('TL_REFERER_ID'$this->getRefererId());
  173.         if (!\defined('TL_SCRIPT')) {
  174.             \define('TL_SCRIPT'$this->getRoute());
  175.         }
  176.         // Define the login status constants (see #4099, #5279)
  177.         if ('FE' === $this->getMode() && ($session $this->getSession()) && $this->request->hasPreviousSession()) {
  178.             $session->start();
  179.             \define('BE_USER_LOGGED_IN'$this->tokenChecker->hasBackendUser() && $this->tokenChecker->isPreviewMode());
  180.             \define('FE_USER_LOGGED_IN'$this->tokenChecker->hasFrontendUser());
  181.         } else {
  182.             \define('BE_USER_LOGGED_IN'false);
  183.             \define('FE_USER_LOGGED_IN'false);
  184.         }
  185.         // Define the relative path to the installation (see #5339)
  186.         \define('TL_PATH'$this->getPath());
  187.     }
  188.     private function getMode(): ?string
  189.     {
  190.         if (true === $this->isFrontend) {
  191.             return 'FE';
  192.         }
  193.         if (null === $this->request) {
  194.             return null;
  195.         }
  196.         if ($this->scopeMatcher->isBackendRequest($this->request)) {
  197.             return 'BE';
  198.         }
  199.         if ($this->scopeMatcher->isFrontendRequest($this->request)) {
  200.             return 'FE';
  201.         }
  202.         return null;
  203.     }
  204.     private function getRefererId(): ?string
  205.     {
  206.         if (null === $this->request) {
  207.             return null;
  208.         }
  209.         return $this->request->attributes->get('_contao_referer_id''');
  210.     }
  211.     private function getRoute(): ?string
  212.     {
  213.         if (null === $this->request) {
  214.             return null;
  215.         }
  216.         return substr($this->request->getBaseUrl().$this->request->getPathInfo(), \strlen($this->request->getBasePath().'/'));
  217.     }
  218.     private function getPath(): ?string
  219.     {
  220.         if (null === $this->request) {
  221.             return null;
  222.         }
  223.         return $this->request->getBasePath();
  224.     }
  225.     private function initializeFramework(): void
  226.     {
  227.         // Set the error_reporting level
  228.         error_reporting($this->errorLevel);
  229.         $this->includeHelpers();
  230.         $this->includeBasicClasses();
  231.         // Set the container
  232.         System::setContainer($this->container);
  233.         /** @var Config $config */
  234.         $config $this->getAdapter(Config::class);
  235.         // Preload the configuration (see #5872)
  236.         $config->preload();
  237.         // Register the class loader
  238.         ClassLoader::scanAndRegister();
  239.         $this->initializeLegacySessionAccess();
  240.         $this->setDefaultLanguage();
  241.         // Fully load the configuration
  242.         $config->getInstance();
  243.         $this->registerHookListeners();
  244.         $this->validateInstallation();
  245.         Input::initialize();
  246.         TemplateLoader::initialize();
  247.         $this->setTimezone();
  248.         $this->triggerInitializeSystemHook();
  249.         $this->handleRequestToken();
  250.     }
  251.     private function includeHelpers(): void
  252.     {
  253.         require __DIR__.'/../Resources/contao/helper/functions.php';
  254.         require __DIR__.'/../Resources/contao/config/constants.php';
  255.     }
  256.     /**
  257.      * Includes the basic classes required for further processing.
  258.      */
  259.     private function includeBasicClasses(): void
  260.     {
  261.         static $basicClasses = [
  262.             'System',
  263.             'Config',
  264.             'ClassLoader',
  265.             'TemplateLoader',
  266.             'ModuleLoader',
  267.         ];
  268.         foreach ($basicClasses as $class) {
  269.             if (!class_exists($classfalse)) {
  270.                 require_once __DIR__.'/../Resources/contao/library/Contao/'.$class.'.php';
  271.             }
  272.         }
  273.     }
  274.     /**
  275.      * Initializes session access for $_SESSION['FE_DATA'] and $_SESSION['BE_DATA'].
  276.      */
  277.     private function initializeLegacySessionAccess(): void
  278.     {
  279.         if (!$session $this->getSession()) {
  280.             return;
  281.         }
  282.         if (!$session->isStarted()) {
  283.             $_SESSION = new LazySessionAccess($session$this->request && $this->request->hasPreviousSession());
  284.         } else {
  285.             $_SESSION['BE_DATA'] = $session->getBag('contao_backend');
  286.             $_SESSION['FE_DATA'] = $session->getBag('contao_frontend');
  287.         }
  288.     }
  289.     private function setDefaultLanguage(): void
  290.     {
  291.         $language 'en';
  292.         if (null !== $this->request) {
  293.             $language LocaleUtil::formatAsLanguageTag($this->request->getLocale());
  294.         }
  295.         // Deprecated since Contao 4.0, to be removed in Contao 5.0
  296.         $GLOBALS['TL_LANGUAGE'] = $language;
  297.     }
  298.     /**
  299.      * Redirects to the install tool if the installation is incomplete.
  300.      */
  301.     private function validateInstallation(): void
  302.     {
  303.         if (null === $this->request) {
  304.             return;
  305.         }
  306.         static $installRoutes = [
  307.             'contao_install',
  308.             'contao_install_redirect',
  309.         ];
  310.         if (\in_array($this->request->attributes->get('_route'), $installRoutestrue)) {
  311.             return;
  312.         }
  313.         /** @var Config $config */
  314.         $config $this->getAdapter(Config::class);
  315.         if (!$config->isComplete()) {
  316.             throw new RedirectResponseException('/contao/install');
  317.         }
  318.     }
  319.     private function setTimezone(): void
  320.     {
  321.         /** @var Config $config */
  322.         $config $this->getAdapter(Config::class);
  323.         $this->iniSet('date.timezone', (string) $config->get('timeZone'));
  324.         date_default_timezone_set((string) $config->get('timeZone'));
  325.     }
  326.     private function triggerInitializeSystemHook(): void
  327.     {
  328.         if (
  329.             !empty($GLOBALS['TL_HOOKS']['initializeSystem'])
  330.             && \is_array($GLOBALS['TL_HOOKS']['initializeSystem'])
  331.             && is_dir(Path::join($this->projectDir'system/tmp'))
  332.         ) {
  333.             foreach ($GLOBALS['TL_HOOKS']['initializeSystem'] as $callback) {
  334.                 System::importStatic($callback[0])->{$callback[1]}();
  335.             }
  336.         }
  337.         if ($this->filesystem->exists($filePath Path::join($this->projectDir'system/config/initconfig.php'))) {
  338.             trigger_deprecation('contao/core-bundle''4.0''Using the "initconfig.php" file has been deprecated and will no longer work in Contao 5.0.');
  339.             include $filePath;
  340.         }
  341.     }
  342.     private function handleRequestToken(): void
  343.     {
  344.         /** @var RequestToken $requestToken */
  345.         $requestToken $this->getAdapter(RequestToken::class);
  346.         // Deprecated since Contao 4.0, to be removed in Contao 5.0
  347.         if (!\defined('REQUEST_TOKEN')) {
  348.             \define('REQUEST_TOKEN''cli' === \PHP_SAPI null $requestToken->get());
  349.         }
  350.     }
  351.     private function iniSet(string $keystring $value): void
  352.     {
  353.         if (\function_exists('ini_set')) {
  354.             ini_set($key$value);
  355.         }
  356.     }
  357.     private function getSession(): ?SessionInterface
  358.     {
  359.         if (null === $this->request || !$this->request->hasSession()) {
  360.             return null;
  361.         }
  362.         return $this->request->getSession();
  363.     }
  364.     private function registerHookListeners(): void
  365.     {
  366.         foreach ($this->hookListeners as $hookName => $priorities) {
  367.             if (isset($GLOBALS['TL_HOOKS'][$hookName]) && \is_array($GLOBALS['TL_HOOKS'][$hookName])) {
  368.                 if (isset($priorities[0])) {
  369.                     $priorities[0] = array_merge($GLOBALS['TL_HOOKS'][$hookName], $priorities[0]);
  370.                 } else {
  371.                     $priorities[0] = $GLOBALS['TL_HOOKS'][$hookName];
  372.                     krsort($priorities);
  373.                 }
  374.             }
  375.             $GLOBALS['TL_HOOKS'][$hookName] = array_merge(...$priorities);
  376.         }
  377.     }
  378.     private function throwOnLegacyRoutingHooks(): void
  379.     {
  380.         if (empty($GLOBALS['TL_HOOKS']['getPageIdFromUrl']) && empty($GLOBALS['TL_HOOKS']['getRootPageFromUrl'])) {
  381.             return;
  382.         }
  383.         throw new LegacyRoutingException('Legacy routing is required to support the "getPageIdFromUrl" and "getRootPageFromUrl" hooks. Check the Symfony inspector for more information.');
  384.     }
  385. }