vendor/shopware/core/Kernel.php line 403

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Shopware\Core;
  3. use Doctrine\DBAL\Connection;
  4. use Doctrine\DBAL\FetchMode;
  5. use Shopware\Core\DevOps\Environment\EnvironmentHelper;
  6. use Shopware\Core\Framework\Adapter\Database\MySQLFactory;
  7. use Shopware\Core\Framework\Api\Controller\FallbackController;
  8. use Shopware\Core\Framework\Migration\MigrationStep;
  9. use Shopware\Core\Framework\Plugin\KernelPluginLoader\KernelPluginLoader;
  10. use Shopware\Core\Maintenance\Maintenance;
  11. use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
  12. use Symfony\Component\Config\ConfigCache;
  13. use Symfony\Component\Config\Loader\LoaderInterface;
  14. use Symfony\Component\DependencyInjection\ContainerBuilder;
  15. use Symfony\Component\HttpKernel\Bundle\BundleInterface;
  16. use Symfony\Component\HttpKernel\Kernel as HttpKernel;
  17. use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
  18. use Symfony\Component\Routing\Route;
  19. class Kernel extends HttpKernel
  20. {
  21.     use MicroKernelTrait;
  22.     /**
  23.      * @internal
  24.      *
  25.      * @deprecated tag:v6.5.0 The connection requirements should be fixed
  26.      */
  27.     public const PLACEHOLDER_DATABASE_URL 'mysql://_placeholder.test';
  28.     public const CONFIG_EXTS '.{php,xml,yaml,yml}';
  29.     /**
  30.      * @var string Fallback version if nothing is provided via kernel constructor
  31.      */
  32.     public const SHOPWARE_FALLBACK_VERSION '6.4.9999999.9999999-dev';
  33.     /**
  34.      * @var string Regex pattern for validating Shopware versions
  35.      */
  36.     private const VALID_VERSION_PATTERN '#^\d\.\d+\.\d+\.(\d+|x)(-\w+)?#';
  37.     /**
  38.      * @var Connection|null
  39.      */
  40.     protected static $connection;
  41.     /**
  42.      * @var KernelPluginLoader
  43.      */
  44.     protected $pluginLoader;
  45.     /**
  46.      * @var string
  47.      */
  48.     protected $shopwareVersion;
  49.     /**
  50.      * @var string|null
  51.      */
  52.     protected $shopwareVersionRevision;
  53.     /**
  54.      * @var string|null
  55.      */
  56.     protected $projectDir;
  57.     private bool $rebooting false;
  58.     private string $cacheId;
  59.     /**
  60.      * {@inheritdoc}
  61.      */
  62.     public function __construct(
  63.         string $environment,
  64.         bool $debug,
  65.         KernelPluginLoader $pluginLoader,
  66.         string $cacheId,
  67.         ?string $version self::SHOPWARE_FALLBACK_VERSION,
  68.         ?Connection $connection null,
  69.         ?string $projectDir null
  70.     ) {
  71.         date_default_timezone_set('UTC');
  72.         parent::__construct($environment$debug);
  73.         self::$connection $connection;
  74.         $this->pluginLoader $pluginLoader;
  75.         $this->parseShopwareVersion($version);
  76.         $this->cacheId $cacheId;
  77.         $this->projectDir $projectDir;
  78.     }
  79.     /**
  80.      * @return \Generator<BundleInterface>
  81.      *
  82.      * @deprecated tag:v6.5.0 - reason:return-type-change -  The return type will be native
  83.      */
  84.     public function registerBundles()/*: \Generator*/
  85.     {
  86.         /** @var array $bundles */
  87.         $bundles = require $this->getProjectDir() . '/config/bundles.php';
  88.         $instanciatedBundleNames = [];
  89.         /** @var class-string<\Symfony\Component\HttpKernel\Bundle\Bundle> $class */
  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.     protected function getKernelParameters(): array
  237.     {
  238.         $parameters parent::getKernelParameters();
  239.         $activePluginMeta = [];
  240.         foreach ($this->pluginLoader->getPluginInstances()->getActives() as $plugin) {
  241.             $class \get_class($plugin);
  242.             $activePluginMeta[$class] = [
  243.                 'name' => $plugin->getName(),
  244.                 'path' => $plugin->getPath(),
  245.                 'class' => $class,
  246.             ];
  247.         }
  248.         $pluginDir $this->pluginLoader->getPluginDir($this->getProjectDir());
  249.         $coreDir \dirname((string) (new \ReflectionClass(self::class))->getFileName());
  250.         return array_merge(
  251.             $parameters,
  252.             [
  253.                 'kernel.cache.hash' => $this->getCacheHash(),
  254.                 'kernel.shopware_version' => $this->shopwareVersion,
  255.                 'kernel.shopware_version_revision' => $this->shopwareVersionRevision,
  256.                 'kernel.shopware_core_dir' => $coreDir,
  257.                 'kernel.plugin_dir' => $pluginDir,
  258.                 'kernel.app_dir' => rtrim($this->getProjectDir(), '/') . '/custom/apps',
  259.                 'kernel.active_plugins' => $activePluginMeta,
  260.                 'kernel.plugin_infos' => $this->pluginLoader->getPluginInfos(),
  261.                 'kernel.supported_api_versions' => [234],
  262.                 'defaults_bool_true' => true,
  263.                 'defaults_bool_false' => false,
  264.                 'default_whitespace' => ' ',
  265.             ]
  266.         );
  267.     }
  268.     protected function getCacheHash(): string
  269.     {
  270.         $plugins = [];
  271.         foreach ($this->pluginLoader->getPluginInfos() as $plugin) {
  272.             if ($plugin['active'] === false) {
  273.                 continue;
  274.             }
  275.             $plugins[$plugin['name']] = $plugin['version'];
  276.         }
  277.         $pluginHash md5((string) json_encode($plugins\JSON_THROW_ON_ERROR));
  278.         return md5((string) \json_encode([
  279.             $this->cacheId,
  280.             substr((string) $this->shopwareVersionRevision08),
  281.             substr($pluginHash08),
  282.             EnvironmentHelper::getVariable('DATABASE_URL'''),
  283.         ], \JSON_THROW_ON_ERROR));
  284.     }
  285.     protected function initializeDatabaseConnectionVariables(): void
  286.     {
  287.         $shopwareSkipConnectionVariables EnvironmentHelper::getVariable('SHOPWARE_SKIP_CONNECTION_VARIABLES'false);
  288.         if ($shopwareSkipConnectionVariables) {
  289.             return;
  290.         }
  291.         $connection self::getConnection();
  292.         try {
  293.             $nonDestructiveMigrations $connection->executeQuery('
  294.             SELECT `creation_timestamp`
  295.             FROM `migration`
  296.             WHERE `update` IS NOT NULL AND `update_destructive` IS NULL
  297.         ')->fetchAll(FetchMode::COLUMN);
  298.             $activeMigrations $this->container->getParameter('migration.active');
  299.             $activeNonDestructiveMigrations array_intersect($activeMigrations$nonDestructiveMigrations);
  300.             $setSessionVariables = (bool) EnvironmentHelper::getVariable('SQL_SET_DEFAULT_SESSION_VARIABLES'true);
  301.             $connectionVariables = [];
  302.             $timeZoneSupportEnabled = (bool) EnvironmentHelper::getVariable('SHOPWARE_DBAL_TIMEZONE_SUPPORT_ENABLED'false);
  303.             if ($timeZoneSupportEnabled) {
  304.                 $connectionVariables[] = 'SET @@session.time_zone = "UTC"';
  305.             }
  306.             if ($setSessionVariables) {
  307.                 $connectionVariables[] = 'SET @@group_concat_max_len = CAST(IF(@@group_concat_max_len > 320000, @@group_concat_max_len, 320000) AS UNSIGNED)';
  308.                 $connectionVariables[] = "SET sql_mode=(SELECT REPLACE(@@sql_mode,'ONLY_FULL_GROUP_BY',''))";
  309.             }
  310.             foreach ($activeNonDestructiveMigrations as $migration) {
  311.                 $connectionVariables[] = sprintf(
  312.                     'SET %s = TRUE',
  313.                     sprintf(MigrationStep::MIGRATION_VARIABLE_FORMAT$migration)
  314.                 );
  315.             }
  316.             if (empty($connectionVariables)) {
  317.                 return;
  318.             }
  319.             $connection->executeQuery(implode(';'$connectionVariables));
  320.         } catch (\Throwable $_) {
  321.         }
  322.     }
  323.     /**
  324.      * Dumps the preload file to an always known location outside the generated cache folder name
  325.      */
  326.     protected function dumpContainer(ConfigCache $cacheContainerBuilder $containerstring $classstring $baseClass): void
  327.     {
  328.         parent::dumpContainer($cache$container$class$baseClass);
  329.         $cacheDir $this->getCacheDir();
  330.         $cacheName basename($cacheDir);
  331.         $fileName substr(basename($cache->getPath()), 0, -3) . 'preload.php';
  332.         $preloadFile \dirname($cacheDir) . '/opcache-preload.php';
  333.         $loader = <<<PHP
  334. <?php
  335. require_once __DIR__ . '/#CACHE_PATH#';
  336. PHP;
  337.         file_put_contents($preloadFilestr_replace(
  338.             ['#CACHE_PATH#'],
  339.             [$cacheName '/' $fileName],
  340.             $loader
  341.         ));
  342.     }
  343.     private function addApiRoutes(RoutingConfigurator $routes): void
  344.     {
  345.         $routes->import('.''api');
  346.     }
  347.     private function addBundleRoutes(RoutingConfigurator $routes): void
  348.     {
  349.         foreach ($this->getBundles() as $bundle) {
  350.             if ($bundle instanceof Framework\Bundle) {
  351.                 $bundle->configureRoutes($routes$this->environment);
  352.             }
  353.         }
  354.     }
  355.     private function addBundleOverwrites(RoutingConfigurator $routes): void
  356.     {
  357.         foreach ($this->getBundles() as $bundle) {
  358.             if ($bundle instanceof Framework\Bundle) {
  359.                 $bundle->configureRouteOverwrites($routes$this->environment);
  360.             }
  361.         }
  362.     }
  363.     private function addFallbackRoute(RoutingConfigurator $routes): void
  364.     {
  365.         // detail routes
  366.         $route = new Route('/');
  367.         $route->setMethods(['GET']);
  368.         $route->setDefault('_controller'FallbackController::class . '::rootFallback');
  369.         $route->setDefault(PlatformRequest::ATTRIBUTE_ROUTE_SCOPE, ['storefront']);
  370.         $routes->add('root.fallback'$route->getPath());
  371.     }
  372.     private function parseShopwareVersion(?string $version): void
  373.     {
  374.         // does not come from composer, was set manually
  375.         if ($version === null || mb_strpos($version'@') === false) {
  376.             $this->shopwareVersion self::SHOPWARE_FALLBACK_VERSION;
  377.             $this->shopwareVersionRevision str_repeat('0'32);
  378.             return;
  379.         }
  380.         [$version$hash] = explode('@'$version);
  381.         $version ltrim($version'v');
  382.         $version str_replace('+''-'$version);
  383.         /*
  384.          * checks if the version is a valid version pattern
  385.          * Shopware\Core\Framework\Test\KernelTest::testItCreatesShopwareVersion()
  386.          */
  387.         if (!preg_match(self::VALID_VERSION_PATTERN$version)) {
  388.             $version self::SHOPWARE_FALLBACK_VERSION;
  389.         }
  390.         $this->shopwareVersion $version;
  391.         $this->shopwareVersionRevision $hash;
  392.     }
  393. }