vendor/shopware/core/Kernel.php line 191

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