vendor/twig/twig/src/Node/ModuleNode.php line 87

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of Twig.
  4.  *
  5.  * (c) Fabien Potencier
  6.  * (c) Armin Ronacher
  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 Twig\Node;
  12. use Twig\Attribute\YieldReady;
  13. use Twig\Compiler;
  14. use Twig\Node\Expression\AbstractExpression;
  15. use Twig\Node\Expression\ConstantExpression;
  16. use Twig\Source;
  17. /**
  18.  * Represents a module node.
  19.  *
  20.  * If you need to customize the behavior of the generated class, add nodes to
  21.  * the following nodes: display_start, display_end, constructor_start,
  22.  * constructor_end, and class_end.
  23.  *
  24.  * @author Fabien Potencier <[email protected]>
  25.  */
  26. #[YieldReady]
  27. final class ModuleNode extends Node
  28. {
  29.     /**
  30.      * @param BodyNode $body
  31.      */
  32.     public function __construct(Node $body, ?AbstractExpression $parentNode $blocksNode $macrosNode $traits$embeddedTemplatesSource $source)
  33.     {
  34.         if (!$body instanceof BodyNode) {
  35.             trigger_deprecation('twig/twig''3.12'\sprintf('Not passing a "%s" instance as the "body" argument of the "%s" constructor is deprecated.'BodyNode::class, static::class));
  36.         }
  37.         $nodes = [
  38.             'body' => $body,
  39.             'blocks' => $blocks,
  40.             'macros' => $macros,
  41.             'traits' => $traits,
  42.             'display_start' => new Nodes(),
  43.             'display_end' => new Nodes(),
  44.             'constructor_start' => new Nodes(),
  45.             'constructor_end' => new Nodes(),
  46.             'class_end' => new Nodes(),
  47.         ];
  48.         if (null !== $parent) {
  49.             $nodes['parent'] = $parent;
  50.         }
  51.         // embedded templates are set as attributes so that they are only visited once by the visitors
  52.         parent::__construct($nodes, [
  53.             'index' => null,
  54.             'embedded_templates' => $embeddedTemplates,
  55.         ], 1);
  56.         // populate the template name of all node children
  57.         $this->setSourceContext($source);
  58.     }
  59.     /**
  60.      * @return void
  61.      */
  62.     public function setIndex($index)
  63.     {
  64.         $this->setAttribute('index'$index);
  65.     }
  66.     public function compile(Compiler $compiler): void
  67.     {
  68.         $this->compileTemplate($compiler);
  69.         foreach ($this->getAttribute('embedded_templates') as $template) {
  70.             $compiler->subcompile($template);
  71.         }
  72.     }
  73.     /**
  74.      * @return void
  75.      */
  76.     protected function compileTemplate(Compiler $compiler)
  77.     {
  78.         if (!$this->getAttribute('index')) {
  79.             $compiler->write('<?php');
  80.         }
  81.         $this->compileClassHeader($compiler);
  82.         $this->compileConstructor($compiler);
  83.         $this->compileGetParent($compiler);
  84.         $this->compileDisplay($compiler);
  85.         $compiler->subcompile($this->getNode('blocks'));
  86.         $this->compileMacros($compiler);
  87.         $this->compileGetTemplateName($compiler);
  88.         $this->compileIsTraitable($compiler);
  89.         $this->compileDebugInfo($compiler);
  90.         $this->compileGetSourceContext($compiler);
  91.         $this->compileClassFooter($compiler);
  92.     }
  93.     /**
  94.      * @return void
  95.      */
  96.     protected function compileGetParent(Compiler $compiler)
  97.     {
  98.         if (!$this->hasNode('parent')) {
  99.             return;
  100.         }
  101.         $parent $this->getNode('parent');
  102.         $compiler
  103.             ->write("protected function doGetParent(array \$context): bool|string|Template|TemplateWrapper\n""{\n")
  104.             ->indent()
  105.             ->addDebugInfo($parent)
  106.             ->write('return ')
  107.         ;
  108.         if ($parent instanceof ConstantExpression) {
  109.             $compiler->subcompile($parent);
  110.         } else {
  111.             $compiler
  112.                 ->raw('$this->loadTemplate(')
  113.                 ->subcompile($parent)
  114.                 ->raw(', ')
  115.                 ->repr($this->getSourceContext()->getName())
  116.                 ->raw(', ')
  117.                 ->repr($parent->getTemplateLine())
  118.                 ->raw(')')
  119.             ;
  120.         }
  121.         $compiler
  122.             ->raw(";\n")
  123.             ->outdent()
  124.             ->write("}\n\n")
  125.         ;
  126.     }
  127.     /**
  128.      * @return void
  129.      */
  130.     protected function compileClassHeader(Compiler $compiler)
  131.     {
  132.         $compiler
  133.             ->write("\n\n")
  134.         ;
  135.         if (!$this->getAttribute('index')) {
  136.             $compiler
  137.                 ->write("use Twig\Environment;\n")
  138.                 ->write("use Twig\Error\LoaderError;\n")
  139.                 ->write("use Twig\Error\RuntimeError;\n")
  140.                 ->write("use Twig\Extension\CoreExtension;\n")
  141.                 ->write("use Twig\Extension\SandboxExtension;\n")
  142.                 ->write("use Twig\Markup;\n")
  143.                 ->write("use Twig\Sandbox\SecurityError;\n")
  144.                 ->write("use Twig\Sandbox\SecurityNotAllowedTagError;\n")
  145.                 ->write("use Twig\Sandbox\SecurityNotAllowedFilterError;\n")
  146.                 ->write("use Twig\Sandbox\SecurityNotAllowedFunctionError;\n")
  147.                 ->write("use Twig\Source;\n")
  148.                 ->write("use Twig\Template;\n")
  149.                 ->write("use Twig\TemplateWrapper;\n")
  150.                 ->write("\n")
  151.             ;
  152.         }
  153.         $compiler
  154.             // if the template name contains */, add a blank to avoid a PHP parse error
  155.             ->write('/* '.str_replace('*/''* /'$this->getSourceContext()->getName())." */\n")
  156.             ->write('class '.$compiler->getEnvironment()->getTemplateClass($this->getSourceContext()->getName(), $this->getAttribute('index')))
  157.             ->raw(" extends Template\n")
  158.             ->write("{\n")
  159.             ->indent()
  160.             ->write("private Source \$source;\n")
  161.             ->write("/**\n")
  162.             ->write(" * @var array<string, Template>\n")
  163.             ->write(" */\n")
  164.             ->write("private array \$macros = [];\n\n")
  165.         ;
  166.     }
  167.     /**
  168.      * @return void
  169.      */
  170.     protected function compileConstructor(Compiler $compiler)
  171.     {
  172.         $compiler
  173.             ->write("public function __construct(Environment \$env)\n""{\n")
  174.             ->indent()
  175.             ->subcompile($this->getNode('constructor_start'))
  176.             ->write("parent::__construct(\$env);\n\n")
  177.             ->write("\$this->source = \$this->getSourceContext();\n\n")
  178.         ;
  179.         // parent
  180.         if (!$this->hasNode('parent')) {
  181.             $compiler->write("\$this->parent = false;\n\n");
  182.         }
  183.         $countTraits \count($this->getNode('traits'));
  184.         if ($countTraits) {
  185.             // traits
  186.             foreach ($this->getNode('traits') as $i => $trait) {
  187.                 $node $trait->getNode('template');
  188.                 $compiler
  189.                     ->addDebugInfo($node)
  190.                     ->write(\sprintf('$_trait_%s = $this->loadTemplate('$i))
  191.                     ->subcompile($node)
  192.                     ->raw(', ')
  193.                     ->repr($node->getTemplateName())
  194.                     ->raw(', ')
  195.                     ->repr($node->getTemplateLine())
  196.                     ->raw(");\n")
  197.                     ->write(\sprintf("if (!\$_trait_%s->unwrap()->isTraitable()) {\n"$i))
  198.                     ->indent()
  199.                     ->write("throw new RuntimeError('Template \"'.")
  200.                     ->subcompile($trait->getNode('template'))
  201.                     ->raw(".'\" cannot be used as a trait.', ")
  202.                     ->repr($node->getTemplateLine())
  203.                     ->raw(", \$this->source);\n")
  204.                     ->outdent()
  205.                     ->write("}\n")
  206.                     ->write(\sprintf("\$_trait_%s_blocks = \$_trait_%s->unwrap()->getBlocks();\n\n"$i$i))
  207.                 ;
  208.                 foreach ($trait->getNode('targets') as $key => $value) {
  209.                     $compiler
  210.                         ->write(\sprintf('if (!isset($_trait_%s_blocks['$i))
  211.                         ->string($key)
  212.                         ->raw("])) {\n")
  213.                         ->indent()
  214.                         ->write("throw new RuntimeError('Block ")
  215.                         ->string($key)
  216.                         ->raw(' is not defined in trait ')
  217.                         ->subcompile($trait->getNode('template'))
  218.                         ->raw(".', ")
  219.                         ->repr($node->getTemplateLine())
  220.                         ->raw(", \$this->source);\n")
  221.                         ->outdent()
  222.                         ->write("}\n\n")
  223.                         ->write(\sprintf('$_trait_%s_blocks['$i))
  224.                         ->subcompile($value)
  225.                         ->raw(\sprintf('] = $_trait_%s_blocks['$i))
  226.                         ->string($key)
  227.                         ->raw(\sprintf(']; unset($_trait_%s_blocks['$i))
  228.                         ->string($key)
  229.                         ->raw(']); $this->traitAliases[')
  230.                         ->subcompile($value)
  231.                         ->raw('] = ')
  232.                         ->string($key)
  233.                         ->raw(";\n\n")
  234.                     ;
  235.                 }
  236.             }
  237.             if ($countTraits 1) {
  238.                 $compiler
  239.                     ->write("\$this->traits = array_merge(\n")
  240.                     ->indent()
  241.                 ;
  242.                 for ($i 0$i $countTraits; ++$i) {
  243.                     $compiler
  244.                         ->write(\sprintf('$_trait_%s_blocks'.($i == $countTraits '' ',')."\n"$i))
  245.                     ;
  246.                 }
  247.                 $compiler
  248.                     ->outdent()
  249.                     ->write(");\n\n")
  250.                 ;
  251.             } else {
  252.                 $compiler
  253.                     ->write("\$this->traits = \$_trait_0_blocks;\n\n")
  254.                 ;
  255.             }
  256.             $compiler
  257.                 ->write("\$this->blocks = array_merge(\n")
  258.                 ->indent()
  259.                 ->write("\$this->traits,\n")
  260.                 ->write("[\n")
  261.             ;
  262.         } else {
  263.             $compiler
  264.                 ->write("\$this->blocks = [\n")
  265.             ;
  266.         }
  267.         // blocks
  268.         $compiler
  269.             ->indent()
  270.         ;
  271.         foreach ($this->getNode('blocks') as $name => $node) {
  272.             $compiler
  273.                 ->write(\sprintf("'%s' => [\$this, 'block_%s'],\n"$name$name))
  274.             ;
  275.         }
  276.         if ($countTraits) {
  277.             $compiler
  278.                 ->outdent()
  279.                 ->write("]\n")
  280.                 ->outdent()
  281.                 ->write(");\n")
  282.             ;
  283.         } else {
  284.             $compiler
  285.                 ->outdent()
  286.                 ->write("];\n")
  287.             ;
  288.         }
  289.         $compiler
  290.             ->subcompile($this->getNode('constructor_end'))
  291.             ->outdent()
  292.             ->write("}\n\n")
  293.         ;
  294.     }
  295.     /**
  296.      * @return void
  297.      */
  298.     protected function compileDisplay(Compiler $compiler)
  299.     {
  300.         $compiler
  301.             ->write("protected function doDisplay(array \$context, array \$blocks = []): iterable\n""{\n")
  302.             ->indent()
  303.             ->write("\$macros = \$this->macros;\n")
  304.             ->subcompile($this->getNode('display_start'))
  305.             ->subcompile($this->getNode('body'))
  306.         ;
  307.         if ($this->hasNode('parent')) {
  308.             $parent $this->getNode('parent');
  309.             $compiler->addDebugInfo($parent);
  310.             if ($parent instanceof ConstantExpression) {
  311.                 $compiler
  312.                     ->write('$this->parent = $this->loadTemplate(')
  313.                     ->subcompile($parent)
  314.                     ->raw(', ')
  315.                     ->repr($this->getSourceContext()->getName())
  316.                     ->raw(', ')
  317.                     ->repr($parent->getTemplateLine())
  318.                     ->raw(");\n")
  319.                 ;
  320.             }
  321.             $compiler->write('yield from ');
  322.             if ($parent instanceof ConstantExpression) {
  323.                 $compiler->raw('$this->parent');
  324.             } else {
  325.                 $compiler->raw('$this->getParent($context)');
  326.             }
  327.             $compiler->raw("->unwrap()->yield(\$context, array_merge(\$this->blocks, \$blocks));\n");
  328.         }
  329.         $compiler->subcompile($this->getNode('display_end'));
  330.         if (!$this->hasNode('parent')) {
  331.             $compiler->write("yield from [];\n");
  332.         }
  333.         $compiler
  334.             ->outdent()
  335.             ->write("}\n\n")
  336.         ;
  337.     }
  338.     /**
  339.      * @return void
  340.      */
  341.     protected function compileClassFooter(Compiler $compiler)
  342.     {
  343.         $compiler
  344.             ->subcompile($this->getNode('class_end'))
  345.             ->outdent()
  346.             ->write("}\n")
  347.         ;
  348.     }
  349.     /**
  350.      * @return void
  351.      */
  352.     protected function compileMacros(Compiler $compiler)
  353.     {
  354.         $compiler->subcompile($this->getNode('macros'));
  355.     }
  356.     /**
  357.      * @return void
  358.      */
  359.     protected function compileGetTemplateName(Compiler $compiler)
  360.     {
  361.         $compiler
  362.             ->write("/**\n")
  363.             ->write(" * @codeCoverageIgnore\n")
  364.             ->write(" */\n")
  365.             ->write("public function getTemplateName(): string\n""{\n")
  366.             ->indent()
  367.             ->write('return ')
  368.             ->repr($this->getSourceContext()->getName())
  369.             ->raw(";\n")
  370.             ->outdent()
  371.             ->write("}\n\n")
  372.         ;
  373.     }
  374.     /**
  375.      * @return void
  376.      */
  377.     protected function compileIsTraitable(Compiler $compiler)
  378.     {
  379.         // A template can be used as a trait if:
  380.         //   * it has no parent
  381.         //   * it has no macros
  382.         //   * it has no body
  383.         //
  384.         // Put another way, a template can be used as a trait if it
  385.         // only contains blocks and use statements.
  386.         $traitable = !$this->hasNode('parent') && === \count($this->getNode('macros'));
  387.         if ($traitable) {
  388.             if ($this->getNode('body') instanceof BodyNode) {
  389.                 $nodes $this->getNode('body')->getNode('0');
  390.             } else {
  391.                 $nodes $this->getNode('body');
  392.             }
  393.             if (!\count($nodes)) {
  394.                 $nodes = new Nodes([$nodes]);
  395.             }
  396.             foreach ($nodes as $node) {
  397.                 if (!\count($node)) {
  398.                     continue;
  399.                 }
  400.                 $traitable false;
  401.                 break;
  402.             }
  403.         }
  404.         if ($traitable) {
  405.             return;
  406.         }
  407.         $compiler
  408.             ->write("/**\n")
  409.             ->write(" * @codeCoverageIgnore\n")
  410.             ->write(" */\n")
  411.             ->write("public function isTraitable(): bool\n""{\n")
  412.             ->indent()
  413.             ->write("return false;\n")
  414.             ->outdent()
  415.             ->write("}\n\n")
  416.         ;
  417.     }
  418.     /**
  419.      * @return void
  420.      */
  421.     protected function compileDebugInfo(Compiler $compiler)
  422.     {
  423.         $compiler
  424.             ->write("/**\n")
  425.             ->write(" * @codeCoverageIgnore\n")
  426.             ->write(" */\n")
  427.             ->write("public function getDebugInfo(): array\n""{\n")
  428.             ->indent()
  429.             ->write(\sprintf("return %s;\n"str_replace("\n"''var_export(array_reverse($compiler->getDebugInfo(), true), true))))
  430.             ->outdent()
  431.             ->write("}\n\n")
  432.         ;
  433.     }
  434.     /**
  435.      * @return void
  436.      */
  437.     protected function compileGetSourceContext(Compiler $compiler)
  438.     {
  439.         $compiler
  440.             ->write("public function getSourceContext(): Source\n""{\n")
  441.             ->indent()
  442.             ->write('return new Source(')
  443.             ->string($compiler->getEnvironment()->isDebug() ? $this->getSourceContext()->getCode() : '')
  444.             ->raw(', ')
  445.             ->string($this->getSourceContext()->getName())
  446.             ->raw(', ')
  447.             ->string($this->getSourceContext()->getPath())
  448.             ->raw(");\n")
  449.             ->outdent()
  450.             ->write("}\n")
  451.         ;
  452.     }
  453. }