Update Twig to v2.15.4

This commit is contained in:
slawkens
2023-02-02 10:37:45 +01:00
parent e552bcfe82
commit 130f7ba405
309 changed files with 3802 additions and 4005 deletions

View File

@@ -26,7 +26,7 @@ use Twig\Compiler;
*/
class AutoEscapeNode extends Node
{
public function __construct($value, \Twig_NodeInterface $body, $lineno, $tag = 'autoescape')
public function __construct($value, Node $body, int $lineno, string $tag = 'autoescape')
{
parent::__construct(['body' => $body], ['value' => $value], $lineno, $tag);
}

View File

@@ -21,7 +21,7 @@ use Twig\Compiler;
*/
class BlockNode extends Node
{
public function __construct($name, \Twig_NodeInterface $body, $lineno, $tag = null)
public function __construct(string $name, Node $body, int $lineno, string $tag = null)
{
parent::__construct(['body' => $body], ['name' => $name], $lineno, $tag);
}
@@ -32,6 +32,7 @@ class BlockNode extends Node
->addDebugInfo($this)
->write(sprintf("public function block_%s(\$context, array \$blocks = [])\n", $this->getAttribute('name')), "{\n")
->indent()
->write("\$macros = \$this->macros;\n")
;
$compiler

View File

@@ -21,7 +21,7 @@ use Twig\Compiler;
*/
class BlockReferenceNode extends Node implements NodeOutputInterface
{
public function __construct($name, $lineno, $tag = null)
public function __construct(string $name, int $lineno, string $tag = null)
{
parent::__construct([], ['name' => $name], $lineno, $tag);
}

View File

@@ -0,0 +1,28 @@
<?php
/*
* This file is part of Twig.
*
* (c) Fabien Potencier
*
* 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;
/**
* @author Fabien Potencier <fabien@symfony.com>
*/
class CheckSecurityCallNode extends Node
{
public function compile(Compiler $compiler)
{
$compiler
->write("\$this->sandbox = \$this->env->getExtension('\Twig\Extension\SandboxExtension');\n")
->write("\$this->checkSecurity();\n")
;
}
}

View File

@@ -18,9 +18,9 @@ use Twig\Compiler;
*/
class CheckSecurityNode extends Node
{
protected $usedFilters;
protected $usedTags;
protected $usedFunctions;
private $usedFilters;
private $usedTags;
private $usedFunctions;
public function __construct(array $usedFilters, array $usedTags, array $usedFunctions)
{
@@ -45,10 +45,13 @@ class CheckSecurityNode extends Node
}
$compiler
->write("\$this->sandbox = \$this->env->getExtension('\Twig\Extension\SandboxExtension');\n")
->write('$tags = ')->repr(array_filter($tags))->raw(";\n")
->write('$filters = ')->repr(array_filter($filters))->raw(";\n")
->write('$functions = ')->repr(array_filter($functions))->raw(";\n\n")
->write("\n")
->write("public function checkSecurity()\n")
->write("{\n")
->indent()
->write('static $tags = ')->repr(array_filter($tags))->raw(";\n")
->write('static $filters = ')->repr(array_filter($filters))->raw(";\n")
->write('static $functions = ')->repr(array_filter($functions))->raw(";\n\n")
->write("try {\n")
->indent()
->write("\$this->sandbox->checkSecurity(\n")
@@ -61,7 +64,7 @@ class CheckSecurityNode extends Node
->outdent()
->write("} catch (SecurityError \$e) {\n")
->indent()
->write("\$e->setSourceContext(\$this->getSourceContext());\n\n")
->write("\$e->setSourceContext(\$this->source);\n\n")
->write("if (\$e instanceof SecurityNotAllowedTagError && isset(\$tags[\$e->getTagName()])) {\n")
->indent()
->write("\$e->setTemplateLine(\$tags[\$e->getTagName()]);\n")
@@ -78,6 +81,8 @@ class CheckSecurityNode extends Node
->write("throw \$e;\n")
->outdent()
->write("}\n\n")
->outdent()
->write("}\n")
;
}
}

View File

@@ -33,10 +33,13 @@ class CheckToStringNode extends AbstractExpression
public function compile(Compiler $compiler)
{
$expr = $this->getNode('expr');
$compiler
->raw('$this->sandbox->ensureToStringAllowed(')
->subcompile($this->getNode('expr'))
->raw(')')
->subcompile($expr)
->raw(', ')
->repr($expr->getTemplateLine())
->raw(', $this->source)')
;
}
}

View File

@@ -22,7 +22,7 @@ use Twig\Node\Expression\ConstantExpression;
*/
class DeprecatedNode extends Node
{
public function __construct(AbstractExpression $expr, $lineno, $tag = null)
public function __construct(AbstractExpression $expr, int $lineno, string $tag = null)
{
parent::__construct(['expr' => $expr], [], $lineno, $tag);
}

View File

@@ -21,7 +21,7 @@ use Twig\Node\Expression\AbstractExpression;
*/
class DoNode extends Node
{
public function __construct(AbstractExpression $expr, $lineno, $tag = null)
public function __construct(AbstractExpression $expr, int $lineno, string $tag = null)
{
parent::__construct(['expr' => $expr], [], $lineno, $tag);
}

View File

@@ -23,13 +23,11 @@ use Twig\Node\Expression\ConstantExpression;
class EmbedNode extends IncludeNode
{
// we don't inject the module to avoid node visitors to traverse it twice (as it will be already visited in the main module)
public function __construct($name, $index, AbstractExpression $variables = null, $only = false, $ignoreMissing = false, $lineno, $tag = null)
public function __construct(string $name, int $index, ?AbstractExpression $variables, bool $only, bool $ignoreMissing, int $lineno, string $tag = null)
{
parent::__construct(new ConstantExpression('not_used', $lineno), $variables, $only, $ignoreMissing, $lineno, $tag);
$this->setAttribute('name', $name);
// to be removed in 2.0, used name instead
$this->setAttribute('filename', $name);
$this->setAttribute('index', $index);
}

View File

@@ -15,9 +15,9 @@ use Twig\Compiler;
class ArrayExpression extends AbstractExpression
{
protected $index;
private $index;
public function __construct(array $elements, $lineno)
public function __construct(array $elements, int $lineno)
{
parent::__construct($elements, [], $lineno);

View File

@@ -44,7 +44,7 @@ class ArrowFunctionExpression extends AbstractExpression
;
}
$compiler
->raw(') use ($context) { ')
->raw(') use ($context, $macros) { ')
;
foreach ($this->getNode('names') as $name) {
$compiler

View File

@@ -14,10 +14,11 @@ namespace Twig\Node\Expression\Binary;
use Twig\Compiler;
use Twig\Node\Expression\AbstractExpression;
use Twig\Node\Node;
abstract class AbstractBinary extends AbstractExpression
{
public function __construct(\Twig_NodeInterface $left, \Twig_NodeInterface $right, $lineno)
public function __construct(Node $left, Node $right, int $lineno)
{
parent::__construct(['left' => $left, 'right' => $right], [], $lineno);
}

View File

@@ -15,21 +15,6 @@ use Twig\Compiler;
class PowerBinary extends AbstractBinary
{
public function compile(Compiler $compiler)
{
if (\PHP_VERSION_ID >= 50600) {
return parent::compile($compiler);
}
$compiler
->raw('pow(')
->subcompile($this->getNode('left'))
->raw(', ')
->subcompile($this->getNode('right'))
->raw(')')
;
}
public function operator(Compiler $compiler)
{
return $compiler->raw('**');

View File

@@ -0,0 +1,22 @@
<?php
/*
* This file is part of Twig.
*
* (c) Fabien Potencier
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Twig\Node\Expression\Binary;
use Twig\Compiler;
class SpaceshipBinary extends AbstractBinary
{
public function operator(Compiler $compiler)
{
return $compiler->raw('<=>');
}
}

View File

@@ -22,17 +22,8 @@ use Twig\Node\Node;
*/
class BlockReferenceExpression extends AbstractExpression
{
/**
* @param Node|null $template
*/
public function __construct(\Twig_NodeInterface $name, $template = null, $lineno, $tag = null)
public function __construct(Node $name, ?Node $template, int $lineno, string $tag = null)
{
if (\is_bool($template)) {
@trigger_error(sprintf('The %s method "$asString" argument is deprecated since version 1.28 and will be removed in 2.0.', __METHOD__), E_USER_DEPRECATED);
$template = null;
}
$nodes = ['name' => $name];
if (null !== $template) {
$nodes['template'] = $template;
@@ -58,7 +49,7 @@ class BlockReferenceExpression extends AbstractExpression
}
}
private function compileTemplateCall(Compiler $compiler, $method)
private function compileTemplateCall(Compiler $compiler, string $method): Compiler
{
if (!$this->hasNode('template')) {
$compiler->write('$this');
@@ -75,12 +66,11 @@ class BlockReferenceExpression extends AbstractExpression
}
$compiler->raw(sprintf('->%s', $method));
$this->compileBlockArguments($compiler);
return $compiler;
return $this->compileBlockArguments($compiler);
}
private function compileBlockArguments(Compiler $compiler)
private function compileBlockArguments(Compiler $compiler): Compiler
{
$compiler
->raw('(')

View File

@@ -22,37 +22,37 @@ abstract class CallExpression extends AbstractExpression
protected function compileCallable(Compiler $compiler)
{
$closingParenthesis = false;
$isArray = false;
if ($this->hasAttribute('callable') && $callable = $this->getAttribute('callable')) {
if (\is_string($callable) && false === strpos($callable, '::')) {
$compiler->raw($callable);
} else {
list($r, $callable) = $this->reflectCallable($callable);
if ($r instanceof \ReflectionMethod && \is_string($callable[0])) {
if ($r->isStatic()) {
$compiler->raw(sprintf('%s::%s', $callable[0], $callable[1]));
} else {
$compiler->raw(sprintf('$this->env->getRuntime(\'%s\')->%s', $callable[0], $callable[1]));
}
} elseif ($r instanceof \ReflectionMethod && $callable[0] instanceof ExtensionInterface) {
$compiler->raw(sprintf('$this->env->getExtension(\'%s\')->%s', \get_class($callable[0]), $callable[1]));
} else {
$type = ucfirst($this->getAttribute('type'));
$compiler->raw(sprintf('call_user_func_array($this->env->get%s(\'%s\')->getCallable(), ', $type, $this->getAttribute('name')));
$closingParenthesis = true;
$isArray = true;
}
}
$callable = $this->getAttribute('callable');
if (\is_string($callable) && false === strpos($callable, '::')) {
$compiler->raw($callable);
} else {
$compiler->raw($this->getAttribute('thing')->compile());
[$r, $callable] = $this->reflectCallable($callable);
if (\is_string($callable)) {
$compiler->raw($callable);
} elseif (\is_array($callable) && \is_string($callable[0])) {
if (!$r instanceof \ReflectionMethod || $r->isStatic()) {
$compiler->raw(sprintf('%s::%s', $callable[0], $callable[1]));
} else {
$compiler->raw(sprintf('$this->env->getRuntime(\'%s\')->%s', $callable[0], $callable[1]));
}
} elseif (\is_array($callable) && $callable[0] instanceof ExtensionInterface) {
$class = \get_class($callable[0]);
if (!$compiler->getEnvironment()->hasExtension($class)) {
// Compile a non-optimized call to trigger a \Twig\Error\RuntimeError, which cannot be a compile-time error
$compiler->raw(sprintf('$this->env->getExtension(\'%s\')', $class));
} else {
$compiler->raw(sprintf('$this->extensions[\'%s\']', ltrim($class, '\\')));
}
$compiler->raw(sprintf('->%s', $callable[1]));
} else {
$compiler->raw(sprintf('$this->env->get%s(\'%s\')->getCallable()', ucfirst($this->getAttribute('type')), $this->getAttribute('name')));
}
}
$this->compileArguments($compiler, $isArray);
if ($closingParenthesis) {
$compiler->raw(')');
}
$this->compileArguments($compiler);
}
protected function compileArguments(Compiler $compiler, $isArray = false)
@@ -93,10 +93,8 @@ abstract class CallExpression extends AbstractExpression
}
if ($this->hasNode('arguments')) {
$callable = $this->hasAttribute('callable') ? $this->getAttribute('callable') : null;
$callable = $this->getAttribute('callable');
$arguments = $this->getArguments($callable, $this->getNode('arguments'));
foreach ($arguments as $node) {
if (!$first) {
$compiler->raw(', ');
@@ -142,14 +140,23 @@ abstract class CallExpression extends AbstractExpression
throw new \LogicException($message);
}
$callableParameters = $this->getCallableParameters($callable, $isVariadic);
list($callableParameters, $isPhpVariadic) = $this->getCallableParameters($callable, $isVariadic);
$arguments = [];
$names = [];
$missingArguments = [];
$optionalArguments = [];
$pos = 0;
foreach ($callableParameters as $callableParameter) {
$names[] = $name = $this->normalizeName($callableParameter->name);
$name = $this->normalizeName($callableParameter->name);
if (\PHP_VERSION_ID >= 80000 && 'range' === $callable) {
if ('start' === $name) {
$name = 'low';
} elseif ('end' === $name) {
$name = 'high';
}
}
$names[] = $name;
if (\array_key_exists($name, $parameters)) {
if (\array_key_exists($pos, $parameters)) {
@@ -187,7 +194,7 @@ abstract class CallExpression extends AbstractExpression
}
if ($isVariadic) {
$arbitraryArguments = new ArrayExpression([], -1);
$arbitraryArguments = $isPhpVariadic ? new VariadicExpression([], -1) : new ArrayExpression([], -1);
foreach ($parameters as $key => $value) {
if (\is_int($key)) {
$arbitraryArguments->addElement($value);
@@ -230,12 +237,9 @@ abstract class CallExpression extends AbstractExpression
return strtolower(preg_replace(['/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'], ['\\1_\\2', '\\1_\\2'], $name));
}
private function getCallableParameters($callable, $isVariadic)
private function getCallableParameters($callable, bool $isVariadic): array
{
list($r) = $this->reflectCallable($callable);
if (null === $r) {
return [];
}
[$r, , $callableName] = $this->reflectCallable($callable);
$parameters = $r->getParameters();
if ($this->hasNode('node')) {
@@ -252,21 +256,21 @@ abstract class CallExpression extends AbstractExpression
array_shift($parameters);
}
}
$isPhpVariadic = false;
if ($isVariadic) {
$argument = end($parameters);
if ($argument && $argument->isArray() && $argument->isDefaultValueAvailable() && [] === $argument->getDefaultValue()) {
$isArray = $argument && $argument->hasType() && 'array' === $argument->getType()->getName();
if ($isArray && $argument->isDefaultValueAvailable() && [] === $argument->getDefaultValue()) {
array_pop($parameters);
} elseif ($argument && $argument->isVariadic()) {
array_pop($parameters);
$isPhpVariadic = true;
} else {
$callableName = $r->name;
if ($r instanceof \ReflectionMethod) {
$callableName = $r->getDeclaringClass()->name.'::'.$callableName;
}
throw new \LogicException(sprintf('The last parameter of "%s" for %s "%s" must be an array with default value, eg. "array $arg = []".', $callableName, $this->getAttribute('type'), $this->getAttribute('name')));
}
}
return $parameters;
return [$parameters, $isPhpVariadic];
}
private function reflectCallable($callable)
@@ -275,30 +279,44 @@ abstract class CallExpression extends AbstractExpression
return $this->reflector;
}
if (\is_array($callable)) {
if (!method_exists($callable[0], $callable[1])) {
// __call()
return [null, []];
}
$r = new \ReflectionMethod($callable[0], $callable[1]);
} elseif (\is_object($callable) && !$callable instanceof \Closure) {
$r = new \ReflectionObject($callable);
$r = $r->getMethod('__invoke');
$callable = [$callable, '__invoke'];
} elseif (\is_string($callable) && false !== $pos = strpos($callable, '::')) {
$class = substr($callable, 0, $pos);
$method = substr($callable, $pos + 2);
if (!method_exists($class, $method)) {
// __staticCall()
return [null, []];
}
$r = new \ReflectionMethod($callable);
$callable = [$class, $method];
} else {
$r = new \ReflectionFunction($callable);
if (\is_string($callable) && false !== $pos = strpos($callable, '::')) {
$callable = [substr($callable, 0, $pos), substr($callable, 2 + $pos)];
}
return $this->reflector = [$r, $callable];
if (\is_array($callable) && method_exists($callable[0], $callable[1])) {
$r = new \ReflectionMethod($callable[0], $callable[1]);
return $this->reflector = [$r, $callable, $r->class.'::'.$r->name];
}
$checkVisibility = $callable instanceof \Closure;
try {
$closure = \Closure::fromCallable($callable);
} catch (\TypeError $e) {
throw new \LogicException(sprintf('Callback for %s "%s" is not callable in the current scope.', $this->getAttribute('type'), $this->getAttribute('name')), 0, $e);
}
$r = new \ReflectionFunction($closure);
if (false !== strpos($r->name, '{closure}')) {
return $this->reflector = [$r, $callable, 'Closure'];
}
if ($object = $r->getClosureThis()) {
$callable = [$object, $r->name];
$callableName = (\function_exists('get_debug_type') ? get_debug_type($object) : \get_class($object)).'::'.$r->name;
} elseif (\PHP_VERSION_ID >= 80111 && $class = $r->getClosureCalledClass()) {
$callableName = $class->name.'::'.$r->name;
} elseif (\PHP_VERSION_ID < 80111 && $class = $r->getClosureScopeClass()) {
$callableName = (\is_array($callable) ? $callable[0] : $class->name).'::'.$r->name;
} else {
$callable = $callableName = $r->name;
}
if ($checkVisibility && \is_array($callable) && method_exists(...$callable) && !(new \ReflectionMethod(...$callable))->isPublic()) {
$callable = $r->getClosure();
}
return $this->reflector = [$r, $callable, $callableName];
}
}

View File

@@ -16,7 +16,7 @@ use Twig\Compiler;
class ConditionalExpression extends AbstractExpression
{
public function __construct(AbstractExpression $expr1, AbstractExpression $expr2, AbstractExpression $expr3, $lineno)
public function __construct(AbstractExpression $expr1, AbstractExpression $expr2, AbstractExpression $expr3, int $lineno)
{
parent::__construct(['expr1' => $expr1, 'expr2' => $expr2, 'expr3' => $expr3], [], $lineno);
}

View File

@@ -16,7 +16,7 @@ use Twig\Compiler;
class ConstantExpression extends AbstractExpression
{
public function __construct($value, $lineno)
public function __construct($value, int $lineno)
{
parent::__construct([], ['value' => $value], $lineno);
}

View File

@@ -29,7 +29,7 @@ use Twig\Node\Node;
*/
class DefaultFilter extends FilterExpression
{
public function __construct(\Twig_NodeInterface $node, ConstantExpression $filterName, \Twig_NodeInterface $arguments, $lineno, $tag = null)
public function __construct(Node $node, ConstantExpression $filterName, Node $arguments, int $lineno, string $tag = null)
{
$default = new FilterExpression($node, new ConstantExpression('default', $node->getTemplateLine()), $arguments, $node->getTemplateLine());

View File

@@ -13,11 +13,11 @@
namespace Twig\Node\Expression;
use Twig\Compiler;
use Twig\TwigFilter;
use Twig\Node\Node;
class FilterExpression extends CallExpression
{
public function __construct(\Twig_NodeInterface $node, ConstantExpression $filterName, \Twig_NodeInterface $arguments, $lineno, $tag = null)
public function __construct(Node $node, ConstantExpression $filterName, Node $arguments, int $lineno, string $tag = null)
{
parent::__construct(['node' => $node, 'filter' => $filterName, 'arguments' => $arguments], [], $lineno, $tag);
}
@@ -29,16 +29,11 @@ class FilterExpression extends CallExpression
$this->setAttribute('name', $name);
$this->setAttribute('type', 'filter');
$this->setAttribute('thing', $filter);
$this->setAttribute('needs_environment', $filter->needsEnvironment());
$this->setAttribute('needs_context', $filter->needsContext());
$this->setAttribute('arguments', $filter->getArguments());
if ($filter instanceof \Twig_FilterCallableInterface || $filter instanceof TwigFilter) {
$this->setAttribute('callable', $filter->getCallable());
}
if ($filter instanceof TwigFilter) {
$this->setAttribute('is_variadic', $filter->isVariadic());
}
$this->setAttribute('callable', $filter->getCallable());
$this->setAttribute('is_variadic', $filter->isVariadic());
$this->compileCallable($compiler);
}

View File

@@ -12,11 +12,11 @@
namespace Twig\Node\Expression;
use Twig\Compiler;
use Twig\TwigFunction;
use Twig\Node\Node;
class FunctionExpression extends CallExpression
{
public function __construct($name, \Twig_NodeInterface $arguments, $lineno)
public function __construct(string $name, Node $arguments, int $lineno)
{
parent::__construct(['arguments' => $arguments], ['name' => $name, 'is_defined_test' => false], $lineno);
}
@@ -28,21 +28,15 @@ class FunctionExpression extends CallExpression
$this->setAttribute('name', $name);
$this->setAttribute('type', 'function');
$this->setAttribute('thing', $function);
$this->setAttribute('needs_environment', $function->needsEnvironment());
$this->setAttribute('needs_context', $function->needsContext());
$this->setAttribute('arguments', $function->getArguments());
if ($function instanceof \Twig_FunctionCallableInterface || $function instanceof TwigFunction) {
$callable = $function->getCallable();
if ('constant' === $name && $this->getAttribute('is_defined_test')) {
$callable = 'twig_constant_is_defined';
}
$this->setAttribute('callable', $callable);
}
if ($function instanceof TwigFunction) {
$this->setAttribute('is_variadic', $function->isVariadic());
$callable = $function->getCallable();
if ('constant' === $name && $this->getAttribute('is_defined_test')) {
$callable = 'twig_constant_is_defined';
}
$this->setAttribute('callable', $callable);
$this->setAttribute('is_variadic', $function->isVariadic());
$this->compileCallable($compiler);
}

View File

@@ -13,67 +13,76 @@
namespace Twig\Node\Expression;
use Twig\Compiler;
use Twig\Extension\SandboxExtension;
use Twig\Template;
class GetAttrExpression extends AbstractExpression
{
public function __construct(AbstractExpression $node, AbstractExpression $attribute, AbstractExpression $arguments = null, $type, $lineno)
public function __construct(AbstractExpression $node, AbstractExpression $attribute, ?AbstractExpression $arguments, string $type, int $lineno)
{
$nodes = ['node' => $node, 'attribute' => $attribute];
if (null !== $arguments) {
$nodes['arguments'] = $arguments;
}
parent::__construct($nodes, ['type' => $type, 'is_defined_test' => false, 'ignore_strict_check' => false, 'disable_c_ext' => false], $lineno);
parent::__construct($nodes, ['type' => $type, 'is_defined_test' => false, 'ignore_strict_check' => false, 'optimizable' => true], $lineno);
}
public function compile(Compiler $compiler)
{
if ($this->getAttribute('disable_c_ext')) {
@trigger_error(sprintf('Using the "disable_c_ext" attribute on %s is deprecated since version 1.30 and will be removed in 2.0.', __CLASS__), E_USER_DEPRECATED);
$env = $compiler->getEnvironment();
// optimize array calls
if (
$this->getAttribute('optimizable')
&& (!$env->isStrictVariables() || $this->getAttribute('ignore_strict_check'))
&& !$this->getAttribute('is_defined_test')
&& Template::ARRAY_CALL === $this->getAttribute('type')
) {
$var = '$'.$compiler->getVarName();
$compiler
->raw('(('.$var.' = ')
->subcompile($this->getNode('node'))
->raw(') && is_array(')
->raw($var)
->raw(') || ')
->raw($var)
->raw(' instanceof ArrayAccess ? (')
->raw($var)
->raw('[')
->subcompile($this->getNode('attribute'))
->raw('] ?? null) : null)')
;
return;
}
if (\function_exists('twig_template_get_attributes') && !$this->getAttribute('disable_c_ext')) {
$compiler->raw('twig_template_get_attributes($this, ');
} else {
$compiler->raw('$this->getAttribute(');
}
$compiler->raw('twig_get_attribute($this->env, $this->source, ');
if ($this->getAttribute('ignore_strict_check')) {
$this->getNode('node')->setAttribute('ignore_strict_check', true);
}
$compiler->subcompile($this->getNode('node'));
$compiler
->subcompile($this->getNode('node'))
->raw(', ')
->subcompile($this->getNode('attribute'))
;
$compiler->raw(', ')->subcompile($this->getNode('attribute'));
// only generate optional arguments when needed (to make generated code more readable)
$needFourth = $this->getAttribute('ignore_strict_check');
$needThird = $needFourth || $this->getAttribute('is_defined_test');
$needSecond = $needThird || Template::ANY_CALL !== $this->getAttribute('type');
$needFirst = $needSecond || $this->hasNode('arguments');
if ($needFirst) {
if ($this->hasNode('arguments')) {
$compiler->raw(', ')->subcompile($this->getNode('arguments'));
} else {
$compiler->raw(', []');
}
if ($this->hasNode('arguments')) {
$compiler->raw(', ')->subcompile($this->getNode('arguments'));
} else {
$compiler->raw(', []');
}
if ($needSecond) {
$compiler->raw(', ')->repr($this->getAttribute('type'));
}
if ($needThird) {
$compiler->raw(', ')->repr($this->getAttribute('is_defined_test'));
}
if ($needFourth) {
$compiler->raw(', ')->repr($this->getAttribute('ignore_strict_check'));
}
$compiler->raw(')');
$compiler->raw(', ')
->repr($this->getAttribute('type'))
->raw(', ')->repr($this->getAttribute('is_defined_test'))
->raw(', ')->repr($this->getAttribute('ignore_strict_check'))
->raw(', ')->repr($env->hasExtension(SandboxExtension::class))
->raw(', ')->repr($this->getNode('node')->getTemplateLine())
->raw(')')
;
}
}

View File

@@ -15,9 +15,9 @@ use Twig\Compiler;
class MethodCallExpression extends AbstractExpression
{
public function __construct(AbstractExpression $node, $method, ArrayExpression $arguments, $lineno)
public function __construct(AbstractExpression $node, string $method, ArrayExpression $arguments, int $lineno)
{
parent::__construct(['node' => $node, 'arguments' => $arguments], ['method' => $method, 'safe' => false], $lineno);
parent::__construct(['node' => $node, 'arguments' => $arguments], ['method' => $method, 'safe' => false, 'is_defined_test' => false], $lineno);
if ($node instanceof NameExpression) {
$node->setAttribute('always_defined', true);
@@ -26,11 +26,24 @@ class MethodCallExpression extends AbstractExpression
public function compile(Compiler $compiler)
{
if ($this->getAttribute('is_defined_test')) {
$compiler
->raw('method_exists($macros[')
->repr($this->getNode('node')->getAttribute('name'))
->raw('], ')
->repr($this->getAttribute('method'))
->raw(')')
;
return;
}
$compiler
->subcompile($this->getNode('node'))
->raw('->')
->raw($this->getAttribute('method'))
->raw('(')
->raw('twig_call_macro($macros[')
->repr($this->getNode('node')->getAttribute('name'))
->raw('], ')
->repr($this->getAttribute('method'))
->raw(', [')
;
$first = true;
foreach ($this->getNode('arguments')->getKeyValuePairs() as $pair) {
@@ -41,7 +54,10 @@ class MethodCallExpression extends AbstractExpression
$compiler->subcompile($pair['value']);
}
$compiler->raw(')');
$compiler
->raw('], ')
->repr($this->getTemplateLine())
->raw(', $context, $this->getSourceContext())');
}
}

View File

@@ -16,13 +16,13 @@ use Twig\Compiler;
class NameExpression extends AbstractExpression
{
protected $specialVars = [
'_self' => '$this',
private $specialVars = [
'_self' => '$this->getTemplateName()',
'_context' => '$context',
'_charset' => '$this->env->getCharset()',
];
public function __construct($name, $lineno)
public function __construct(string $name, int $lineno)
{
parent::__construct([], ['name' => $name, 'is_defined_test' => false, 'ignore_strict_check' => false, 'always_defined' => false], $lineno);
}
@@ -36,7 +36,7 @@ class NameExpression extends AbstractExpression
if ($this->getAttribute('is_defined_test')) {
if ($this->isSpecial()) {
$compiler->repr(true);
} elseif (\PHP_VERSION_ID >= 700400) {
} elseif (\PHP_VERSION_ID >= 70400) {
$compiler
->raw('array_key_exists(')
->string($name)
@@ -60,45 +60,25 @@ class NameExpression extends AbstractExpression
->raw(']')
;
} else {
if (\PHP_VERSION_ID >= 70000) {
// use PHP 7 null coalescing operator
if ($this->getAttribute('ignore_strict_check') || !$compiler->getEnvironment()->isStrictVariables()) {
$compiler
->raw('($context[')
->string($name)
->raw('] ?? ')
->raw('] ?? null)')
;
if ($this->getAttribute('ignore_strict_check') || !$compiler->getEnvironment()->isStrictVariables()) {
$compiler->raw('null)');
} else {
$compiler->raw('$this->getContext($context, ')->string($name)->raw('))');
}
} elseif (\PHP_VERSION_ID >= 50400) {
// PHP 5.4 ternary operator performance was optimized
} else {
$compiler
->raw('(isset($context[')
->string($name)
->raw(']) ? $context[')
->raw(']) || array_key_exists(')
->string($name)
->raw('] : ')
;
if ($this->getAttribute('ignore_strict_check') || !$compiler->getEnvironment()->isStrictVariables()) {
$compiler->raw('null)');
} else {
$compiler->raw('$this->getContext($context, ')->string($name)->raw('))');
}
} else {
$compiler
->raw('$this->getContext($context, ')
->raw(', $context) ? $context[')
->string($name)
;
if ($this->getAttribute('ignore_strict_check')) {
$compiler->raw(', true');
}
$compiler
->raw('] : (function () { throw new RuntimeError(\'Variable ')
->string($name)
->raw(' does not exist.\', ')
->repr($this->lineno)
->raw(', $this->source); })()')
->raw(')')
;
}

View File

@@ -20,7 +20,7 @@ use Twig\Node\Node;
class NullCoalesceExpression extends ConditionalExpression
{
public function __construct(\Twig_NodeInterface $left, \Twig_NodeInterface $right, $lineno)
public function __construct(Node $left, Node $right, int $lineno)
{
$test = new DefinedTest(clone $left, 'defined', new Node(), $left->getTemplateLine());
// for "block()", we don't need the null test as the return value is always a string
@@ -44,7 +44,7 @@ class NullCoalesceExpression extends ConditionalExpression
* cases might be implemented as an optimizer node visitor, but has not been done
* as benefits are probably not worth the added complexity.
*/
if (\PHP_VERSION_ID >= 70000 && $this->getNode('expr2') instanceof NameExpression) {
if ($this->getNode('expr2') instanceof NameExpression) {
$this->getNode('expr2')->setAttribute('always_defined', true);
$compiler
->raw('((')

View File

@@ -21,7 +21,7 @@ use Twig\Compiler;
*/
class ParentExpression extends AbstractExpression
{
public function __construct($name, $lineno, $tag = null)
public function __construct(string $name, int $lineno, string $tag = null)
{
parent::__construct([], ['output' => false, 'name' => $name], $lineno, $tag);
}

View File

@@ -15,7 +15,7 @@ use Twig\Compiler;
class TempNameExpression extends AbstractExpression
{
public function __construct($name, $lineno)
public function __construct(string $name, int $lineno)
{
parent::__construct([], ['name' => $name], $lineno);
}

View File

@@ -18,8 +18,10 @@ use Twig\Node\Expression\BlockReferenceExpression;
use Twig\Node\Expression\ConstantExpression;
use Twig\Node\Expression\FunctionExpression;
use Twig\Node\Expression\GetAttrExpression;
use Twig\Node\Expression\MethodCallExpression;
use Twig\Node\Expression\NameExpression;
use Twig\Node\Expression\TestExpression;
use Twig\Node\Node;
/**
* Checks if a variable is defined in the current context.
@@ -33,7 +35,7 @@ use Twig\Node\Expression\TestExpression;
*/
class DefinedTest extends TestExpression
{
public function __construct(\Twig_NodeInterface $node, $name, \Twig_NodeInterface $arguments = null, $lineno)
public function __construct(Node $node, string $name, ?Node $arguments, int $lineno)
{
if ($node instanceof NameExpression) {
$node->setAttribute('is_defined_test', true);
@@ -46,6 +48,8 @@ class DefinedTest extends TestExpression
$node->setAttribute('is_defined_test', true);
} elseif ($node instanceof ConstantExpression || $node instanceof ArrayExpression) {
$node = new ConstantExpression(true, $node->getTemplateLine());
} elseif ($node instanceof MethodCallExpression) {
$node->setAttribute('is_defined_test', true);
} else {
throw new SyntaxError('The "defined" test only works with simple variables.', $lineno);
}
@@ -53,8 +57,9 @@ class DefinedTest extends TestExpression
parent::__construct($node, $name, $arguments, $lineno);
}
protected function changeIgnoreStrictCheck(GetAttrExpression $node)
private function changeIgnoreStrictCheck(GetAttrExpression $node)
{
$node->setAttribute('optimizable', false);
$node->setAttribute('ignore_strict_check', true);
if ($node->getNode('node') instanceof GetAttrExpression) {

View File

@@ -28,7 +28,7 @@ class OddTest extends TestExpression
$compiler
->raw('(')
->subcompile($this->getNode('node'))
->raw(' % 2 == 1')
->raw(' % 2 != 0')
->raw(')')
;
}

View File

@@ -12,11 +12,11 @@
namespace Twig\Node\Expression;
use Twig\Compiler;
use Twig\TwigTest;
use Twig\Node\Node;
class TestExpression extends CallExpression
{
public function __construct(\Twig_NodeInterface $node, $name, \Twig_NodeInterface $arguments = null, $lineno)
public function __construct(Node $node, string $name, ?Node $arguments, int $lineno)
{
$nodes = ['node' => $node];
if (null !== $arguments) {
@@ -33,16 +33,9 @@ class TestExpression extends CallExpression
$this->setAttribute('name', $name);
$this->setAttribute('type', 'test');
$this->setAttribute('thing', $test);
if ($test instanceof TwigTest) {
$this->setAttribute('arguments', $test->getArguments());
}
if ($test instanceof \Twig_TestCallableInterface || $test instanceof TwigTest) {
$this->setAttribute('callable', $test->getCallable());
}
if ($test instanceof TwigTest) {
$this->setAttribute('is_variadic', $test->isVariadic());
}
$this->setAttribute('arguments', $test->getArguments());
$this->setAttribute('callable', $test->getCallable());
$this->setAttribute('is_variadic', $test->isVariadic());
$this->compileCallable($compiler);
}

View File

@@ -14,10 +14,11 @@ namespace Twig\Node\Expression\Unary;
use Twig\Compiler;
use Twig\Node\Expression\AbstractExpression;
use Twig\Node\Node;
abstract class AbstractUnary extends AbstractExpression
{
public function __construct(\Twig_NodeInterface $node, $lineno)
public function __construct(Node $node, int $lineno)
{
parent::__construct(['node' => $node], [], $lineno);
}

View File

@@ -0,0 +1,24 @@
<?php
/*
* This file is part of Twig.
*
* (c) Fabien Potencier
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Twig\Node\Expression;
use Twig\Compiler;
class VariadicExpression extends ArrayExpression
{
public function compile(Compiler $compiler)
{
$compiler->raw('...');
parent::compile($compiler);
}
}

View File

@@ -20,7 +20,7 @@ use Twig\Compiler;
*/
class FlushNode extends Node
{
public function __construct($lineno, $tag)
public function __construct(int $lineno, string $tag)
{
parent::__construct([], [], $lineno, $tag);
}

View File

@@ -20,7 +20,7 @@ use Twig\Compiler;
*/
class ForLoopNode extends Node
{
public function __construct($lineno, $tag = null)
public function __construct(int $lineno, string $tag = null)
{
parent::__construct([], ['with_loop' => false, 'ifexpr' => false, 'else' => false], $lineno, $tag);
}

View File

@@ -23,9 +23,9 @@ use Twig\Node\Expression\AssignNameExpression;
*/
class ForNode extends Node
{
protected $loop;
private $loop;
public function __construct(AssignNameExpression $keyTarget, AssignNameExpression $valueTarget, AbstractExpression $seq, AbstractExpression $ifexpr = null, \Twig_NodeInterface $body, \Twig_NodeInterface $else = null, $lineno, $tag = null)
public function __construct(AssignNameExpression $keyTarget, AssignNameExpression $valueTarget, AbstractExpression $seq, ?AbstractExpression $ifexpr, Node $body, ?Node $else, int $lineno, string $tag = null)
{
$body = new Node([$body, $this->loop = new ForLoopNode($lineno, $tag)]);

View File

@@ -21,7 +21,7 @@ use Twig\Compiler;
*/
class IfNode extends Node
{
public function __construct(\Twig_NodeInterface $tests, \Twig_NodeInterface $else = null, $lineno, $tag = null)
public function __construct(Node $tests, ?Node $else, int $lineno, string $tag = null)
{
$nodes = ['tests' => $tests];
if (null !== $else) {
@@ -50,8 +50,11 @@ class IfNode extends Node
->subcompile($this->getNode('tests')->getNode($i))
->raw(") {\n")
->indent()
->subcompile($this->getNode('tests')->getNode($i + 1))
;
// The node might not exists if the content is empty
if ($this->getNode('tests')->hasNode($i + 1)) {
$compiler->subcompile($this->getNode('tests')->getNode($i + 1));
}
}
if ($this->hasNode('else')) {

View File

@@ -22,20 +22,28 @@ use Twig\Node\Expression\NameExpression;
*/
class ImportNode extends Node
{
public function __construct(AbstractExpression $expr, AbstractExpression $var, $lineno, $tag = null)
public function __construct(AbstractExpression $expr, AbstractExpression $var, int $lineno, string $tag = null, bool $global = true)
{
parent::__construct(['expr' => $expr, 'var' => $var], [], $lineno, $tag);
parent::__construct(['expr' => $expr, 'var' => $var], ['global' => $global], $lineno, $tag);
}
public function compile(Compiler $compiler)
{
$compiler
->addDebugInfo($this)
->write('')
->subcompile($this->getNode('var'))
->raw(' = ')
->write('$macros[')
->repr($this->getNode('var')->getAttribute('name'))
->raw('] = ')
;
if ($this->getAttribute('global')) {
$compiler
->raw('$this->macros[')
->repr($this->getNode('var')->getAttribute('name'))
->raw('] = ')
;
}
if ($this->getNode('expr') instanceof NameExpression && '_self' === $this->getNode('expr')->getAttribute('name')) {
$compiler->raw('$this');
} else {

View File

@@ -22,7 +22,7 @@ use Twig\Node\Expression\AbstractExpression;
*/
class IncludeNode extends Node implements NodeOutputInterface
{
public function __construct(AbstractExpression $expr, AbstractExpression $variables = null, $only = false, $ignoreMissing = false, $lineno, $tag = null)
public function __construct(AbstractExpression $expr, ?AbstractExpression $variables, bool $only, bool $ignoreMissing, int $lineno, string $tag = null)
{
$nodes = ['expr' => $expr];
if (null !== $variables) {

View File

@@ -21,9 +21,9 @@ use Twig\Error\SyntaxError;
*/
class MacroNode extends Node
{
const VARARGS_NAME = 'varargs';
public const VARARGS_NAME = 'varargs';
public function __construct($name, \Twig_NodeInterface $body, \Twig_NodeInterface $arguments, $lineno, $tag = null)
public function __construct(string $name, Node $body, Node $arguments, int $lineno, string $tag = null)
{
foreach ($arguments as $argumentName => $argument) {
if (self::VARARGS_NAME === $argumentName) {
@@ -38,7 +38,7 @@ class MacroNode extends Node
{
$compiler
->addDebugInfo($this)
->write(sprintf('public function get%s(', $this->getAttribute('name')))
->write(sprintf('public function macro_%s(', $this->getAttribute('name')))
;
$count = \count($this->getNode('arguments'));
@@ -54,21 +54,16 @@ class MacroNode extends Node
}
}
if (\PHP_VERSION_ID >= 50600) {
if ($count) {
$compiler->raw(', ');
}
$compiler->raw('...$__varargs__');
if ($count) {
$compiler->raw(', ');
}
$compiler
->raw('...$__varargs__')
->raw(")\n")
->write("{\n")
->indent()
;
$compiler
->write("\$macros = \$this->macros;\n")
->write("\$context = \$this->env->mergeGlobals([\n")
->indent()
;
@@ -88,19 +83,8 @@ class MacroNode extends Node
->raw(' => ')
;
if (\PHP_VERSION_ID >= 50600) {
$compiler->raw("\$__varargs__,\n");
} else {
$compiler
->raw('func_num_args() > ')
->repr($count)
->raw(' ? array_slice(func_get_args(), ')
->repr($count)
->raw(") : [],\n")
;
}
$compiler
->raw("\$__varargs__,\n")
->outdent()
->write("]);\n\n")
->write("\$blocks = [];\n\n")
@@ -114,19 +98,14 @@ class MacroNode extends Node
->write("try {\n")
->indent()
->subcompile($this->getNode('body'))
->raw("\n")
->write("return ('' === \$tmp = ob_get_contents()) ? '' : new Markup(\$tmp, \$this->env->getCharset());\n")
->outdent()
->write("} catch (\Exception \$e) {\n")
->write("} finally {\n")
->indent()
->write("ob_end_clean();\n\n")
->write("throw \$e;\n")
->write("ob_end_clean();\n")
->outdent()
->write("} catch (\Throwable \$e) {\n")
->indent()
->write("ob_end_clean();\n\n")
->write("throw \$e;\n")
->outdent()
->write("}\n\n")
->write("return ('' === \$tmp = ob_get_clean()) ? '' : new Markup(\$tmp, \$this->env->getCharset());\n")
->write("}\n")
->outdent()
->write("}\n\n")
;

View File

@@ -25,16 +25,15 @@ use Twig\Source;
* display_end, constructor_start, constructor_end, and class_end.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @final since Twig 2.4.0
*/
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 = '')
public function __construct(Node $body, ?AbstractExpression $parent, Node $blocks, Node $macros, Node $traits, $embeddedTemplates, Source $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;
if (__CLASS__ !== static::class) {
@trigger_error('Overriding '.__CLASS__.' is deprecated since Twig 2.4.0 and the class will be final in 3.0.', \E_USER_DEPRECATED);
}
$nodes = [
@@ -54,16 +53,11 @@ class ModuleNode extends Node
// 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);
}
@@ -89,16 +83,7 @@ class ModuleNode extends Node
$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->compileConstructor($compiler);
$this->compileGetParent($compiler);
@@ -114,8 +99,6 @@ class ModuleNode extends Node
$this->compileDebugInfo($compiler);
$this->compileGetSource($compiler);
$this->compileGetSourceContext($compiler);
$this->compileClassFooter($compiler);
@@ -166,6 +149,7 @@ class ModuleNode extends Node
->write("use Twig\Environment;\n")
->write("use Twig\Error\LoaderError;\n")
->write("use Twig\Error\RuntimeError;\n")
->write("use Twig\Extension\SandboxExtension;\n")
->write("use Twig\Markup;\n")
->write("use Twig\Sandbox\SecurityError;\n")
->write("use Twig\Sandbox\SecurityNotAllowedTagError;\n")
@@ -179,9 +163,11 @@ class ModuleNode extends Node
// 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()))
->raw(sprintf(" extends %s\n", $compiler->getEnvironment()->getBaseTemplateClass(false)))
->write("{\n")
->indent()
->write("private \$source;\n")
->write("private \$macros = [];\n\n")
;
}
@@ -192,6 +178,7 @@ class ModuleNode extends Node
->indent()
->subcompile($this->getNode('constructor_start'))
->write("parent::__construct(\$env);\n\n")
->write("\$this->source = \$this->getSourceContext();\n\n")
;
// parent
@@ -203,18 +190,24 @@ class ModuleNode extends Node
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('$_trait_%s = $this->loadTemplate(', $i))
->subcompile($node)
->raw(', ')
->repr($node->getTemplateName())
->raw(', ')
->repr($node->getTemplateLine())
->raw(");\n")
->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")
->raw(", \$this->source);\n")
->outdent()
->write("}\n")
->write(sprintf("\$_trait_%s_blocks = \$_trait_%s->getBlocks();\n\n", $i, $i))
@@ -226,13 +219,13 @@ class ModuleNode extends Node
->string($key)
->raw("])) {\n")
->indent()
->write("throw new RuntimeError(sprintf('Block ")
->write("throw new RuntimeError('Block ")
->string($key)
->raw(' is not defined in trait ')
->subcompile($trait->getNode('template'))
->raw(".'), ")
->raw(".', ")
->repr($node->getTemplateLine())
->raw(", \$this->getSourceContext());\n")
->raw(", \$this->source);\n")
->outdent()
->write("}\n\n")
@@ -318,6 +311,7 @@ class ModuleNode extends Node
$compiler
->write("protected function doDisplay(array \$context, array \$blocks = [])\n", "{\n")
->indent()
->write("\$macros = \$this->macros;\n")
->subcompile($this->getNode('display_start'))
->subcompile($this->getNode('body'))
;
@@ -440,20 +434,6 @@ class ModuleNode extends Node
;
}
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

View File

@@ -20,7 +20,7 @@ use Twig\Source;
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class Node implements \Twig_NodeInterface
class Node implements \Countable, \IteratorAggregate
{
protected $nodes;
protected $attributes;
@@ -36,11 +36,11 @@ class Node implements \Twig_NodeInterface
* @param int $lineno The line number
* @param string $tag The tag name associated with the Node
*/
public function __construct(array $nodes = [], array $attributes = [], $lineno = 0, $tag = null)
public function __construct(array $nodes = [], array $attributes = [], int $lineno = 0, string $tag = null)
{
foreach ($nodes as $name => $node) {
if (!$node instanceof \Twig_NodeInterface) {
@trigger_error(sprintf('Using "%s" for the value of node "%s" of "%s" is deprecated since version 1.25 and will be removed in 2.0.', \is_object($node) ? \get_class($node) : (null === $node ? 'null' : \gettype($node)), $name, \get_class($this)), E_USER_DEPRECATED);
if (!$node instanceof self) {
throw new \InvalidArgumentException(sprintf('Using "%s" for the value of node "%s" of "%s" is not supported. You must pass a \Twig\Node\Node instance.', \is_object($node) ? \get_class($node) : (null === $node ? 'null' : \gettype($node)), $name, static::class));
}
}
$this->nodes = $nodes;
@@ -56,7 +56,7 @@ class Node implements \Twig_NodeInterface
$attributes[] = sprintf('%s: %s', $name, str_replace("\n", '', var_export($value, true)));
}
$repr = [\get_class($this).'('.implode(', ', $attributes)];
$repr = [static::class.'('.implode(', ', $attributes)];
if (\count($this->nodes)) {
foreach ($this->nodes as $name => $node) {
@@ -77,41 +77,6 @@ class Node implements \Twig_NodeInterface
return implode("\n", $repr);
}
/**
* @deprecated since 1.16.1 (to be removed in 2.0)
*/
public function toXml($asDom = false)
{
@trigger_error(sprintf('%s is deprecated since version 1.16.1 and will be removed in 2.0.', __METHOD__), E_USER_DEPRECATED);
$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->formatOutput = true;
$dom->appendChild($xml = $dom->createElement('twig'));
$xml->appendChild($node = $dom->createElement('node'));
$node->setAttribute('class', \get_class($this));
foreach ($this->attributes as $name => $value) {
$node->appendChild($attribute = $dom->createElement('attribute'));
$attribute->setAttribute('name', $name);
$attribute->appendChild($dom->createTextNode($value));
}
foreach ($this->nodes as $name => $n) {
if (null === $n) {
continue;
}
$child = $n->toXml(true)->getElementsByTagName('node')->item(0);
$child = $dom->importNode($child, true);
$child->setAttribute('name', $name);
$node->appendChild($child);
}
return $asDom ? $dom : $dom->saveXML();
}
public function compile(Compiler $compiler)
{
foreach ($this->nodes as $node) {
@@ -124,16 +89,6 @@ class Node implements \Twig_NodeInterface
return $this->lineno;
}
/**
* @deprecated since 1.27 (to be removed in 2.0)
*/
public function getLine()
{
@trigger_error('The '.__METHOD__.' method is deprecated since version 1.27 and will be removed in 2.0. Use getTemplateLine() instead.', E_USER_DEPRECATED);
return $this->lineno;
}
public function getNodeTag()
{
return $this->tag;
@@ -153,7 +108,7 @@ class Node implements \Twig_NodeInterface
public function getAttribute($name)
{
if (!\array_key_exists($name, $this->attributes)) {
throw new \LogicException(sprintf('Attribute "%s" does not exist for Node "%s".', $name, \get_class($this)));
throw new \LogicException(sprintf('Attribute "%s" does not exist for Node "%s".', $name, static::class));
}
return $this->attributes[$name];
@@ -178,7 +133,7 @@ class Node implements \Twig_NodeInterface
*/
public function hasNode($name)
{
return \array_key_exists($name, $this->nodes);
return isset($this->nodes[$name]);
}
/**
@@ -186,19 +141,15 @@ class Node implements \Twig_NodeInterface
*/
public function getNode($name)
{
if (!\array_key_exists($name, $this->nodes)) {
throw new \LogicException(sprintf('Node "%s" does not exist for Node "%s".', $name, \get_class($this)));
if (!isset($this->nodes[$name])) {
throw new \LogicException(sprintf('Node "%s" does not exist for Node "%s".', $name, static::class));
}
return $this->nodes[$name];
}
public function setNode($name, $node = null)
public function setNode($name, self $node)
{
if (!$node instanceof \Twig_NodeInterface) {
@trigger_error(sprintf('Using "%s" for the value of node "%s" of "%s" is deprecated since version 1.25 and will be removed in 2.0.', \is_object($node) ? \get_class($node) : (null === $node ? 'null' : \gettype($node)), $name, \get_class($this)), E_USER_DEPRECATED);
}
$this->nodes[$name] = $node;
}
@@ -207,65 +158,59 @@ class Node implements \Twig_NodeInterface
unset($this->nodes[$name]);
}
/**
* @return int
*/
#[\ReturnTypeWillChange]
public function count()
{
return \count($this->nodes);
}
/**
* @return \Traversable
*/
#[\ReturnTypeWillChange]
public function getIterator()
{
return new \ArrayIterator($this->nodes);
}
public function setTemplateName($name)
/**
* @deprecated since 2.8 (to be removed in 3.0)
*/
public function setTemplateName($name/*, $triggerDeprecation = true */)
{
$triggerDeprecation = 2 > \func_num_args() || \func_get_arg(1);
if ($triggerDeprecation) {
@trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0. Use setSourceContext() instead.', \E_USER_DEPRECATED);
}
$this->name = $name;
foreach ($this->nodes as $node) {
if (null !== $node) {
$node->setTemplateName($name);
}
$node->setTemplateName($name, $triggerDeprecation);
}
}
public function getTemplateName()
{
return $this->name;
return $this->sourceContext ? $this->sourceContext->getName() : null;
}
public function setSourceContext(Source $source)
{
$this->sourceContext = $source;
foreach ($this->nodes as $node) {
if ($node instanceof Node) {
$node->setSourceContext($source);
}
$node->setSourceContext($source);
}
$this->setTemplateName($source->getName(), false);
}
public function getSourceContext()
{
return $this->sourceContext;
}
/**
* @deprecated since 1.27 (to be removed in 2.0)
*/
public function setFilename($name)
{
@trigger_error('The '.__METHOD__.' method is deprecated since version 1.27 and will be removed in 2.0. Use setTemplateName() instead.', E_USER_DEPRECATED);
$this->setTemplateName($name);
}
/**
* @deprecated since 1.27 (to be removed in 2.0)
*/
public function getFilename()
{
@trigger_error('The '.__METHOD__.' method is deprecated since version 1.27 and will be removed in 2.0. Use getTemplateName() instead.', E_USER_DEPRECATED);
return $this->name;
}
}
class_alias('Twig\Node\Node', 'Twig_Node');

View File

@@ -22,7 +22,7 @@ use Twig\Node\Expression\AbstractExpression;
*/
class PrintNode extends Node implements NodeOutputInterface
{
public function __construct(AbstractExpression $expr, $lineno, $tag = null)
public function __construct(AbstractExpression $expr, int $lineno, string $tag = null)
{
parent::__construct(['expr' => $expr], [], $lineno, $tag);
}

View File

@@ -20,7 +20,7 @@ use Twig\Compiler;
*/
class SandboxNode extends Node
{
public function __construct(\Twig_NodeInterface $body, $lineno, $tag = null)
public function __construct(Node $body, int $lineno, string $tag = null)
{
parent::__construct(['body' => $body], [], $lineno, $tag);
}
@@ -34,12 +34,19 @@ class SandboxNode extends Node
->write("\$this->sandbox->enableSandbox();\n")
->outdent()
->write("}\n")
->write("try {\n")
->indent()
->subcompile($this->getNode('body'))
->outdent()
->write("} finally {\n")
->indent()
->write("if (!\$alreadySandboxed) {\n")
->indent()
->write("\$this->sandbox->disableSandbox();\n")
->outdent()
->write("}\n")
->outdent()
->write("}\n")
;
}
}

View File

@@ -13,7 +13,6 @@ namespace Twig\Node;
use Twig\Compiler;
use Twig\Node\Expression\ConstantExpression;
use Twig\Node\Expression\FilterExpression;
/**
* Adds a check for the __toString() method when the variable is an object and the sandbox is activated.
@@ -42,28 +41,14 @@ class SandboxedPrintNode extends PrintNode
;
} else {
$compiler
->write('$this->env->getExtension(\'\Twig\Extension\SandboxExtension\')->ensureToStringAllowed(')
->write('$this->extensions[SandboxExtension::class]->ensureToStringAllowed(')
->subcompile($expr)
->raw(");\n")
->raw(', ')
->repr($expr->getTemplateLine())
->raw(", \$this->source);\n")
;
}
}
/**
* Removes node filters.
*
* This is mostly needed when another visitor adds filters (like the escaper one).
*
* @return Node
*/
protected function removeNodeFilter(Node $node)
{
if ($node instanceof FilterExpression) {
return $this->removeNodeFilter($node->getNode('node'));
}
return $node;
}
}
class_alias('Twig\Node\SandboxedPrintNode', 'Twig_Node_SandboxedPrint');

View File

@@ -21,7 +21,7 @@ use Twig\Node\Expression\ConstantExpression;
*/
class SetNode extends Node implements NodeCaptureInterface
{
public function __construct($capture, \Twig_NodeInterface $names, \Twig_NodeInterface $values, $lineno, $tag = null)
public function __construct(bool $capture, Node $names, Node $values, int $lineno, string $tag = null)
{
parent::__construct(['names' => $names, 'values' => $values], ['capture' => $capture, 'safe' => false], $lineno, $tag);

View File

@@ -18,11 +18,13 @@ use Twig\Compiler;
*
* It removes spaces between HTML tags.
*
* @deprecated since Twig 2.7, to be removed in 3.0
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class SpacelessNode extends Node
class SpacelessNode extends Node implements NodeOutputInterface
{
public function __construct(\Twig_NodeInterface $body, $lineno, $tag = 'spaceless')
public function __construct(Node $body, int $lineno, string $tag = 'spaceless')
{
parent::__construct(['body' => $body], [], $lineno, $tag);
}

View File

@@ -21,7 +21,7 @@ use Twig\Compiler;
*/
class TextNode extends Node implements NodeOutputInterface
{
public function __construct($data, $lineno)
public function __construct(string $data, int $lineno)
{
parent::__construct([], ['data' => $data], $lineno);
}

View File

@@ -20,14 +20,14 @@ use Twig\Compiler;
*/
class WithNode extends Node
{
public function __construct(Node $body, Node $variables = null, $only = false, $lineno, $tag = null)
public function __construct(Node $body, ?Node $variables, bool $only, int $lineno, string $tag = null)
{
$nodes = ['body' => $body];
if (null !== $variables) {
$nodes['variables'] = $variables;
}
parent::__construct($nodes, ['only' => (bool) $only], $lineno, $tag);
parent::__construct($nodes, ['only' => $only], $lineno, $tag);
}
public function compile(Compiler $compiler)