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

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