vendor/contao/core-bundle/src/Resources/contao/library/Contao/TemplateInheritance.php line 357

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of Contao.
  4.  *
  5.  * (c) Leo Feyer
  6.  *
  7.  * @license LGPL-3.0-or-later
  8.  */
  9. namespace Contao;
  10. use Contao\CoreBundle\Framework\ContaoFramework;
  11. use Symfony\Component\DependencyInjection\ContainerInterface;
  12. /**
  13.  * Provides the template inheritance logic
  14.  */
  15. trait TemplateInheritance
  16. {
  17.     /**
  18.      * Template file
  19.      * @var string
  20.      */
  21.     protected $strTemplate;
  22.     /**
  23.      * Parent template
  24.      * @var string
  25.      */
  26.     protected $strParent;
  27.     /**
  28.      * Default template
  29.      * @var string
  30.      */
  31.     protected $strDefault;
  32.     /**
  33.      * Output format
  34.      * @var string
  35.      */
  36.     protected $strFormat 'html5';
  37.     /**
  38.      * Tag ending
  39.      * @var string
  40.      */
  41.     protected $strTagEnding '>';
  42.     /**
  43.      * Blocks
  44.      * @var array
  45.      */
  46.     protected $arrBlocks = array();
  47.     /**
  48.      * Block names
  49.      * @var array
  50.      */
  51.     protected $arrBlockNames = array();
  52.     /**
  53.      * Buffer level
  54.      * @var int
  55.      */
  56.     protected $intBufferLevel 0;
  57.     /**
  58.      * @var bool|null
  59.      */
  60.     protected $blnDebug;
  61.     /**
  62.      * Parse the template file and return it as string
  63.      *
  64.      * @return string The template markup
  65.      */
  66.     public function inherit()
  67.     {
  68.         if (null !== ($result $this->renderTwigSurrogateIfExists()))
  69.         {
  70.             return $result;
  71.         }
  72.         $strBuffer '';
  73.         // Start with the template itself
  74.         $this->strParent $this->strTemplate;
  75.         // Include the parent templates
  76.         while ($this->strParent !== null)
  77.         {
  78.             $strCurrent $this->strParent;
  79.             $strParent $this->strDefault ?: $this->getTemplatePath($this->strParent$this->strFormat);
  80.             // Reset the flags
  81.             $this->strParent null;
  82.             $this->strDefault null;
  83.             ob_start();
  84.             $this->intBufferLevel 1;
  85.             try
  86.             {
  87.                 if (file_exists($strParent))
  88.                 {
  89.                     include $strParent;
  90.                 }
  91.                 else
  92.                 {
  93.                     System::getContainer()->get('monolog.logger.contao.error')->error('Invalid template path: ' StringUtil::stripRootDir($strParent));
  94.                 }
  95.                 // Capture the output of the root template
  96.                 if ($this->strParent === null)
  97.                 {
  98.                     $strBuffer ob_get_contents();
  99.                 }
  100.                 elseif ($this->strParent == $strCurrent)
  101.                 {
  102.                     $this->strDefault $this->getTemplatePath($this->strParent$this->strFormattrue);
  103.                 }
  104.             }
  105.             finally
  106.             {
  107.                 for ($i=0$i<$this->intBufferLevel$i++)
  108.                 {
  109.                     ob_end_clean();
  110.                 }
  111.             }
  112.         }
  113.         // Reset the internal arrays
  114.         $this->arrBlocks = array();
  115.         $blnDebug $this->blnDebug;
  116.         if ($blnDebug === null)
  117.         {
  118.             $blnDebug System::getContainer()->getParameter('kernel.debug');
  119.             // Backwards compatibility
  120.             if ($blnDebug !== (bool) ($GLOBALS['TL_CONFIG']['debugMode'] ?? false))
  121.             {
  122.                 trigger_deprecation('contao/core-bundle''4.12''Dynamically setting TL_CONFIG.debugMode has been deprecated. Use %s::setDebug() instead.'__CLASS__);
  123.                 $blnDebug = (bool) ($GLOBALS['TL_CONFIG']['debugMode'] ?? false);
  124.             }
  125.         }
  126.         // Add start and end markers in debug mode
  127.         if ($blnDebug)
  128.         {
  129.             $strRelPath StringUtil::stripRootDir($this->getTemplatePath($this->strTemplate$this->strFormat));
  130.             $strBuffer "\n<!-- TEMPLATE START: $strRelPath -->\n$strBuffer\n<!-- TEMPLATE END: $strRelPath -->\n";
  131.         }
  132.         return $strBuffer;
  133.     }
  134.     public function setDebug(bool $debug null): self
  135.     {
  136.         $this->blnDebug $debug;
  137.         return $this;
  138.     }
  139.     /**
  140.      * Extend another template
  141.      *
  142.      * @param string $name The template name
  143.      */
  144.     public function extend($name)
  145.     {
  146.         $this->strParent $name;
  147.     }
  148.     /**
  149.      * Insert the content of the parent block
  150.      */
  151.     public function parent()
  152.     {
  153.         $nonce ContaoFramework::getNonce();
  154.         echo "[[TL_PARENT_$nonce]]";
  155.     }
  156.     /**
  157.      * Start a new block
  158.      *
  159.      * @param string $name The block name
  160.      *
  161.      * @throws \Exception If a child templates contains nested blocks
  162.      */
  163.     public function block($name)
  164.     {
  165.         $this->arrBlockNames[] = $name;
  166.         $nonce ContaoFramework::getNonce();
  167.         // Root template
  168.         if ($this->strParent === null)
  169.         {
  170.             // Register the block name
  171.             if (!isset($this->arrBlocks[$name]))
  172.             {
  173.                 $this->arrBlocks[$name] = "[[TL_PARENT_$nonce]]";
  174.             }
  175.             // Combine the contents of the child blocks
  176.             elseif (\is_array($this->arrBlocks[$name]))
  177.             {
  178.                 $callback = static function ($current$parent) use ($nonce)
  179.                 {
  180.                     return str_replace("[[TL_PARENT_$nonce]]"$parent$current);
  181.                 };
  182.                 $this->arrBlocks[$name] = array_reduce($this->arrBlocks[$name], $callback"[[TL_PARENT_$nonce]]");
  183.             }
  184.             // Handle nested blocks
  185.             if ($this->arrBlocks[$name] != "[[TL_PARENT_$nonce]]")
  186.             {
  187.                 // Output everything before the first TL_PARENT tag
  188.                 if (strpos($this->arrBlocks[$name], "[[TL_PARENT_$nonce]]") !== false)
  189.                 {
  190.                     list($content) = explode("[[TL_PARENT_$nonce]]"$this->arrBlocks[$name], 2);
  191.                     echo $content;
  192.                 }
  193.                 // Output the current block and start a new output buffer to remove the following blocks
  194.                 else
  195.                 {
  196.                     echo $this->arrBlocks[$name];
  197.                     ob_start();
  198.                     ++$this->intBufferLevel;
  199.                 }
  200.             }
  201.         }
  202.         // Child template
  203.         else
  204.         {
  205.             // Clean the output buffer
  206.             ob_clean();
  207.             // Check for nested blocks
  208.             if (\count($this->arrBlockNames) > 1)
  209.             {
  210.                 throw new \Exception('Nested blocks are not allowed in child templates');
  211.             }
  212.         }
  213.     }
  214.     /**
  215.      * End a block
  216.      *
  217.      * @throws \Exception If there is no open block
  218.      */
  219.     public function endblock()
  220.     {
  221.         // Check for open blocks
  222.         if (empty($this->arrBlockNames))
  223.         {
  224.             throw new \Exception('You must start a block before you can end it');
  225.         }
  226.         // Get the block name
  227.         $name array_pop($this->arrBlockNames);
  228.         // Root template
  229.         if ($this->strParent === null)
  230.         {
  231.             $nonce ContaoFramework::getNonce();
  232.             // Handle nested blocks
  233.             if ($this->arrBlocks[$name] != "[[TL_PARENT_$nonce]]")
  234.             {
  235.                 // Output everything after the first TL_PARENT tag
  236.                 if (strpos($this->arrBlocks[$name], "[[TL_PARENT_$nonce]]") !== false)
  237.                 {
  238.                     list(, $content) = explode("[[TL_PARENT_$nonce]]"$this->arrBlocks[$name], 2);
  239.                     echo $content;
  240.                 }
  241.                 // Remove the overwritten content
  242.                 else
  243.                 {
  244.                     ob_end_clean();
  245.                     --$this->intBufferLevel;
  246.                 }
  247.             }
  248.         }
  249.         // Child template
  250.         else
  251.         {
  252.             // Capture the block content
  253.             $this->arrBlocks[$name][] = ob_get_clean();
  254.             // Start a new output buffer
  255.             ob_start();
  256.         }
  257.     }
  258.     /**
  259.      * Insert a template
  260.      *
  261.      * @param string $name The template name
  262.      * @param array  $data An optional data array
  263.      */
  264.     public function insert($name, array $data=null)
  265.     {
  266.         /** @var Template $tpl */
  267.         if ($this instanceof Template)
  268.         {
  269.             $tpl = new static($name);
  270.         }
  271.         elseif (TL_MODE == 'BE')
  272.         {
  273.             $tpl = new BackendTemplate($name);
  274.         }
  275.         else
  276.         {
  277.             $tpl = new FrontendTemplate($name);
  278.         }
  279.         if ($data !== null)
  280.         {
  281.             $tpl->setData($data);
  282.         }
  283.         echo $tpl->parse();
  284.     }
  285.     /**
  286.      * Find a particular template file and return its path
  287.      *
  288.      * @param string  $strTemplate The name of the template
  289.      * @param string  $strFormat   The file extension
  290.      * @param boolean $blnDefault  If true, the default template path is returned
  291.      *
  292.      * @return string The path to the template file
  293.      */
  294.     protected function getTemplatePath($strTemplate$strFormat='html5'$blnDefault=false)
  295.     {
  296.         if ($blnDefault)
  297.         {
  298.             return TemplateLoader::getDefaultPath($strTemplate$strFormat);
  299.         }
  300.         return Controller::getTemplate($strTemplate);
  301.     }
  302.     /**
  303.      * Render a Twig template if one exists
  304.      */
  305.     protected function renderTwigSurrogateIfExists(): ?string
  306.     {
  307.         $container System::getContainer();
  308.         if (null === ($twig $container->get('twig'ContainerInterface::NULL_ON_INVALID_REFERENCE)))
  309.         {
  310.             return null;
  311.         }
  312.         $templateCandidate "@Contao/$this->strTemplate.html.twig";
  313.         if (!$twig->getLoader()->exists($templateCandidate))
  314.         {
  315.             return null;
  316.         }
  317.         $contextFactory $container->get('contao.twig.interop.context_factory');
  318.         $context $this instanceof Template ?
  319.             $contextFactory->fromContaoTemplate($this) :
  320.             $contextFactory->fromClass($this)
  321.         ;
  322.         return $twig->render($templateCandidate$context);
  323.     }
  324. }
  325. class_alias(TemplateInheritance::class, 'TemplateInheritance');