vendor/contao/manager-bundle/src/HttpKernel/ContaoKernel.php line 364

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\ManagerBundle\HttpKernel;
  11. use AppBundle\AppBundle;
  12. use Contao\ManagerBundle\Api\ManagerConfig;
  13. use Contao\ManagerBundle\ContaoManager\Plugin;
  14. use Contao\ManagerPlugin\Bundle\BundleLoader;
  15. use Contao\ManagerPlugin\Bundle\Config\ConfigResolverFactory;
  16. use Contao\ManagerPlugin\Bundle\Parser\DelegatingParser;
  17. use Contao\ManagerPlugin\Bundle\Parser\IniParser;
  18. use Contao\ManagerPlugin\Bundle\Parser\JsonParser;
  19. use Contao\ManagerPlugin\Config\ConfigPluginInterface;
  20. use Contao\ManagerPlugin\Config\ContainerBuilder as PluginContainerBuilder;
  21. use Contao\ManagerPlugin\HttpKernel\HttpCacheSubscriberPluginInterface;
  22. use Contao\ManagerPlugin\PluginLoader;
  23. use FOS\HttpCache\SymfonyCache\HttpCacheProvider;
  24. use ProxyManager\Configuration;
  25. use Symfony\Bridge\ProxyManager\LazyProxy\Instantiator\RuntimeInstantiator;
  26. use Symfony\Component\Config\Loader\LoaderInterface;
  27. use Symfony\Component\Console\Input\InputInterface;
  28. use Symfony\Component\Dotenv\Dotenv;
  29. use Symfony\Component\ErrorHandler\Debug;
  30. use Symfony\Component\HttpFoundation\Request;
  31. use Symfony\Component\HttpKernel\HttpKernelInterface;
  32. use Symfony\Component\HttpKernel\Kernel;
  33. use Webmozart\PathUtil\Path;
  34. class ContaoKernel extends Kernel implements HttpCacheProvider
  35. {
  36.     /**
  37.      * @var string
  38.      */
  39.     protected static $projectDir;
  40.     /**
  41.      * @var PluginLoader
  42.      */
  43.     private $pluginLoader;
  44.     /**
  45.      * @var BundleLoader
  46.      */
  47.     private $bundleLoader;
  48.     /**
  49.      * @var JwtManager
  50.      */
  51.     private $jwtManager;
  52.     /**
  53.      * @var ManagerConfig
  54.      */
  55.     private $managerConfig;
  56.     /**
  57.      * @var ContaoCache
  58.      */
  59.     private $httpCache;
  60.     public function shutdown(): void
  61.     {
  62.         // Reset bundle loader to re-calculate bundle order after cache:clear
  63.         if ($this->booted) {
  64.             $this->bundleLoader null;
  65.         }
  66.         parent::shutdown();
  67.     }
  68.     public function registerBundles(): array
  69.     {
  70.         $bundles = [];
  71.         $this->addBundlesFromPlugins($bundles);
  72.         return $bundles;
  73.     }
  74.     public function getProjectDir(): string
  75.     {
  76.         if (null === self::$projectDir) {
  77.             throw new \LogicException('ContaoKernel::setProjectDir() must be called to initialize the Contao kernel');
  78.         }
  79.         return self::$projectDir;
  80.     }
  81.     /**
  82.      * @deprecated since Symfony 4.2, use getProjectDir() instead
  83.      */
  84.     public function getRootDir(): string
  85.     {
  86.         return Path::join($this->getProjectDir(), 'app');
  87.     }
  88.     public function getCacheDir(): string
  89.     {
  90.         return Path::join($this->getProjectDir(), 'var/cache'$this->getEnvironment());
  91.     }
  92.     public function getLogDir(): string
  93.     {
  94.         return Path::join($this->getProjectDir(), 'var/logs');
  95.     }
  96.     public function getPluginLoader(): PluginLoader
  97.     {
  98.         if (null === $this->pluginLoader) {
  99.             $this->pluginLoader = new PluginLoader();
  100.             $config $this->getManagerConfig()->all();
  101.             if (
  102.                 isset($config['contao_manager']['disabled_packages'])
  103.                 && \is_array($config['contao_manager']['disabled_packages'])
  104.             ) {
  105.                 $this->pluginLoader->setDisabledPackages($config['contao_manager']['disabled_packages']);
  106.             }
  107.         }
  108.         return $this->pluginLoader;
  109.     }
  110.     public function setPluginLoader(PluginLoader $pluginLoader): void
  111.     {
  112.         $this->pluginLoader $pluginLoader;
  113.     }
  114.     public function getBundleLoader(): BundleLoader
  115.     {
  116.         if (null === $this->bundleLoader) {
  117.             $parser = new DelegatingParser();
  118.             $parser->addParser(new JsonParser());
  119.             $parser->addParser(new IniParser(Path::join($this->getProjectDir(), 'system/modules')));
  120.             $this->bundleLoader = new BundleLoader($this->getPluginLoader(), new ConfigResolverFactory(), $parser);
  121.         }
  122.         return $this->bundleLoader;
  123.     }
  124.     public function setBundleLoader(BundleLoader $bundleLoader): void
  125.     {
  126.         $this->bundleLoader $bundleLoader;
  127.     }
  128.     public function getJwtManager(): ?JwtManager
  129.     {
  130.         return $this->jwtManager;
  131.     }
  132.     public function setJwtManager(JwtManager $jwtManager): void
  133.     {
  134.         $this->jwtManager $jwtManager;
  135.     }
  136.     public function getManagerConfig(): ManagerConfig
  137.     {
  138.         if (null === $this->managerConfig) {
  139.             $this->managerConfig = new ManagerConfig($this->getProjectDir());
  140.         }
  141.         return $this->managerConfig;
  142.     }
  143.     public function setManagerConfig(ManagerConfig $managerConfig): void
  144.     {
  145.         $this->managerConfig $managerConfig;
  146.     }
  147.     public function registerContainerConfiguration(LoaderInterface $loader): void
  148.     {
  149.         if ($parametersFile $this->getConfigFile('parameters')) {
  150.             $loader->load($parametersFile);
  151.         }
  152.         $config $this->getManagerConfig()->all();
  153.         $plugins $this->getPluginLoader()->getInstancesOf(PluginLoader::CONFIG_PLUGINS);
  154.         /** @var array<ConfigPluginInterface> $plugins */
  155.         foreach ($plugins as $plugin) {
  156.             $plugin->registerContainerConfiguration($loader$config);
  157.         }
  158.         // Reload the parameters.yml file
  159.         if ($parametersFile) {
  160.             $loader->load($parametersFile);
  161.         }
  162.         if ($configFile $this->getConfigFile('config_'.$this->getEnvironment())) {
  163.             $loader->load($configFile);
  164.         } elseif ($configFile $this->getConfigFile('config')) {
  165.             $loader->load($configFile);
  166.         }
  167.         // Automatically load the services.yml file if it exists
  168.         if ($servicesFile $this->getConfigFile('services')) {
  169.             $loader->load($servicesFile);
  170.         }
  171.         if (is_dir(Path::join($this->getProjectDir(), 'src'))) {
  172.             $loader->load(__DIR__.'/../Resources/skeleton/config/services.php');
  173.         }
  174.     }
  175.     public function getHttpCache(): ContaoCache
  176.     {
  177.         if (null !== $this->httpCache) {
  178.             return $this->httpCache;
  179.         }
  180.         $this->httpCache = new ContaoCache($thisPath::join($this->getProjectDir(), 'var/cache/prod/http_cache'));
  181.         /** @var array<HttpCacheSubscriberPluginInterface> $plugins */
  182.         $plugins $this->getPluginLoader()->getInstancesOf(HttpCacheSubscriberPluginInterface::class);
  183.         foreach ($plugins as $plugin) {
  184.             foreach ($plugin->getHttpCacheSubscribers() as $subscriber) {
  185.                 $this->httpCache->addSubscriber($subscriber);
  186.             }
  187.         }
  188.         return $this->httpCache;
  189.     }
  190.     /**
  191.      * Sets the project directory (the Contao kernel does not know its location).
  192.      */
  193.     public static function setProjectDir(string $projectDir): void
  194.     {
  195.         self::$projectDir realpath($projectDir) ?: $projectDir;
  196.     }
  197.     /**
  198.      * @return ContaoKernel|ContaoCache
  199.      */
  200.     public static function fromRequest(string $projectDirRequest $request): HttpKernelInterface
  201.     {
  202.         self::loadEnv($projectDir'jwt');
  203.         if ($trustedHosts $_SERVER['TRUSTED_HOSTS'] ?? null) {
  204.             Request::setTrustedHosts(explode(','$trustedHosts));
  205.         }
  206.         if ($trustedProxies $_SERVER['TRUSTED_PROXIES'] ?? null) {
  207.             $trustedHeaderSet Request::HEADER_X_FORWARDED_FOR Request::HEADER_X_FORWARDED_PORT Request::HEADER_X_FORWARDED_PROTO;
  208.             // If we have a limited list of trusted hosts, we can safely use the X-Forwarded-Host header
  209.             if ($trustedHosts) {
  210.                 $trustedHeaderSet |= Request::HEADER_X_FORWARDED_HOST;
  211.             }
  212.             Request::setTrustedProxies(explode(','$trustedProxies), $trustedHeaderSet);
  213.         }
  214.         Request::enableHttpMethodParameterOverride();
  215.         $jwtManager null;
  216.         $env null;
  217.         $parseJwt 'jwt' === $_SERVER['APP_ENV'];
  218.         if ($parseJwt) {
  219.             $env 'prod';
  220.             $jwtManager = new JwtManager($projectDir);
  221.             $jwt $jwtManager->parseRequest($request);
  222.             if (\is_array($jwt) && ($jwt['debug'] ?? false)) {
  223.                 $env 'dev';
  224.             }
  225.             $_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = $env;
  226.         }
  227.         $kernel = static::create($projectDir$env);
  228.         if ($parseJwt) {
  229.             $kernel->setJwtManager($jwtManager);
  230.         }
  231.         // Enable the Symfony reverse proxy if not disabled explicitly
  232.         if (!($_SERVER['DISABLE_HTTP_CACHE'] ?? null) && !$kernel->isDebug()) {
  233.             return $kernel->getHttpCache();
  234.         }
  235.         return $kernel;
  236.     }
  237.     public static function fromInput(string $projectDirInputInterface $input): self
  238.     {
  239.         $env $input->getParameterOption(['--env''-e'], null);
  240.         self::loadEnv($projectDir$env ?: 'prod');
  241.         return static::create($projectDir$env);
  242.     }
  243.     protected function getContainerBuilder(): PluginContainerBuilder
  244.     {
  245.         $container = new PluginContainerBuilder($this->getPluginLoader(), []);
  246.         $container->getParameterBag()->add($this->getKernelParameters());
  247.         if (class_exists(Configuration::class) && class_exists(RuntimeInstantiator::class)) {
  248.             $container->setProxyInstantiator(new RuntimeInstantiator());
  249.         }
  250.         return $container;
  251.     }
  252.     protected function initializeContainer(): void
  253.     {
  254.         parent::initializeContainer();
  255.         if (null === ($container $this->getContainer())) {
  256.             return;
  257.         }
  258.         // Set the plugin loader again, so it is available at runtime (synthetic service)
  259.         $container->set('contao_manager.plugin_loader'$this->getPluginLoader());
  260.         // Set the JWT manager only if the debug mode has not been configured in env variables
  261.         if ($jwtManager $this->getJwtManager()) {
  262.             $container->set('contao_manager.jwt_manager'$jwtManager);
  263.         }
  264.     }
  265.     private function getConfigFile(string $file): ?string
  266.     {
  267.         $projectDir $this->getProjectDir();
  268.         foreach (['.yaml''.yml'] as $ext) {
  269.             if (file_exists($path Path::join($projectDir'config'$file.$ext))) {
  270.                 return $path;
  271.             }
  272.         }
  273.         // Fallback to the legacy config file (see #566)
  274.         foreach (['.yaml''.yml'] as $ext) {
  275.             $path Path::join($projectDir'app/config'$file.$ext);
  276.             if (file_exists($path)) {
  277.                 trigger_deprecation('contao/manager-bundle''4.9'sprintf('Storing the "%s" file in the "app/config" folder has been deprecated and will no longer work in Contao 5.0. Move it to the "config" folder instead.'$file.$ext));
  278.                 return $path;
  279.             }
  280.         }
  281.         return null;
  282.     }
  283.     private function addBundlesFromPlugins(array &$bundles): void
  284.     {
  285.         $configs $this->getBundleLoader()->getBundleConfigs(
  286.             'dev' === $this->getEnvironment(),
  287.             $this->debug null Path::join($this->getCacheDir(), 'bundles.map')
  288.         );
  289.         foreach ($configs as $config) {
  290.             $bundles[$config->getName()] = $config->getBundleInstance($this);
  291.         }
  292.         // Autoload AppBundle for convenience
  293.         $appBundle AppBundle::class;
  294.         if (!isset($bundles[$appBundle]) && class_exists($appBundle)) {
  295.             $bundles[$appBundle] = new $appBundle();
  296.         }
  297.     }
  298.     private static function create(string $projectDirstring $env null): self
  299.     {
  300.         if (null === $env) {
  301.             $env $_SERVER['APP_ENV'] ?? 'prod';
  302.         }
  303.         if ('dev' !== $env && 'prod' !== $env) {
  304.             throw new \RuntimeException('The Contao Managed Edition only supports the "dev" and "prod" environments');
  305.         }
  306.         Plugin::autoloadModules(Path::join($projectDir'system/modules'));
  307.         static::setProjectDir($projectDir);
  308.         if ('dev' === $env) {
  309.             Debug::enable();
  310.         }
  311.         return new static($env'dev' === $env);
  312.     }
  313.     private static function loadEnv(string $projectDirstring $defaultEnv 'prod'): void
  314.     {
  315.         // Load cached env vars if the .env.local.php file exists
  316.         // See https://github.com/symfony/recipes/blob/master/symfony/framework-bundle/4.2/config/bootstrap.php
  317.         if (\is_array($env = @include Path::join($projectDir'.env.local.php'))) {
  318.             foreach ($env as $k => $v) {
  319.                 $_ENV[$k] = $_ENV[$k] ?? (isset($_SERVER[$k]) && !== strpos($k'HTTP_') ? $_SERVER[$k] : $v);
  320.             }
  321.         } elseif (file_exists($filePath Path::join($projectDir'.env'))) {
  322.             (new Dotenv(false))->loadEnv($filePath'APP_ENV'$defaultEnv);
  323.         }
  324.         $_SERVER += $_ENV;
  325.         $_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = $_SERVER['APP_ENV'] ?? $_ENV['APP_ENV'] ?? null ?: $defaultEnv;
  326.     }
  327. }