vendor/shopware/core/Framework/Adapter/Twig/TemplateFinder.php line 103

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Shopware\Core\Framework\Adapter\Twig;
  3. use Shopware\Core\Framework\Adapter\Twig\NamespaceHierarchy\NamespaceHierarchyBuilder;
  4. use Symfony\Contracts\Service\ResetInterface;
  5. use Twig\Cache\FilesystemCache;
  6. use Twig\Environment;
  7. use Twig\Error\LoaderError;
  8. use Twig\Loader\LoaderInterface;
  9. class TemplateFinder implements TemplateFinderInterfaceResetInterface
  10. {
  11.     private Environment $twig;
  12.     private LoaderInterface $loader;
  13.     /**
  14.      * @var string[]
  15.      */
  16.     private array $namespaceHierarchy = [];
  17.     private string $cacheDir;
  18.     private NamespaceHierarchyBuilder $namespaceHierarchyBuilder;
  19.     /**
  20.      * @internal
  21.      */
  22.     public function __construct(
  23.         Environment $twig,
  24.         LoaderInterface $loader,
  25.         string $cacheDir,
  26.         NamespaceHierarchyBuilder $namespaceHierarchyBuilder
  27.     ) {
  28.         $this->twig $twig;
  29.         $this->loader $loader;
  30.         $this->cacheDir $cacheDir '/twig';
  31.         $this->namespaceHierarchyBuilder $namespaceHierarchyBuilder;
  32.     }
  33.     public function getTemplateName(string $template): string
  34.     {
  35.         //remove static template inheritance prefix
  36.         if (mb_strpos($template'@') !== 0) {
  37.             return $template;
  38.         }
  39.         $template explode('/'$template);
  40.         array_shift($template);
  41.         $template implode('/'$template);
  42.         return $template;
  43.     }
  44.     /**
  45.      * {@inheritdoc}
  46.      */
  47.     public function find(string $template$ignoreMissing false, ?string $source null): string
  48.     {
  49.         $templatePath $this->getTemplateName($template);
  50.         $sourcePath $source $this->getTemplateName($source) : null;
  51.         $sourceBundleName $source $this->getSourceBundleName($source) : null;
  52.         $originalTemplate $source null $template;
  53.         $queue $this->getNamespaceHierarchy();
  54.         $modifiedQueue $queue;
  55.         // If we are trying to load the same file as the template, we do are not allowed to search the hierarchy
  56.         // up to the source file as that has already been searched and that would lead to an endless template inheritance.
  57.         if ($sourceBundleName !== null && $sourcePath === $templatePath) {
  58.             $index \array_search($sourceBundleName$modifiedQueuetrue);
  59.             if (\is_int($index)) {
  60.                 $modifiedQueue \array_merge(\array_slice($modifiedQueue$index 1), \array_slice($queue0$index));
  61.             }
  62.         }
  63.         // iterate over all bundles but exclude the originally requested bundle
  64.         // example: if @Storefront/storefront/index.html.twig is requested, all bundles except Storefront will be checked first
  65.         foreach ($modifiedQueue as $prefix) {
  66.             $name '@' $prefix '/' $templatePath;
  67.             // original template is loaded last
  68.             if ($name === $originalTemplate) {
  69.                 continue;
  70.             }
  71.             if (!$this->loader->exists($name)) {
  72.                 continue;
  73.             }
  74.             return $name;
  75.         }
  76.         // Throw a useful error when the template cannot be found
  77.         if ($originalTemplate === null) {
  78.             if ($ignoreMissing === true) {
  79.                 return $templatePath;
  80.             }
  81.             throw new LoaderError(sprintf('Unable to load template "%s". (Looked into: %s)'$templatePathimplode(', 'array_values($modifiedQueue))));
  82.         }
  83.         // if no other bundle extends the requested template, load the original template
  84.         if ($this->loader->exists($originalTemplate)) {
  85.             return $originalTemplate;
  86.         }
  87.         if ($ignoreMissing === true) {
  88.             return $templatePath;
  89.         }
  90.         throw new LoaderError(sprintf('Unable to load template "%s". (Looked into: %s)'$templatePathimplode(', 'array_values($modifiedQueue))));
  91.     }
  92.     public function reset(): void
  93.     {
  94.         $this->namespaceHierarchy = [];
  95.     }
  96.     private function getSourceBundleName(string $source): ?string
  97.     {
  98.         if (mb_strpos($source'@') !== 0) {
  99.             return null;
  100.         }
  101.         $source explode('/'$source);
  102.         $source array_shift($source);
  103.         $source $source ltrim($source'@') : null;
  104.         return $source ?: null;
  105.     }
  106.     /**
  107.      * @return string[]
  108.      */
  109.     private function getNamespaceHierarchy(): array
  110.     {
  111.         if ($this->namespaceHierarchy) {
  112.             return $this->namespaceHierarchy;
  113.         }
  114.         $namespaceHierarchy $this->namespaceHierarchyBuilder->buildHierarchy();
  115.         $this->defineCache($namespaceHierarchy);
  116.         return $this->namespaceHierarchy array_keys($namespaceHierarchy);
  117.     }
  118.     /**
  119.      * @param string[] $queue
  120.      */
  121.     private function defineCache(array $queue): void
  122.     {
  123.         if ($this->twig->getCache(false) instanceof FilesystemCache) {
  124.             $configHash md5((string) json_encode($queue));
  125.             $fileSystemCache = new ConfigurableFilesystemCache($this->cacheDir);
  126.             $fileSystemCache->setConfigHash($configHash);
  127.             // Set individual twig cache for different configurations
  128.             $this->twig->setCache($fileSystemCache);
  129.         }
  130.     }
  131. }