myaac/system/libs/Twig/Node/ModuleNode.php
slawkens 8021308822 Update Twig from 1.35.0 to 1.42.4 (PHP 5.5 is now required!)
This fixes some errors on PHP 7.4 and contains even more fixes
Also bumped PHP version to 5.5 as Twig requires it.
2020-02-15 05:41:38 +01:00

493 lines
16 KiB
PHP

<?php
/*
* This file is part of Twig.
*
* (c) Fabien Potencier
* (c) Armin Ronacher
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Twig\Node;
use Twig\Compiler;
use Twig\Node\Expression\AbstractExpression;
use Twig\Node\Expression\ConstantExpression;
use Twig\Source;
/**
* Represents a module node.
*
* Consider this class as being final. If you need to customize the behavior of
* the generated class, consider adding nodes to the following nodes: display_start,
* display_end, constructor_start, constructor_end, and class_end.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class ModuleNode extends Node
{
public function __construct(\Twig_NodeInterface $body, AbstractExpression $parent = null, \Twig_NodeInterface $blocks, \Twig_NodeInterface $macros, \Twig_NodeInterface $traits, $embeddedTemplates, $name, $source = '')
{
if (!$name instanceof Source) {
@trigger_error(sprintf('Passing a string as the $name argument of %s() is deprecated since version 1.27. Pass a \Twig\Source instance instead.', __METHOD__), E_USER_DEPRECATED);
$source = new Source($source, $name);
} else {
$source = $name;
}
$nodes = [
'body' => $body,
'blocks' => $blocks,
'macros' => $macros,
'traits' => $traits,
'display_start' => new Node(),
'display_end' => new Node(),
'constructor_start' => new Node(),
'constructor_end' => new Node(),
'class_end' => new Node(),
];
if (null !== $parent) {
$nodes['parent'] = $parent;
}
// embedded templates are set as attributes so that they are only visited once by the visitors
parent::__construct($nodes, [
// source to be remove in 2.0
'source' => $source->getCode(),
// filename to be remove in 2.0 (use getTemplateName() instead)
'filename' => $source->getName(),
'index' => null,
'embedded_templates' => $embeddedTemplates,
], 1);
// populate the template name of all node children
$this->setTemplateName($source->getName());
$this->setSourceContext($source);
}
public function setIndex($index)
{
$this->setAttribute('index', $index);
}
public function compile(Compiler $compiler)
{
$this->compileTemplate($compiler);
foreach ($this->getAttribute('embedded_templates') as $template) {
$compiler->subcompile($template);
}
}
protected function compileTemplate(Compiler $compiler)
{
if (!$this->getAttribute('index')) {
$compiler->write('<?php');
}
$this->compileClassHeader($compiler);
if (
\count($this->getNode('blocks'))
|| \count($this->getNode('traits'))
|| !$this->hasNode('parent')
|| $this->getNode('parent') instanceof ConstantExpression
|| \count($this->getNode('constructor_start'))
|| \count($this->getNode('constructor_end'))
) {
$this->compileConstructor($compiler);
}
$this->compileGetParent($compiler);
$this->compileDisplay($compiler);
$compiler->subcompile($this->getNode('blocks'));
$this->compileMacros($compiler);
$this->compileGetTemplateName($compiler);
$this->compileIsTraitable($compiler);
$this->compileDebugInfo($compiler);
$this->compileGetSource($compiler);
$this->compileGetSourceContext($compiler);
$this->compileClassFooter($compiler);
}
protected function compileGetParent(Compiler $compiler)
{
if (!$this->hasNode('parent')) {
return;
}
$parent = $this->getNode('parent');
$compiler
->write("protected function doGetParent(array \$context)\n", "{\n")
->indent()
->addDebugInfo($parent)
->write('return ')
;
if ($parent instanceof ConstantExpression) {
$compiler->subcompile($parent);
} else {
$compiler
->raw('$this->loadTemplate(')
->subcompile($parent)
->raw(', ')
->repr($this->getSourceContext()->getName())
->raw(', ')
->repr($parent->getTemplateLine())
->raw(')')
;
}
$compiler
->raw(";\n")
->outdent()
->write("}\n\n")
;
}
protected function compileClassHeader(Compiler $compiler)
{
$compiler
->write("\n\n")
;
if (!$this->getAttribute('index')) {
$compiler
->write("use Twig\Environment;\n")
->write("use Twig\Error\LoaderError;\n")
->write("use Twig\Error\RuntimeError;\n")
->write("use Twig\Markup;\n")
->write("use Twig\Sandbox\SecurityError;\n")
->write("use Twig\Sandbox\SecurityNotAllowedTagError;\n")
->write("use Twig\Sandbox\SecurityNotAllowedFilterError;\n")
->write("use Twig\Sandbox\SecurityNotAllowedFunctionError;\n")
->write("use Twig\Source;\n")
->write("use Twig\Template;\n\n")
;
}
$compiler
// if the template name contains */, add a blank to avoid a PHP parse error
->write('/* '.str_replace('*/', '* /', $this->getSourceContext()->getName())." */\n")
->write('class '.$compiler->getEnvironment()->getTemplateClass($this->getSourceContext()->getName(), $this->getAttribute('index')))
->raw(sprintf(" extends %s\n", $compiler->getEnvironment()->getBaseTemplateClass()))
->write("{\n")
->indent()
;
}
protected function compileConstructor(Compiler $compiler)
{
$compiler
->write("public function __construct(Environment \$env)\n", "{\n")
->indent()
->subcompile($this->getNode('constructor_start'))
->write("parent::__construct(\$env);\n\n")
;
// parent
if (!$this->hasNode('parent')) {
$compiler->write("\$this->parent = false;\n\n");
}
$countTraits = \count($this->getNode('traits'));
if ($countTraits) {
// traits
foreach ($this->getNode('traits') as $i => $trait) {
$this->compileLoadTemplate($compiler, $trait->getNode('template'), sprintf('$_trait_%s', $i));
$node = $trait->getNode('template');
$compiler
->addDebugInfo($node)
->write(sprintf("if (!\$_trait_%s->isTraitable()) {\n", $i))
->indent()
->write("throw new RuntimeError('Template \"'.")
->subcompile($trait->getNode('template'))
->raw(".'\" cannot be used as a trait.', ")
->repr($node->getTemplateLine())
->raw(", \$this->getSourceContext());\n")
->outdent()
->write("}\n")
->write(sprintf("\$_trait_%s_blocks = \$_trait_%s->getBlocks();\n\n", $i, $i))
;
foreach ($trait->getNode('targets') as $key => $value) {
$compiler
->write(sprintf('if (!isset($_trait_%s_blocks[', $i))
->string($key)
->raw("])) {\n")
->indent()
->write("throw new RuntimeError(sprintf('Block ")
->string($key)
->raw(' is not defined in trait ')
->subcompile($trait->getNode('template'))
->raw(".'), ")
->repr($node->getTemplateLine())
->raw(", \$this->getSourceContext());\n")
->outdent()
->write("}\n\n")
->write(sprintf('$_trait_%s_blocks[', $i))
->subcompile($value)
->raw(sprintf('] = $_trait_%s_blocks[', $i))
->string($key)
->raw(sprintf(']; unset($_trait_%s_blocks[', $i))
->string($key)
->raw("]);\n\n")
;
}
}
if ($countTraits > 1) {
$compiler
->write("\$this->traits = array_merge(\n")
->indent()
;
for ($i = 0; $i < $countTraits; ++$i) {
$compiler
->write(sprintf('$_trait_%s_blocks'.($i == $countTraits - 1 ? '' : ',')."\n", $i))
;
}
$compiler
->outdent()
->write(");\n\n")
;
} else {
$compiler
->write("\$this->traits = \$_trait_0_blocks;\n\n")
;
}
$compiler
->write("\$this->blocks = array_merge(\n")
->indent()
->write("\$this->traits,\n")
->write("[\n")
;
} else {
$compiler
->write("\$this->blocks = [\n")
;
}
// blocks
$compiler
->indent()
;
foreach ($this->getNode('blocks') as $name => $node) {
$compiler
->write(sprintf("'%s' => [\$this, 'block_%s'],\n", $name, $name))
;
}
if ($countTraits) {
$compiler
->outdent()
->write("]\n")
->outdent()
->write(");\n")
;
} else {
$compiler
->outdent()
->write("];\n")
;
}
$compiler
->subcompile($this->getNode('constructor_end'))
->outdent()
->write("}\n\n")
;
}
protected function compileDisplay(Compiler $compiler)
{
$compiler
->write("protected function doDisplay(array \$context, array \$blocks = [])\n", "{\n")
->indent()
->subcompile($this->getNode('display_start'))
->subcompile($this->getNode('body'))
;
if ($this->hasNode('parent')) {
$parent = $this->getNode('parent');
$compiler->addDebugInfo($parent);
if ($parent instanceof ConstantExpression) {
$compiler
->write('$this->parent = $this->loadTemplate(')
->subcompile($parent)
->raw(', ')
->repr($this->getSourceContext()->getName())
->raw(', ')
->repr($parent->getTemplateLine())
->raw(");\n")
;
$compiler->write('$this->parent');
} else {
$compiler->write('$this->getParent($context)');
}
$compiler->raw("->display(\$context, array_merge(\$this->blocks, \$blocks));\n");
}
$compiler
->subcompile($this->getNode('display_end'))
->outdent()
->write("}\n\n")
;
}
protected function compileClassFooter(Compiler $compiler)
{
$compiler
->subcompile($this->getNode('class_end'))
->outdent()
->write("}\n")
;
}
protected function compileMacros(Compiler $compiler)
{
$compiler->subcompile($this->getNode('macros'));
}
protected function compileGetTemplateName(Compiler $compiler)
{
$compiler
->write("public function getTemplateName()\n", "{\n")
->indent()
->write('return ')
->repr($this->getSourceContext()->getName())
->raw(";\n")
->outdent()
->write("}\n\n")
;
}
protected function compileIsTraitable(Compiler $compiler)
{
// A template can be used as a trait if:
// * it has no parent
// * it has no macros
// * it has no body
//
// Put another way, a template can be used as a trait if it
// only contains blocks and use statements.
$traitable = !$this->hasNode('parent') && 0 === \count($this->getNode('macros'));
if ($traitable) {
if ($this->getNode('body') instanceof BodyNode) {
$nodes = $this->getNode('body')->getNode(0);
} else {
$nodes = $this->getNode('body');
}
if (!\count($nodes)) {
$nodes = new Node([$nodes]);
}
foreach ($nodes as $node) {
if (!\count($node)) {
continue;
}
if ($node instanceof TextNode && ctype_space($node->getAttribute('data'))) {
continue;
}
if ($node instanceof BlockReferenceNode) {
continue;
}
$traitable = false;
break;
}
}
if ($traitable) {
return;
}
$compiler
->write("public function isTraitable()\n", "{\n")
->indent()
->write(sprintf("return %s;\n", $traitable ? 'true' : 'false'))
->outdent()
->write("}\n\n")
;
}
protected function compileDebugInfo(Compiler $compiler)
{
$compiler
->write("public function getDebugInfo()\n", "{\n")
->indent()
->write(sprintf("return %s;\n", str_replace("\n", '', var_export(array_reverse($compiler->getDebugInfo(), true), true))))
->outdent()
->write("}\n\n")
;
}
protected function compileGetSource(Compiler $compiler)
{
$compiler
->write("/** @deprecated since 1.27 (to be removed in 2.0). Use getSourceContext() instead */\n")
->write("public function getSource()\n", "{\n")
->indent()
->write("@trigger_error('The '.__METHOD__.' method is deprecated since version 1.27 and will be removed in 2.0. Use getSourceContext() instead.', E_USER_DEPRECATED);\n\n")
->write('return $this->getSourceContext()->getCode();')
->raw("\n")
->outdent()
->write("}\n\n")
;
}
protected function compileGetSourceContext(Compiler $compiler)
{
$compiler
->write("public function getSourceContext()\n", "{\n")
->indent()
->write('return new Source(')
->string($compiler->getEnvironment()->isDebug() ? $this->getSourceContext()->getCode() : '')
->raw(', ')
->string($this->getSourceContext()->getName())
->raw(', ')
->string($this->getSourceContext()->getPath())
->raw(");\n")
->outdent()
->write("}\n")
;
}
protected function compileLoadTemplate(Compiler $compiler, $node, $var)
{
if ($node instanceof ConstantExpression) {
$compiler
->write(sprintf('%s = $this->loadTemplate(', $var))
->subcompile($node)
->raw(', ')
->repr($node->getTemplateName())
->raw(', ')
->repr($node->getTemplateLine())
->raw(");\n")
;
} else {
throw new \LogicException('Trait templates can only be constant nodes.');
}
}
}
class_alias('Twig\Node\ModuleNode', 'Twig_Node_Module');