vendor/shopware/core/Kernel.php line 437

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Shopware\Core;
  3. use Doctrine\DBAL\Connection;
  4. use Shopware\Core\DevOps\Environment\EnvironmentHelper;
  5. use Shopware\Core\Framework\Adapter\Database\MySQLFactory;
  6. use Shopware\Core\Framework\Api\Controller\FallbackController;
  7. use Shopware\Core\Framework\Log\Package;
  8. use Shopware\Core\Framework\Migration\MigrationStep;
  9. use Shopware\Core\Framework\Plugin\KernelPluginLoader\KernelPluginLoader;
  10. use Shopware\Core\Framework\Util\VersionParser;
  11. use Shopware\Core\Maintenance\Maintenance;
  12. use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
  13. use Symfony\Component\Config\ConfigCache;
  14. use Symfony\Component\Config\Loader\LoaderInterface;
  15. use Symfony\Component\DependencyInjection\ContainerBuilder;
  16. use Symfony\Component\HttpKernel\Bundle\Bundle;
  17. use Symfony\Component\HttpKernel\Bundle\BundleInterface;
  18. use Symfony\Component\HttpKernel\Kernel as HttpKernel;
  19. use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
  20. use Symfony\Component\Routing\Route;
  21. #[Package('core')]
  22. class Kernel extends HttpKernel
  23. {
  24.     use MicroKernelTrait;
  25.     /**
  26.      * @internal
  27.      *
  28.      * @deprecated tag:v6.5.0 The connection requirements should be fixed
  29.      */
  30.     public const PLACEHOLDER_DATABASE_URL 'mysql://_placeholder.test';
  31.     public const CONFIG_EXTS '.{php,xml,yaml,yml}';
  32.     /**
  33.      * @var string Fallback version if nothing is provided via kernel constructor
  34.      */
  35.     public const SHOPWARE_FALLBACK_VERSION '6.4.9999999.9999999-dev';
  36.     /**
  37.      * @var Connection|null
  38.      */
  39.     protected static $connection;
  40.     /**
  41.      * @var KernelPluginLoader
  42.      */
  43.     protected $pluginLoader;
  44.     /**
  45.      * @var string
  46.      */
  47.     protected $shopwareVersion;
  48.     /**
  49.      * @var string|null
  50.      */
  51.     protected $shopwareVersionRevision;
  52.     /**
  53.      * @var string|null
  54.      */
  55.     protected $projectDir;
  56.     private bool $rebooting false;
  57.     private string $cacheId;
  58.     /**
  59.      * {@inheritdoc}
  60.      */
  61.     public function __construct(
  62.         string $environment,
  63.         bool $debug,
  64.         KernelPluginLoader $pluginLoader,
  65.         string $cacheId,
  66.         ?string $version self::SHOPWARE_FALLBACK_VERSION,
  67.         ?Connection $connection null,
  68.         ?string $projectDir null
  69.     ) {
  70.         date_default_timezone_set('UTC');
  71.         parent::__construct($environment$debug);
  72.         self::$connection $connection;
  73.         $this->pluginLoader $pluginLoader;
  74.         $version VersionParser::parseShopwareVersion($version);
  75.         $this->shopwareVersion $version['version'];
  76.         $this->shopwareVersionRevision $version['revision'];
  77.         $this->cacheId $cacheId;
  78.         $this->projectDir $projectDir;
  79.     }
  80.     /**
  81.      * @return \Generator<BundleInterface>
  82.      *
  83.      * @deprecated tag:v6.5.0 - reason:return-type-change -  The return type will be native
  84.      */
  85.     public function registerBundles()/*: \Generator*/
  86.     {
  87.         /** @var array<class-string<Bundle>, array<string, bool>> $bundles */
  88.         $bundles = require $this->getProjectDir() . '/config/bundles.php';
  89.         $instanciatedBundleNames = [];
  90.         foreach ($bundles as $class => $envs) {
  91.             if (isset($envs['all']) || isset($envs[$this->environment])) {
  92.                 $bundle = new $class();
  93.                 $instanciatedBundleNames[] = $bundle->getName();
  94.                 yield $bundle;
  95.             }
  96.         }
  97.         /* @deprecated tag:v6.5.0 Maintenance bundle need to be added to config/bundles.php file */
  98.         if (!\in_array('Maintenance'$instanciatedBundleNamestrue)) {
  99.             yield new Maintenance();
  100.         }
  101.         yield from $this->pluginLoader->getBundles($this->getKernelParameters(), $instanciatedBundleNames);
  102.     }
  103.     /**
  104.      * @return string
  105.      *
  106.      * @deprecated tag:v6.5.0 - reason:return-type-change - The return type will be native
  107.      */
  108.     public function getProjectDir()/*: string*/
  109.     {
  110.         if ($this->projectDir === null) {
  111.             if ($dir $_ENV['PROJECT_ROOT'] ?? $_SERVER['PROJECT_ROOT'] ?? false) {
  112.                 return $this->projectDir $dir;
  113.             }
  114.             $r = new \ReflectionObject($this);
  115.             $dir = (string) $r->getFileName();
  116.             if (!file_exists($dir)) {
  117.                 throw new \LogicException(sprintf('Cannot auto-detect project dir for kernel of class "%s".'$r->name));
  118.             }
  119.             $dir $rootDir = \dirname($dir);
  120.             while (!file_exists($dir '/vendor')) {
  121.                 if ($dir === \dirname($dir)) {
  122.                     return $this->projectDir $rootDir;
  123.                 }
  124.                 $dir = \dirname($dir);
  125.             }
  126.             $this->projectDir $dir;
  127.         }
  128.         return $this->projectDir;
  129.     }
  130.     public function boot(): void
  131.     {
  132.         if ($this->booted === true) {
  133.             if ($this->debug) {
  134.                 $this->startTime microtime(true);
  135.             }
  136.             return;
  137.         }
  138.         if ($this->debug) {
  139.             $this->startTime microtime(true);
  140.         }
  141.         if ($this->debug && !EnvironmentHelper::hasVariable('SHELL_VERBOSITY')) {
  142.             putenv('SHELL_VERBOSITY=3');
  143.             $_ENV['SHELL_VERBOSITY'] = 3;
  144.             $_SERVER['SHELL_VERBOSITY'] = 3;
  145.         }
  146.         try {
  147.             $this->pluginLoader->initializePlugins($this->getProjectDir());
  148.         } catch (\Throwable $e) {
  149.             if (\defined('\STDERR')) {
  150.                 fwrite(\STDERR'Warning: Failed to load plugins. Message: ' $e->getMessage() . \PHP_EOL);
  151.             }
  152.         }
  153.         // init bundles
  154.         $this->initializeBundles();
  155.         // init container
  156.         $this->initializeContainer();
  157.         foreach ($this->getBundles() as $bundle) {
  158.             $bundle->setContainer($this->container);
  159.             $bundle->boot();
  160.         }
  161.         $this->initializeDatabaseConnectionVariables();
  162.         $this->booted true;
  163.     }
  164.     public static function getConnection(): Connection
  165.     {
  166.         if (self::$connection) {
  167.             return self::$connection;
  168.         }
  169.         self::$connection MySQLFactory::create();
  170.         return self::$connection;
  171.     }
  172.     public function getCacheDir(): string
  173.     {
  174.         return sprintf(
  175.             '%s/var/cache/%s_h%s%s',
  176.             $this->getProjectDir(),
  177.             $this->getEnvironment(),
  178.             $this->getCacheHash(),
  179.             EnvironmentHelper::getVariable('TEST_TOKEN') ?? ''
  180.         );
  181.     }
  182.     public function getPluginLoader(): KernelPluginLoader
  183.     {
  184.         return $this->pluginLoader;
  185.     }
  186.     public function shutdown(): void
  187.     {
  188.         if (!$this->booted) {
  189.             return;
  190.         }
  191.         // keep connection when rebooting
  192.         if (!$this->rebooting) {
  193.             self::$connection null;
  194.         }
  195.         parent::shutdown();
  196.     }
  197.     public function reboot($warmupDir, ?KernelPluginLoader $pluginLoader null, ?string $cacheId null): void
  198.     {
  199.         $this->rebooting true;
  200.         try {
  201.             if ($pluginLoader) {
  202.                 $this->pluginLoader $pluginLoader;
  203.             }
  204.             if ($cacheId) {
  205.                 $this->cacheId $cacheId;
  206.             }
  207.             parent::reboot($warmupDir);
  208.         } finally {
  209.             $this->rebooting false;
  210.         }
  211.     }
  212.     protected function configureContainer(ContainerBuilder $containerLoaderInterface $loader): void
  213.     {
  214.         $container->setParameter('container.dumper.inline_class_loader'true);
  215.         $container->setParameter('container.dumper.inline_factories'true);
  216.         $confDir $this->getProjectDir() . '/config';
  217.         $loader->load($confDir '/{packages}/*' self::CONFIG_EXTS'glob');
  218.         $loader->load($confDir '/{packages}/' $this->environment '/**/*' self::CONFIG_EXTS'glob');
  219.         $loader->load($confDir '/{services}' self::CONFIG_EXTS'glob');
  220.         $loader->load($confDir '/{services}_' $this->environment self::CONFIG_EXTS'glob');
  221.     }
  222.     protected function configureRoutes(RoutingConfigurator $routes): void
  223.     {
  224.         $confDir $this->getProjectDir() . '/config';
  225.         $routes->import($confDir '/{routes}/*' self::CONFIG_EXTS'glob');
  226.         $routes->import($confDir '/{routes}/' $this->environment '/**/*' self::CONFIG_EXTS'glob');
  227.         $routes->import($confDir '/{routes}' self::CONFIG_EXTS'glob');
  228.         $this->addBundleRoutes($routes);
  229.         $this->addApiRoutes($routes);
  230.         $this->addBundleOverwrites($routes);
  231.         $this->addFallbackRoute($routes);
  232.     }
  233.     /**
  234.      * {@inheritdoc}
  235.      *
  236.      * @return array<string, mixed>
  237.      */
  238.     protected function getKernelParameters(): array
  239.     {
  240.         $parameters parent::getKernelParameters();
  241.         $activePluginMeta = [];
  242.         foreach ($this->pluginLoader->getPluginInstances()->getActives() as $plugin) {
  243.             $class = \get_class($plugin);
  244.             $activePluginMeta[$class] = [
  245.                 'name' => $plugin->getName(),
  246.                 'path' => $plugin->getPath(),
  247.                 'class' => $class,
  248.             ];
  249.         }
  250.         $pluginDir $this->pluginLoader->getPluginDir($this->getProjectDir());
  251.         $coreDir = \dirname((string) (new \ReflectionClass(self::class))->getFileName());
  252.         return array_merge(
  253.             $parameters,
  254.             [
  255.                 'kernel.cache.hash' => $this->getCacheHash(),
  256.                 'kernel.shopware_version' => $this->shopwareVersion,
  257.                 'kernel.shopware_version_revision' => $this->shopwareVersionRevision,
  258.                 'kernel.shopware_core_dir' => $coreDir,
  259.                 'kernel.plugin_dir' => $pluginDir,
  260.                 'kernel.app_dir' => rtrim($this->getProjectDir(), '/') . '/custom/apps',
  261.                 'kernel.active_plugins' => $activePluginMeta,
  262.                 'kernel.plugin_infos' => $this->pluginLoader->getPluginInfos(),
  263.                 'kernel.supported_api_versions' => [234],
  264.                 'defaults_bool_true' => true,
  265.                 'defaults_bool_false' => false,
  266.                 'default_whitespace' => ' ',
  267.             ]
  268.         );
  269.     }
  270.     protected function getCacheHash(): string
  271.     {
  272.         $plugins = [];
  273.         foreach ($this->pluginLoader->getPluginInfos() as $plugin) {
  274.             if ($plugin['active'] === false) {
  275.                 continue;
  276.             }
  277.             $plugins[$plugin['name']] = $plugin['version'];
  278.         }
  279.         $pluginHash md5((string) json_encode($plugins, \JSON_THROW_ON_ERROR));
  280.         return md5((string) \json_encode([
  281.             $this->cacheId,
  282.             substr((string) $this->shopwareVersionRevision08),
  283.             substr($pluginHash08),
  284.             EnvironmentHelper::getVariable('DATABASE_URL'''),
  285.         ], \JSON_THROW_ON_ERROR));
  286.     }
  287.     protected function initializeDatabaseConnectionVariables(): void
  288.     {
  289.         $shopwareSkipConnectionVariables EnvironmentHelper::getVariable('SHOPWARE_SKIP_CONNECTION_VARIABLES'false);
  290.         if ($shopwareSkipConnectionVariables) {
  291.             return;
  292.         }
  293.         $connection self::getConnection();
  294.         try {
  295.             $setSessionVariables = (bool) EnvironmentHelper::getVariable('SQL_SET_DEFAULT_SESSION_VARIABLES'true);
  296.             $connectionVariables = [];
  297.             $timeZoneSupportEnabled = (bool) EnvironmentHelper::getVariable('SHOPWARE_DBAL_TIMEZONE_SUPPORT_ENABLED'false);
  298.             if ($timeZoneSupportEnabled) {
  299.                 $connectionVariables[] = 'SET @@session.time_zone = "UTC"';
  300.             }
  301.             if ($setSessionVariables) {
  302.                 $connectionVariables[] = 'SET @@group_concat_max_len = CAST(IF(@@group_concat_max_len > 320000, @@group_concat_max_len, 320000) AS UNSIGNED)';
  303.                 $connectionVariables[] = 'SET sql_mode=(SELECT REPLACE(@@sql_mode,\'ONLY_FULL_GROUP_BY\',\'\'))';
  304.             }
  305.             /**
  306.              * @deprecated tag:v6.5.0 - old trigger logic is removed, therefore we don't need all those connection variables
  307.              */
  308.             $nonDestructiveMigrations $connection->executeQuery('
  309.                 SELECT `creation_timestamp`
  310.                 FROM `migration`
  311.                 WHERE `update` IS NOT NULL AND `update_destructive` IS NULL
  312.             ')->fetchFirstColumn();
  313.             $activeMigrations $this->container->getParameter('migration.active');
  314.             $activeNonDestructiveMigrations array_intersect($activeMigrations$nonDestructiveMigrations);
  315.             foreach ($activeNonDestructiveMigrations as $migration) {
  316.                 $connectionVariables[] = sprintf(
  317.                     'SET %s = TRUE',
  318.                     sprintf(MigrationStep::MIGRATION_VARIABLE_FORMAT$migration)
  319.                 );
  320.             }
  321.             // end deprecated
  322.             if (empty($connectionVariables)) {
  323.                 return;
  324.             }
  325.             $connection->executeQuery(implode(';'$connectionVariables));
  326.         } catch (\Throwable $_) {
  327.         }
  328.     }
  329.     /**
  330.      * Dumps the preload file to an always known location outside the generated cache folder name
  331.      */
  332.     protected function dumpContainer(ConfigCache $cacheContainerBuilder $containerstring $classstring $baseClass): void
  333.     {
  334.         parent::dumpContainer($cache$container$class$baseClass);
  335.         $cacheDir $this->getCacheDir();
  336.         $cacheName basename($cacheDir);
  337.         $fileName substr(basename($cache->getPath()), 0, -3) . 'preload.php';
  338.         $preloadFile = \dirname($cacheDir) . '/opcache-preload.php';
  339.         $loader = <<<PHP
  340. <?php
  341. require_once __DIR__ . '/#CACHE_PATH#';
  342. PHP;
  343.         file_put_contents($preloadFilestr_replace(
  344.             ['#CACHE_PATH#'],
  345.             [$cacheName '/' $fileName],
  346.             $loader
  347.         ));
  348.     }
  349.     private function addApiRoutes(RoutingConfigurator $routes): void
  350.     {
  351.         $routes->import('.''api');
  352.     }
  353.     private function addBundleRoutes(RoutingConfigurator $routes): void
  354.     {
  355.         foreach ($this->getBundles() as $bundle) {
  356.             if ($bundle instanceof Framework\Bundle) {
  357.                 $bundle->configureRoutes($routes$this->environment);
  358.             }
  359.         }
  360.     }
  361.     private function addBundleOverwrites(RoutingConfigurator $routes): void
  362.     {
  363.         foreach ($this->getBundles() as $bundle) {
  364.             if ($bundle instanceof Framework\Bundle) {
  365.                 $bundle->configureRouteOverwrites($routes$this->environment);
  366.             }
  367.         }
  368.     }
  369.     private function addFallbackRoute(RoutingConfigurator $routes): void
  370.     {
  371.         // detail routes
  372.         $route = new Route('/');
  373.         $route->setMethods(['GET']);
  374.         $route->setDefault('_controller'FallbackController::class . '::rootFallback');
  375.         $route->setDefault(PlatformRequest::ATTRIBUTE_ROUTE_SCOPE, ['storefront']);
  376.         $routes->add('root.fallback'$route->getPath());
  377.     }
  378. }