vendor/shopware/core/Kernel.php line 406

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