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

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