vendor/shopware/core/Framework/Adapter/Twig/EntityTemplateLoader.php line 40

  1. <?php declare(strict_types=1);
  2. namespace Shopware\Core\Framework\Adapter\Twig;
  3. use Doctrine\DBAL\Connection;
  4. use Shopware\Core\DevOps\Environment\EnvironmentHelper;
  5. use Shopware\Core\Framework\DependencyInjection\CompilerPass\TwigLoaderConfigCompilerPass;
  6. use Shopware\Core\Framework\Log\Package;
  7. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  8. use Symfony\Contracts\Service\ResetInterface;
  9. use Twig\Error\LoaderError;
  10. use Twig\Loader\LoaderInterface;
  11. use Twig\Source;
  12. /**
  13.  * @internal
  14.  */
  15. #[Package('core')]
  16. class EntityTemplateLoader implements LoaderInterfaceEventSubscriberInterfaceResetInterface
  17. {
  18.     /**
  19.      * @var array<string, array<string, array{template: string, updatedAt: \DateTimeInterface|null}|null>>
  20.      */
  21.     private array $databaseTemplateCache = [];
  22.     /**
  23.      * @internal
  24.      */
  25.     public function __construct(
  26.         private readonly Connection $connection,
  27.         private readonly string $environment
  28.     ) {
  29.     }
  30.     public static function getSubscribedEvents(): array
  31.     {
  32.         return ['app_template.written' => 'reset'];
  33.     }
  34.     public function reset(): void
  35.     {
  36.         $this->databaseTemplateCache = [];
  37.     }
  38.     public function getSourceContext(string $name): Source
  39.     {
  40.         $template $this->findDatabaseTemplate($name);
  41.         if (!$template) {
  42.             throw new LoaderError(sprintf('Template "%s" is not defined.'$name));
  43.         }
  44.         return new Source($template['template'], $name);
  45.     }
  46.     public function getCacheKey(string $name): string
  47.     {
  48.         return $name;
  49.     }
  50.     public function isFresh(string $nameint $time): bool
  51.     {
  52.         $template $this->findDatabaseTemplate($name);
  53.         if (!$template) {
  54.             return false;
  55.         }
  56.         return $template['updatedAt'] === null || $template['updatedAt']->getTimestamp() < $time;
  57.     }
  58.     /**
  59.      * @return bool
  60.      */
  61.     public function exists(string $name)
  62.     {
  63.         $template $this->findDatabaseTemplate($name);
  64.         if (!$template) {
  65.             return false;
  66.         }
  67.         return true;
  68.     }
  69.     /**
  70.      * @return array{template: string, updatedAt: \DateTimeInterface|null}|null
  71.      */
  72.     private function findDatabaseTemplate(string $name): ?array
  73.     {
  74.         if (EnvironmentHelper::getVariable('DISABLE_EXTENSIONS'false)) {
  75.             return null;
  76.         }
  77.         /*
  78.          * In dev env app templates are directly loaded over the filesystem
  79.          * @see TwigLoaderConfigCompilerPass::addAppTemplatePaths()
  80.          */
  81.         if ($this->environment === 'dev') {
  82.             return null;
  83.         }
  84.         $templateName $this->splitTemplateName($name);
  85.         $namespace $templateName['namespace'];
  86.         $path $templateName['path'];
  87.         if (empty($this->databaseTemplateCache)) {
  88.             /** @var array<array{path: string, template: string, updatedAt: string|null, namespace: string}> $templates */
  89.             $templates $this->connection->fetchAllAssociative('
  90.                 SELECT
  91.                     `app_template`.`path` AS `path`,
  92.                     `app_template`.`template` AS `template`,
  93.                     `app_template`.`updated_at` AS `updatedAt`,
  94.                     `app`.`name` AS `namespace`
  95.                 FROM `app_template`
  96.                 INNER JOIN `app` ON `app_template`.`app_id` = `app`.`id`
  97.                 WHERE `app_template`.`active` = 1 AND `app`.`active` = 1
  98.             ');
  99.             foreach ($templates as $template) {
  100.                 $this->databaseTemplateCache[$template['path']][$template['namespace']] = [
  101.                     'template' => $template['template'],
  102.                     'updatedAt' => $template['updatedAt'] ? new \DateTimeImmutable($template['updatedAt']) : null,
  103.                 ];
  104.             }
  105.         }
  106.         if (\array_key_exists($path$this->databaseTemplateCache) && \array_key_exists($namespace$this->databaseTemplateCache[$path])) {
  107.             return $this->databaseTemplateCache[$path][$namespace];
  108.         }
  109.         // we have already loaded all DB templates
  110.         // if the namespace is not included return null
  111.         return $this->databaseTemplateCache[$path][$namespace] = null;
  112.     }
  113.     /**
  114.      * @return array{namespace: string, path: string}
  115.      */
  116.     private function splitTemplateName(string $template): array
  117.     {
  118.         // remove static template inheritance prefix
  119.         if (mb_strpos($template'@') !== 0) {
  120.             return ['path' => $template'namespace' => ''];
  121.         }
  122.         // remove "@"
  123.         $template mb_substr($template1);
  124.         $template explode('/'$template);
  125.         $namespace array_shift($template);
  126.         $template implode('/'$template);
  127.         return ['path' => $template'namespace' => $namespace];
  128.     }
  129. }