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

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