core/lib/Thelia/Core/Thelia.php line 438

  1. <?php
  2. /*
  3.  * This file is part of the Thelia package.
  4.  * http://www.thelia.net
  5.  *
  6.  * (c) OpenStudio <info@thelia.net>
  7.  *
  8.  * For the full copyright and license information, please view the LICENSE
  9.  * file that was distributed with this source code.
  10.  */
  11. namespace Thelia\Core;
  12. /*
  13.  * Root class of Thelia
  14.  *
  15.  * It extends Symfony\Component\HttpKernel\Kernel for changing some features
  16.  *
  17.  *
  18.  * @author Manuel Raynaud <manu@raynaud.io>
  19.  */
  20. use Composer\Autoload\ClassLoader;
  21. use Propel\Runtime\Connection\ConnectionInterface;
  22. use Propel\Runtime\DataFetcher\PDODataFetcher;
  23. use Propel\Runtime\Propel;
  24. use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
  25. use Symfony\Component\Config\FileLocator;
  26. use Symfony\Component\DependencyInjection\ContainerAwareInterface;
  27. use Symfony\Component\DependencyInjection\ContainerBuilder;
  28. use Symfony\Component\DependencyInjection\ContainerInterface;
  29. use Symfony\Component\DependencyInjection\Definition;
  30. use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
  31. use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
  32. use Symfony\Component\DependencyInjection\Reference;
  33. use Symfony\Component\ErrorHandler\Debug;
  34. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  35. use Symfony\Component\Filesystem\Filesystem;
  36. use Symfony\Component\Finder\Finder;
  37. use Symfony\Component\Form\FormExtensionInterface;
  38. use Symfony\Component\HttpFoundation\Request;
  39. use Symfony\Component\HttpKernel\HttpKernelInterface;
  40. use Symfony\Component\HttpKernel\Kernel;
  41. use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
  42. use Symfony\Component\VarExporter\VarExporter;
  43. use Symfony\Contracts\EventDispatcher\Event;
  44. use Thelia\Condition\Implementation\ConditionInterface;
  45. use Thelia\Controller\ControllerInterface;
  46. use Thelia\Core\Archiver\ArchiverInterface;
  47. use Thelia\Core\DependencyInjection\Loader\XmlFileLoader;
  48. use Thelia\Core\DependencyInjection\TheliaContainer;
  49. use Thelia\Core\Event\TheliaEvents;
  50. use Thelia\Core\Hook\BaseHookInterface;
  51. use Thelia\Core\Propel\Schema\SchemaLocator;
  52. use Thelia\Core\Serializer\SerializerInterface;
  53. use Thelia\Core\Template\Element\BaseLoopInterface;
  54. use Thelia\Core\Template\TemplateDefinition;
  55. use Thelia\Core\Template\TemplateHelperInterface;
  56. use Thelia\Core\Translation\Translator;
  57. use Thelia\Coupon\Type\CouponInterface;
  58. use Thelia\Form\FormInterface;
  59. use Thelia\Log\Tlog;
  60. use Thelia\Model\ConfigQuery;
  61. use Thelia\Model\Module;
  62. use Thelia\Model\ModuleQuery;
  63. use Thelia\Module\ModuleManagement;
  64. use TheliaSmarty\Template\SmartyParser;
  65. class Thelia extends Kernel
  66. {
  67.     use MicroKernelTrait;
  68.     public const THELIA_VERSION '2.5.2';
  69.     /** @var SchemaLocator */
  70.     protected $propelSchemaLocator;
  71.     /** @var PropelInitService */
  72.     protected $propelInitService;
  73.     protected $propelConnectionAvailable;
  74.     protected $theliaDatabaseConnection;
  75.     protected $cacheRefresh;
  76.     public function __construct($environment$debug)
  77.     {
  78.         $loader = new ClassLoader();
  79.         $loader->addPsr4(''THELIA_ROOT."var/cache/{$environment}/propel/model");
  80.         $loader->addPsr4('TheliaMain\\'THELIA_ROOT."var/cache/{$environment}/propel/database/TheliaMain");
  81.         $loader->register();
  82.         parent::__construct($environment$debug);
  83.         if ($debug) {
  84.             Debug::enable();
  85.         }
  86.     }
  87.     /** Get the front and back templates "components" directory path.
  88.      *
  89.      */
  90.     public static function getTemplateComponentsDirectories(): array
  91.     {
  92.         return [
  93.             'backOffice\\' => THELIA_TEMPLATE_DIR
  94.                 .TemplateDefinition::BACK_OFFICE_SUBDIR.DS
  95.                 .ConfigQuery::read(TemplateDefinition::BACK_OFFICE_CONFIG_NAME'default').DS
  96.                 .'components',
  97.             'frontOffice\\' => THELIA_TEMPLATE_DIR
  98.                 .TemplateDefinition::FRONT_OFFICE_SUBDIR.DS
  99.                 .ConfigQuery::read(TemplateDefinition::BACK_OFFICE_CONFIG_NAME'default').DS
  100.                 .'components',
  101.         ];
  102.     }
  103.     /**
  104.      * Configures the container.
  105.      *
  106.      * You can register extensions:
  107.      *
  108.      *     $c->extension('framework', [
  109.      *         'secret' => '%secret%'
  110.      *     ]);
  111.      *
  112.      * Or services:
  113.      *
  114.      *     $c->services()->set('halloween', 'FooBundle\HalloweenProvider');
  115.      *
  116.      * Or parameters:
  117.      *
  118.      *     $c->parameters()->set('halloween', 'lot of fun');
  119.      */
  120.     protected function configureContainer(ContainerConfigurator $container): void
  121.     {
  122.         $container->parameters()->set('thelia_front_template'ConfigQuery::read(TemplateDefinition::FRONT_OFFICE_CONFIG_NAME'default'));
  123.         $container->parameters()->set('thelia_admin_template'ConfigQuery::read(TemplateDefinition::BACK_OFFICE_CONFIG_NAME'default'));
  124.         $container->import(__DIR__.'/../Config/Resources/*.yaml');
  125.         $container->import(__DIR__.'/../Config/Resources/{packages}/*.yaml');
  126.         $container->import(__DIR__.'/../Config/Resources/{packages}/'.$this->environment.'/*.yaml');
  127.     }
  128.     protected function configureRoutes(RoutingConfigurator $routes): void
  129.     {
  130.     }
  131.     public static function isInstalled()
  132.     {
  133.         return file_exists(THELIA_CONF_DIR.'database.yml') || (!empty($_SERVER['DB_HOST']));
  134.     }
  135.     protected function checkMySQLConfigurations(ConnectionInterface $con): void
  136.     {
  137.         if (!file_exists($this->getCacheDir().DS.'check_mysql_configurations.php')) {
  138.             $sessionSqlMode = [];
  139.             $canUpdate false;
  140.             $logs = [];
  141.             /** @var PDODataFetcher $result */
  142.             $result $con->query('SELECT VERSION() as version, @@SESSION.sql_mode as session_sql_mode');
  143.             if ($result && $data $result->fetch(\PDO::FETCH_ASSOC)) {
  144.                 $sessionSqlMode explode(','$data['session_sql_mode']);
  145.                 if (empty($sessionSqlMode[0])) {
  146.                     unset($sessionSqlMode[0]);
  147.                 }
  148.                 // MariaDB is not impacted by this problem
  149.                 if (false === strpos($data['version'], 'MariaDB')) {
  150.                     // MySQL 5.6+ compatibility
  151.                     if (version_compare($data['version'], '5.6.0''>=')) {
  152.                         // add NO_ENGINE_SUBSTITUTION
  153.                         if (!\in_array('NO_ENGINE_SUBSTITUTION'$sessionSqlMode)) {
  154.                             $sessionSqlMode[] = 'NO_ENGINE_SUBSTITUTION';
  155.                             $canUpdate true;
  156.                             $logs[] = 'Add sql_mode NO_ENGINE_SUBSTITUTION. Please configure your MySQL server.';
  157.                         }
  158.                         // remove STRICT_TRANS_TABLES
  159.                         if (($key array_search('STRICT_TRANS_TABLES'$sessionSqlMode)) !== false) {
  160.                             unset($sessionSqlMode[$key]);
  161.                             $canUpdate true;
  162.                             $logs[] = 'Remove sql_mode STRICT_TRANS_TABLES. Please configure your MySQL server.';
  163.                         }
  164.                         // remove ONLY_FULL_GROUP_BY
  165.                         if (($key array_search('ONLY_FULL_GROUP_BY'$sessionSqlMode)) !== false) {
  166.                             unset($sessionSqlMode[$key]);
  167.                             $canUpdate true;
  168.                             $logs[] = 'Remove sql_mode ONLY_FULL_GROUP_BY. Please configure your MySQL server.';
  169.                         }
  170.                     }
  171.                 } else {
  172.                     // MariaDB 10.2.4+ compatibility
  173.                     if (version_compare($data['version'], '10.2.4''>=')) {
  174.                         // remove STRICT_TRANS_TABLES
  175.                         if (($key array_search('STRICT_TRANS_TABLES'$sessionSqlMode)) !== false) {
  176.                             unset($sessionSqlMode[$key]);
  177.                             $canUpdate true;
  178.                             $logs[] = 'Remove sql_mode STRICT_TRANS_TABLES. Please configure your MySQL server.';
  179.                         }
  180.                     }
  181.                     if (version_compare($data['version'], '10.1.7''>=')) {
  182.                         if (!\in_array('NO_ENGINE_SUBSTITUTION'$sessionSqlMode)) {
  183.                             $sessionSqlMode[] = 'NO_ENGINE_SUBSTITUTION';
  184.                             $canUpdate true;
  185.                             $logs[] = 'Add sql_mode NO_ENGINE_SUBSTITUTION. Please configure your MySQL server.';
  186.                         }
  187.                     }
  188.                 }
  189.             } else {
  190.                 $logs[] = 'Failed to get MySQL version and sql_mode';
  191.             }
  192.             foreach ($logs as $log) {
  193.                 Tlog::getInstance()->addWarning($log);
  194.             }
  195.             (new Filesystem())->dumpFile(
  196.                 $this->getCacheDir().DS.'check_mysql_configurations.php',
  197.                 '<?php return '.VarExporter::export([
  198.                     'modes' => array_values($sessionSqlMode),
  199.                     'canUpdate' => $canUpdate,
  200.                     'logs' => $logs,
  201.                 ]).';'
  202.             );
  203.         }
  204.         $cache = require $this->getCacheDir().DS.'check_mysql_configurations.php';
  205.         if (!empty($cache['canUpdate'])) {
  206.             if (null === $con->query("SET SESSION sql_mode='".implode(','$cache['modes'])."';")) {
  207.                 throw new \RuntimeException('Failed to set MySQL global and session sql_mode');
  208.             }
  209.         }
  210.     }
  211.     /**
  212.      * Gets the container's base class.
  213.      *
  214.      * All names except Container must be fully qualified.
  215.      */
  216.     protected function getContainerBaseClass(): string
  217.     {
  218.         return TheliaContainer::class;
  219.     }
  220.     protected function initializeContainer(): void
  221.     {
  222.         // initialize Propel, building its cache if necessary
  223.         $this->propelSchemaLocator = new SchemaLocator(
  224.             THELIA_CONF_DIR,
  225.             THELIA_MODULE_DIR
  226.         );
  227.         $this->propelInitService = new PropelInitService(
  228.             $this->getEnvironment(),
  229.             $this->isDebug(),
  230.             $this->getKernelParameters(),
  231.             $this->propelSchemaLocator
  232.         );
  233.         $this->propelConnectionAvailable $this->initializePropelService(false$this->cacheRefresh);
  234.         if ($this->propelConnectionAvailable) {
  235.             $this->theliaDatabaseConnection Propel::getConnection('TheliaMain');
  236.             $this->checkMySQLConfigurations($this->theliaDatabaseConnection);
  237.         }
  238.         parent::initializeContainer();
  239.         $this->getContainer()->set('thelia.propel.schema.locator'$this->propelSchemaLocator);
  240.         $this->getContainer()->set('thelia.propel.init'$this->propelInitService);
  241.     }
  242.     /**
  243.      * {@inheritDoc}
  244.      *
  245.      * @throws \Exception
  246.      */
  247.     public function boot(): void
  248.     {
  249.         parent::boot();
  250.         if ($this->cacheRefresh) {
  251.             $moduleManagement = new ModuleManagement($this->getContainer());
  252.             $moduleManagement->updateModules($this->getContainer());
  253.         }
  254.         /** @var EventDispatcherInterface $eventDispatcher */
  255.         $eventDispatcher $this->getContainer()->get('event_dispatcher');
  256.         if ($this->propelConnectionAvailable) {
  257.             $this->theliaDatabaseConnection->setEventDispatcher($eventDispatcher);
  258.         }
  259.         if (self::isInstalled()) {
  260.             $eventDispatcher->dispatch(new Event(), TheliaEvents::BOOT);
  261.         }
  262.     }
  263.     public function initCacheConfigs(bool $force false): void
  264.     {
  265.         if ($force || !file_exists($this->getCacheDir().DS.'thelia_configs.php')) {
  266.             $caches = [];
  267.             $configs ConfigQuery::create()->find();
  268.             foreach ($configs as $config) {
  269.                 $caches[$config->getName()] = $config->getValue();
  270.             }
  271.             (new Filesystem())->dumpFile(
  272.                 $this->getCacheDir().DS.'thelia_configs.php',
  273.                 '<?php return '.VarExporter::export($caches).';'
  274.             );
  275.         }
  276.         ConfigQuery::initCache(
  277.             require $this->getCacheDir().DS.'thelia_configs.php'
  278.         );
  279.     }
  280.     /**
  281.      * @throws \Throwable
  282.      *
  283.      * @return bool
  284.      */
  285.     public function initializePropelService($forcePropelCacheGeneration, &$cacheRefresh)
  286.     {
  287.         $cacheRefresh false;
  288.         return $this->propelInitService->init($forcePropelCacheGeneration$cacheRefresh);
  289.     }
  290.     /**
  291.      * Add all module's standard templates to the parser environment.
  292.      *
  293.      * @param Definition $parser the parser
  294.      * @param Module     $module the Module
  295.      */
  296.     protected function addStandardModuleTemplatesToParserEnvironment($parser$module): void
  297.     {
  298.         $stdTpls TemplateDefinition::getStandardTemplatesSubdirsIterator();
  299.         foreach ($stdTpls as $templateType => $templateSubdirName) {
  300.             $this->addModuleTemplateToParserEnvironment($parser$module$templateType$templateSubdirName);
  301.         }
  302.     }
  303.     /**
  304.      * Add a module template directory to the parser environment.
  305.      *
  306.      * @param Definition $parser             the parser
  307.      * @param Module     $module             the Module
  308.      * @param string     $templateType       the template type (one of the TemplateDefinition type constants)
  309.      * @param string     $templateSubdirName the template subdirectory name (one of the TemplateDefinition::XXX_SUBDIR constants)
  310.      */
  311.     protected function addModuleTemplateToParserEnvironment($parser$module$templateType$templateSubdirName): void
  312.     {
  313.         // Get template path
  314.         $templateDirectory $module->getAbsoluteTemplateDirectoryPath($templateSubdirName);
  315.         try {
  316.             $templateDirBrowser = new \DirectoryIterator($templateDirectory);
  317.             $code ucfirst($module->getCode());
  318.             /* browse the directory */
  319.             foreach ($templateDirBrowser as $templateDirContent) {
  320.                 /* is it a directory which is not . or .. ? */
  321.                 if ($templateDirContent->isDir() && !$templateDirContent->isDot()) {
  322.                     $parser->addMethodCall(
  323.                         'addTemplateDirectory',
  324.                         [
  325.                             $templateType,
  326.                             $templateDirContent->getFilename(),
  327.                             $templateDirContent->getPathName(),
  328.                             $code,
  329.                         ]
  330.                     );
  331.                 }
  332.             }
  333.         } catch (\UnexpectedValueException $ex) {
  334.             // The directory does not exists, ignore it.
  335.         }
  336.     }
  337.     private function preBoot(): ContainerInterface
  338.     {
  339.         if (!self::isInstalled()) {
  340.             throw new \Exception('Thelia is not installed');
  341.         }
  342.         if ($this->debug) {
  343.             $this->startTime microtime(true);
  344.         }
  345.         if ($this->debug && !isset($_ENV['SHELL_VERBOSITY']) && !isset($_SERVER['SHELL_VERBOSITY'])) {
  346.             putenv('SHELL_VERBOSITY=3');
  347.             $_ENV['SHELL_VERBOSITY'] = 3;
  348.             $_SERVER['SHELL_VERBOSITY'] = 3;
  349.         }
  350.         $this->initializeBundles();
  351.         $this->initializeContainer();
  352.         $container $this->container;
  353.         if ($container->hasParameter('kernel.trusted_hosts') && $trustedHosts $container->getParameter('kernel.trusted_hosts')) {
  354.             Request::setTrustedHosts($trustedHosts);
  355.         }
  356.         if ($container->hasParameter('kernel.trusted_proxies') && $container->hasParameter('kernel.trusted_headers') && $trustedProxies $container->getParameter('kernel.trusted_proxies')) {
  357.             Request::setTrustedProxies(\is_array($trustedProxies) ? $trustedProxies array_map('trim'explode(','$trustedProxies)), $container->getParameter('kernel.trusted_headers'));
  358.         }
  359.         return $container;
  360.     }
  361.     /**
  362.      * @throws \Exception
  363.      */
  364.     public function handle(Request $requestint $type HttpKernelInterface::MAIN_REQUESTbool $catch true): \Symfony\Component\HttpFoundation\Response
  365.     {
  366.         if (!$this->booted) {
  367.             $container $this->container ?? $this->preBoot();
  368.             if ($container->has('http_cache')) {
  369.                 return $container->get('http_cache')->handle($request$type$catch);
  370.             }
  371.         }
  372.         $this->boot();
  373.         return parent::handle($request$type$catch);
  374.     }
  375.     /**
  376.      * Load some configuration
  377.      * Initialize all plugins.
  378.      *
  379.      * @throws \Exception
  380.      */
  381.     protected function loadConfiguration(ContainerBuilder $container): void
  382.     {
  383.         $fileLocator = new FileLocator(__DIR__.'/../Config/Resources');
  384.         $phpLoader = new PhpFileLoader($container$fileLocator);
  385.         $phpLoader->load('services.php');
  386.         $autoconfiguredInterfaces = [
  387.             SerializerInterface::class => 'thelia.serializer',
  388.             ArchiverInterface::class => 'thelia.archiver',
  389.             FormExtensionInterface::class => 'thelia.forms.extension',
  390.             BaseLoopInterface::class => 'thelia.loop',
  391.             ContainerAwareInterface::class => 'thelia.command',
  392.             FormInterface::class => 'thelia.form',
  393.             CouponInterface::class => 'thelia.coupon.addCoupon',
  394.             ConditionInterface::class => 'thelia.coupon.addCondition',
  395.             ControllerInterface::class => 'controller.service_arguments',
  396.         ];
  397.         foreach ($autoconfiguredInterfaces as $interfaceClass => $tag) {
  398.             $container->registerForAutoconfiguration($interfaceClass)
  399.                 ->addTag($tag);
  400.         }
  401.         // We set this particular service with public true to have all of his subscribers after removing type (see TheliaBundle.php)
  402.         $container->registerForAutoconfiguration(BaseHookInterface::class)
  403.             ->addTag('hook.event_listener')
  404.             ->setPublic(true);
  405.         $loader = new XmlFileLoader($container$fileLocator);
  406.         $finder Finder::create()
  407.             ->name('*.xml')
  408.             ->depth(0)
  409.             ->in(__DIR__.'/../Config/Resources');
  410.         /** @var \SplFileInfo $file */
  411.         foreach ($finder as $file) {
  412.             $loader->load($file->getBaseName());
  413.         }
  414.         if (\defined('THELIA_INSTALL_MODE') === false) {
  415.             $modules ModuleQuery::getActivated();
  416.             $translationDirs = [];
  417.             /** @var Module $module */
  418.             foreach ($modules as $module) {
  419.                 try {
  420.                     // In case modules want add configuration
  421.                     \call_user_func([$module->getFullNamespace(), 'loadConfiguration'], $container);
  422.                     $definition = new Definition();
  423.                     $definition->setClass($module->getFullNamespace());
  424.                     $definition->addMethodCall('setContainer', [new Reference('service_container')]);
  425.                     $definition->setPublic(true);
  426.                     $container->setDefinition(
  427.                         'module.'.$module->getCode(),
  428.                         $definition
  429.                     );
  430.                     $compilers \call_user_func([$module->getFullNamespace(), 'getCompilers']);
  431.                     foreach ($compilers as $compiler) {
  432.                         if (\is_array($compiler)) {
  433.                             $container->addCompilerPass($compiler[0], $compiler[1]);
  434.                         } else {
  435.                             $container->addCompilerPass($compiler);
  436.                         }
  437.                     }
  438.                     $loader = new XmlFileLoader($container, new FileLocator($module->getAbsoluteConfigPath()));
  439.                     $loader->load('config.xml''module.'.$module->getCode());
  440.                     $envConfigFileName sprintf('config_%s.xml'$this->environment);
  441.                     $envConfigFile sprintf('%s%s%s'$module->getAbsoluteConfigPath(), DS$envConfigFileName);
  442.                     if (is_file($envConfigFile) && is_readable($envConfigFile)) {
  443.                         $loader->load($envConfigFileName'module.'.$module->getCode());
  444.                     }
  445.                     $templateBasePath $module->getAbsoluteTemplateBasePath();
  446.                     if (is_dir($templateBasePath)) {
  447.                         $container->loadFromExtension('twig', [
  448.                             'paths' => [
  449.                                 $templateBasePath => $module->getCode().'Module',
  450.                             ],
  451.                         ]);
  452.                     }
  453.                 } catch (\Exception $e) {
  454.                     if ($this->debug) {
  455.                         throw $e;
  456.                     }
  457.                     Tlog::getInstance()->addError(
  458.                         sprintf('Failed to load module %s: %s'$module->getCode(), $e->getMessage()),
  459.                         $e
  460.                     );
  461.                 }
  462.             }
  463.             // Register templates 'component' directories in a class loader.
  464.             $templateClassLoader = new ClassLoader();
  465.             foreach (self::getTemplateComponentsDirectories() as $namespace => $resource) {
  466.                 if (is_dir($resource)) {
  467.                     $templateClassLoader->addPsr4($namespace$resource);
  468.                 }
  469.             }
  470.             $templateClassLoader->register();
  471.             $parser $container->getDefinition(SmartyParser::class);
  472.             /** @var \Thelia\Core\Template\TemplateHelperInterface $templateHelper */
  473.             $templateHelper $container->get('thelia.template_helper');
  474.             /** @var Module $module */
  475.             foreach ($modules as $module) {
  476.                 try {
  477.                     $this->loadModuleTranslationDirectories($module$translationDirs$templateHelper);
  478.                     $this->addStandardModuleTemplatesToParserEnvironment($parser$module);
  479.                 } catch (\Exception $e) {
  480.                     if ($this->debug) {
  481.                         throw $e;
  482.                     }
  483.                     Tlog::getInstance()->addError(
  484.                         sprintf('Failed to load module %s: %s'$module->getCode(), $e->getMessage()),
  485.                         $e
  486.                     );
  487.                 }
  488.             }
  489.             // Load core translation
  490.             $translationDirs['core'] = THELIA_LIB.'Config'.DS.'I18n';
  491.             // Load core translation
  492.             $translationDirs[Translator::GLOBAL_FALLBACK_DOMAIN] = THELIA_LOCAL_DIR.'I18n';
  493.             // Standard templates (front, back, pdf, mail)
  494.             /** @var TemplateDefinition $templateDefinition */
  495.             foreach ($templateHelper->getStandardTemplateDefinitions() as $templateDefinition) {
  496.                 // Load parent templates transaltions, the current template translations.
  497.                 $templateList array_merge(
  498.                     $templateDefinition->getParentList(),
  499.                     [$templateDefinition]
  500.                 );
  501.                 /** @var TemplateDefinition $tplDef */
  502.                 foreach ($templateList as $tplDef) {
  503.                     if (is_dir($dir $tplDef->getAbsoluteI18nPath())) {
  504.                         $translationDirs[$tplDef->getTranslationDomain()] = $dir;
  505.                     }
  506.                 }
  507.             }
  508.             if ($translationDirs) {
  509.                 $this->loadTranslation($container$translationDirs);
  510.             }
  511.         }
  512.     }
  513.     /**
  514.      * @param TemplateHelperInterface $templateHelper
  515.      */
  516.     private function loadModuleTranslationDirectories(Module $module, array &$translationDirs$templateHelper): void
  517.     {
  518.         // Core module translation
  519.         if (is_dir($dir $module->getAbsoluteI18nPath())) {
  520.             $translationDirs[$module->getTranslationDomain()] = $dir;
  521.         }
  522.         // Admin includes translation
  523.         if (is_dir($dir $module->getAbsoluteAdminIncludesI18nPath())) {
  524.             $translationDirs[$module->getAdminIncludesTranslationDomain()] = $dir;
  525.         }
  526.         // Module back-office template, if any
  527.         $templates =
  528.             $templateHelper->getList(
  529.                 TemplateDefinition::BACK_OFFICE,
  530.                 $module->getAbsoluteTemplateBasePath()
  531.             );
  532.         foreach ($templates as $template) {
  533.             $translationDirs[$module->getBackOfficeTemplateTranslationDomain($template->getName())] =
  534.                 $module->getAbsoluteBackOfficeI18nTemplatePath($template->getName());
  535.         }
  536.         // Module front-office template, if any
  537.         $templates =
  538.             $templateHelper->getList(
  539.                 TemplateDefinition::FRONT_OFFICE,
  540.                 $module->getAbsoluteTemplateBasePath()
  541.             );
  542.         foreach ($templates as $template) {
  543.             $translationDirs[$module->getFrontOfficeTemplateTranslationDomain($template->getName())] =
  544.                 $module->getAbsoluteFrontOfficeI18nTemplatePath($template->getName());
  545.         }
  546.         // Module pdf template, if any
  547.         $templates =
  548.             $templateHelper->getList(
  549.                 TemplateDefinition::PDF,
  550.                 $module->getAbsoluteTemplateBasePath()
  551.             );
  552.         foreach ($templates as $template) {
  553.             $translationDirs[$module->getPdfTemplateTranslationDomain($template->getName())] =
  554.                 $module->getAbsolutePdfI18nTemplatePath($template->getName());
  555.         }
  556.         // Module email template, if any
  557.         $templates =
  558.             $templateHelper->getList(
  559.                 TemplateDefinition::EMAIL,
  560.                 $module->getAbsoluteTemplateBasePath()
  561.             );
  562.         foreach ($templates as $template) {
  563.             $translationDirs[$module->getEmailTemplateTranslationDomain($template->getName())] =
  564.                 $module->getAbsoluteEmailI18nTemplatePath($template->getName());
  565.         }
  566.     }
  567.     private function loadTranslation(ContainerBuilder $container, array $dirs): void
  568.     {
  569.         $translator $container->getDefinition(Translator::class);
  570.         foreach ($dirs as $domain => $dir) {
  571.             try {
  572.                 $finder Finder::create()
  573.                     ->files()
  574.                     ->depth(0)
  575.                     ->in($dir);
  576.                 /** @var \DirectoryIterator $file */
  577.                 foreach ($finder as $file) {
  578.                     [$locale$format] = explode('.'$file->getBaseName(), 2);
  579.                     $translator->addMethodCall('addResource', [$format, (string) $file$locale$domain]);
  580.                 }
  581.             } catch (\InvalidArgumentException $ex) {
  582.                 // Ignore missing I18n directories
  583.                 Tlog::getInstance()->addWarning("loadTranslation: missing $dir directory");
  584.             }
  585.         }
  586.     }
  587.     /**
  588.      * Builds the service container.
  589.      *
  590.      * @throws \Exception
  591.      *
  592.      * @return ContainerBuilder The compiled service container
  593.      */
  594.     protected function buildContainer(): ContainerBuilder
  595.     {
  596.         $container parent::buildContainer();
  597.         $this->loadConfiguration($container);
  598.         return $container;
  599.     }
  600.     /**
  601.      * Gets the cache directory.
  602.      *
  603.      * @return string The cache directory
  604.      *
  605.      * @api
  606.      */
  607.     public function getCacheDir(): string
  608.     {
  609.         if (\defined('THELIA_ROOT')) {
  610.             return THELIA_CACHE_DIR.$this->environment;
  611.         }
  612.         return parent::getCacheDir();
  613.     }
  614.     /**
  615.      * Gets the log directory.
  616.      *
  617.      * @return string The log directory
  618.      *
  619.      * @api
  620.      */
  621.     public function getLogDir(): string
  622.     {
  623.         if (\defined('THELIA_ROOT')) {
  624.             return THELIA_LOG_DIR;
  625.         }
  626.         return parent::getLogDir();
  627.     }
  628.     /**
  629.      * Returns the kernel parameters.
  630.      *
  631.      * @return array An array of kernel parameters
  632.      */
  633.     protected function getKernelParameters(): array
  634.     {
  635.         $parameters parent::getKernelParameters();
  636.         // Todo replace this by real runtime env
  637.         $parameters['kernel.runtime_environment'] = $this->environment;
  638.         $parameters['thelia.root_dir'] = THELIA_ROOT;
  639.         $parameters['thelia.core_dir'] = \dirname(__DIR__); // This class is in core/lib/Thelia/Core.
  640.         $parameters['thelia.module_dir'] = THELIA_MODULE_DIR;
  641.         $parameters['thelia.db_host'] = $_SERVER['DB_HOST'] ?? null;
  642.         $parameters['thelia.db_port'] = $_SERVER['DB_PORT'] ?? null;
  643.         $parameters['thelia.db_name'] = $_SERVER['DB_NAME'] ?? null;
  644.         $parameters['thelia.db_user'] = $_SERVER['DB_USER'] ?? null;
  645.         $parameters['thelia.db_password'] = $_SERVER['DB_PASSWORD'] ?? null;
  646.         return $parameters;
  647.     }
  648.     /**
  649.      * {@inheritdoc}
  650.      */
  651.     public function registerBundles(): iterable
  652.     {
  653.         $contents = [
  654.             Bundle\TheliaBundle::class => ['all' => true],
  655.         ];
  656.         if (file_exists(THELIA_ROOT.'config/bundles.php')) {
  657.             $contents array_merge($contents, require THELIA_ROOT.'config/bundles.php');
  658.         }
  659.         foreach ($contents as $class => $envs) {
  660.             if ($envs[$this->environment] ?? $envs['all'] ?? false) {
  661.                 yield new $class();
  662.             }
  663.         }
  664.     }
  665. }