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.
This commit is contained in:
slawkens
2020-02-15 05:41:38 +01:00
parent d9e449b6cf
commit 8021308822
414 changed files with 9276 additions and 5531 deletions

View File

@@ -0,0 +1,59 @@
<?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\NodeVisitor;
use Twig\Environment;
use Twig\Node\Node;
/**
* Used to make node visitors compatible with Twig 1.x and 2.x.
*
* To be removed in Twig 3.1.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
abstract class AbstractNodeVisitor implements NodeVisitorInterface
{
final public function enterNode(\Twig_NodeInterface $node, Environment $env)
{
if (!$node instanceof Node) {
throw new \LogicException(sprintf('%s only supports \Twig\Node\Node instances.', __CLASS__));
}
return $this->doEnterNode($node, $env);
}
final public function leaveNode(\Twig_NodeInterface $node, Environment $env)
{
if (!$node instanceof Node) {
throw new \LogicException(sprintf('%s only supports \Twig\Node\Node instances.', __CLASS__));
}
return $this->doLeaveNode($node, $env);
}
/**
* Called before child nodes are visited.
*
* @return Node The modified node
*/
abstract protected function doEnterNode(Node $node, Environment $env);
/**
* Called after child nodes are visited.
*
* @return Node|false|null The modified node or null if the node must be removed
*/
abstract protected function doLeaveNode(Node $node, Environment $env);
}
class_alias('Twig\NodeVisitor\AbstractNodeVisitor', 'Twig_BaseNodeVisitor');

View File

@@ -1,154 +0,0 @@
<?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.
*/
/**
* Twig_NodeVisitor_Escaper implements output escaping.
*
* @final
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class Twig_NodeVisitor_Escaper extends Twig_BaseNodeVisitor
{
protected $statusStack = array();
protected $blocks = array();
protected $safeAnalysis;
protected $traverser;
protected $defaultStrategy = false;
protected $safeVars = array();
public function __construct()
{
$this->safeAnalysis = new Twig_NodeVisitor_SafeAnalysis();
}
protected function doEnterNode(Twig_Node $node, Twig_Environment $env)
{
if ($node instanceof Twig_Node_Module) {
if ($env->hasExtension('Twig_Extension_Escaper') && $defaultStrategy = $env->getExtension('Twig_Extension_Escaper')->getDefaultStrategy($node->getTemplateName())) {
$this->defaultStrategy = $defaultStrategy;
}
$this->safeVars = array();
$this->blocks = array();
} elseif ($node instanceof Twig_Node_AutoEscape) {
$this->statusStack[] = $node->getAttribute('value');
} elseif ($node instanceof Twig_Node_Block) {
$this->statusStack[] = isset($this->blocks[$node->getAttribute('name')]) ? $this->blocks[$node->getAttribute('name')] : $this->needEscaping($env);
} elseif ($node instanceof Twig_Node_Import) {
$this->safeVars[] = $node->getNode('var')->getAttribute('name');
}
return $node;
}
protected function doLeaveNode(Twig_Node $node, Twig_Environment $env)
{
if ($node instanceof Twig_Node_Module) {
$this->defaultStrategy = false;
$this->safeVars = array();
$this->blocks = array();
} elseif ($node instanceof Twig_Node_Expression_Filter) {
return $this->preEscapeFilterNode($node, $env);
} elseif ($node instanceof Twig_Node_Print) {
return $this->escapePrintNode($node, $env, $this->needEscaping($env));
}
if ($node instanceof Twig_Node_AutoEscape || $node instanceof Twig_Node_Block) {
array_pop($this->statusStack);
} elseif ($node instanceof Twig_Node_BlockReference) {
$this->blocks[$node->getAttribute('name')] = $this->needEscaping($env);
}
return $node;
}
protected function escapePrintNode(Twig_Node_Print $node, Twig_Environment $env, $type)
{
if (false === $type) {
return $node;
}
$expression = $node->getNode('expr');
if ($this->isSafeFor($type, $expression, $env)) {
return $node;
}
$class = get_class($node);
return new $class(
$this->getEscaperFilter($type, $expression),
$node->getTemplateLine()
);
}
protected function preEscapeFilterNode(Twig_Node_Expression_Filter $filter, Twig_Environment $env)
{
$name = $filter->getNode('filter')->getAttribute('value');
$type = $env->getFilter($name)->getPreEscape();
if (null === $type) {
return $filter;
}
$node = $filter->getNode('node');
if ($this->isSafeFor($type, $node, $env)) {
return $filter;
}
$filter->setNode('node', $this->getEscaperFilter($type, $node));
return $filter;
}
protected function isSafeFor($type, Twig_NodeInterface $expression, $env)
{
$safe = $this->safeAnalysis->getSafe($expression);
if (null === $safe) {
if (null === $this->traverser) {
$this->traverser = new Twig_NodeTraverser($env, array($this->safeAnalysis));
}
$this->safeAnalysis->setSafeVars($this->safeVars);
$this->traverser->traverse($expression);
$safe = $this->safeAnalysis->getSafe($expression);
}
return in_array($type, $safe) || in_array('all', $safe);
}
protected function needEscaping(Twig_Environment $env)
{
if (count($this->statusStack)) {
return $this->statusStack[count($this->statusStack) - 1];
}
return $this->defaultStrategy ? $this->defaultStrategy : false;
}
protected function getEscaperFilter($type, Twig_NodeInterface $node)
{
$line = $node->getTemplateLine();
$name = new Twig_Node_Expression_Constant('escape', $line);
$args = new Twig_Node(array(new Twig_Node_Expression_Constant((string) $type, $line), new Twig_Node_Expression_Constant(null, $line), new Twig_Node_Expression_Constant(true, $line)));
return new Twig_Node_Expression_Filter($node, $name, $args, $line);
}
public function getPriority()
{
return 0;
}
}
class_alias('Twig_NodeVisitor_Escaper', 'Twig\NodeVisitor\EscaperNodeVisitor', false);

View File

@@ -0,0 +1,209 @@
<?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\NodeVisitor;
use Twig\Environment;
use Twig\Node\AutoEscapeNode;
use Twig\Node\BlockNode;
use Twig\Node\BlockReferenceNode;
use Twig\Node\DoNode;
use Twig\Node\Expression\ConditionalExpression;
use Twig\Node\Expression\ConstantExpression;
use Twig\Node\Expression\FilterExpression;
use Twig\Node\Expression\InlinePrint;
use Twig\Node\ImportNode;
use Twig\Node\ModuleNode;
use Twig\Node\Node;
use Twig\Node\PrintNode;
use Twig\NodeTraverser;
/**
* @final
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class EscaperNodeVisitor extends AbstractNodeVisitor
{
protected $statusStack = [];
protected $blocks = [];
protected $safeAnalysis;
protected $traverser;
protected $defaultStrategy = false;
protected $safeVars = [];
public function __construct()
{
$this->safeAnalysis = new SafeAnalysisNodeVisitor();
}
protected function doEnterNode(Node $node, Environment $env)
{
if ($node instanceof ModuleNode) {
if ($env->hasExtension('\Twig\Extension\EscaperExtension') && $defaultStrategy = $env->getExtension('\Twig\Extension\EscaperExtension')->getDefaultStrategy($node->getTemplateName())) {
$this->defaultStrategy = $defaultStrategy;
}
$this->safeVars = [];
$this->blocks = [];
} elseif ($node instanceof AutoEscapeNode) {
$this->statusStack[] = $node->getAttribute('value');
} elseif ($node instanceof BlockNode) {
$this->statusStack[] = isset($this->blocks[$node->getAttribute('name')]) ? $this->blocks[$node->getAttribute('name')] : $this->needEscaping($env);
} elseif ($node instanceof ImportNode) {
$this->safeVars[] = $node->getNode('var')->getAttribute('name');
}
return $node;
}
protected function doLeaveNode(Node $node, Environment $env)
{
if ($node instanceof ModuleNode) {
$this->defaultStrategy = false;
$this->safeVars = [];
$this->blocks = [];
} elseif ($node instanceof FilterExpression) {
return $this->preEscapeFilterNode($node, $env);
} elseif ($node instanceof PrintNode && false !== $type = $this->needEscaping($env)) {
$expression = $node->getNode('expr');
if ($expression instanceof ConditionalExpression && $this->shouldUnwrapConditional($expression, $env, $type)) {
return new DoNode($this->unwrapConditional($expression, $env, $type), $expression->getTemplateLine());
}
return $this->escapePrintNode($node, $env, $type);
}
if ($node instanceof AutoEscapeNode || $node instanceof BlockNode) {
array_pop($this->statusStack);
} elseif ($node instanceof BlockReferenceNode) {
$this->blocks[$node->getAttribute('name')] = $this->needEscaping($env);
}
return $node;
}
private function shouldUnwrapConditional(ConditionalExpression $expression, Environment $env, $type)
{
$expr2Safe = $this->isSafeFor($type, $expression->getNode('expr2'), $env);
$expr3Safe = $this->isSafeFor($type, $expression->getNode('expr3'), $env);
return $expr2Safe !== $expr3Safe;
}
private function unwrapConditional(ConditionalExpression $expression, Environment $env, $type)
{
// convert "echo a ? b : c" to "a ? echo b : echo c" recursively
$expr2 = $expression->getNode('expr2');
if ($expr2 instanceof ConditionalExpression && $this->shouldUnwrapConditional($expr2, $env, $type)) {
$expr2 = $this->unwrapConditional($expr2, $env, $type);
} else {
$expr2 = $this->escapeInlinePrintNode(new InlinePrint($expr2, $expr2->getTemplateLine()), $env, $type);
}
$expr3 = $expression->getNode('expr3');
if ($expr3 instanceof ConditionalExpression && $this->shouldUnwrapConditional($expr3, $env, $type)) {
$expr3 = $this->unwrapConditional($expr3, $env, $type);
} else {
$expr3 = $this->escapeInlinePrintNode(new InlinePrint($expr3, $expr3->getTemplateLine()), $env, $type);
}
return new ConditionalExpression($expression->getNode('expr1'), $expr2, $expr3, $expression->getTemplateLine());
}
private function escapeInlinePrintNode(InlinePrint $node, Environment $env, $type)
{
$expression = $node->getNode('node');
if ($this->isSafeFor($type, $expression, $env)) {
return $node;
}
return new InlinePrint($this->getEscaperFilter($type, $expression), $node->getTemplateLine());
}
protected function escapePrintNode(PrintNode $node, Environment $env, $type)
{
if (false === $type) {
return $node;
}
$expression = $node->getNode('expr');
if ($this->isSafeFor($type, $expression, $env)) {
return $node;
}
$class = \get_class($node);
return new $class($this->getEscaperFilter($type, $expression), $node->getTemplateLine());
}
protected function preEscapeFilterNode(FilterExpression $filter, Environment $env)
{
$name = $filter->getNode('filter')->getAttribute('value');
$type = $env->getFilter($name)->getPreEscape();
if (null === $type) {
return $filter;
}
$node = $filter->getNode('node');
if ($this->isSafeFor($type, $node, $env)) {
return $filter;
}
$filter->setNode('node', $this->getEscaperFilter($type, $node));
return $filter;
}
protected function isSafeFor($type, \Twig_NodeInterface $expression, $env)
{
$safe = $this->safeAnalysis->getSafe($expression);
if (null === $safe) {
if (null === $this->traverser) {
$this->traverser = new NodeTraverser($env, [$this->safeAnalysis]);
}
$this->safeAnalysis->setSafeVars($this->safeVars);
$this->traverser->traverse($expression);
$safe = $this->safeAnalysis->getSafe($expression);
}
return \in_array($type, $safe) || \in_array('all', $safe);
}
protected function needEscaping(Environment $env)
{
if (\count($this->statusStack)) {
return $this->statusStack[\count($this->statusStack) - 1];
}
return $this->defaultStrategy ? $this->defaultStrategy : false;
}
protected function getEscaperFilter($type, \Twig_NodeInterface $node)
{
$line = $node->getTemplateLine();
$name = new ConstantExpression('escape', $line);
$args = new Node([new ConstantExpression((string) $type, $line), new ConstantExpression(null, $line), new ConstantExpression(true, $line)]);
return new FilterExpression($node, $name, $args, $line);
}
public function getPriority()
{
return 0;
}
}
class_alias('Twig\NodeVisitor\EscaperNodeVisitor', 'Twig_NodeVisitor_Escaper');

View File

@@ -0,0 +1,50 @@
<?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\NodeVisitor;
use Twig\Environment;
/**
* Interface for node visitor classes.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
interface NodeVisitorInterface
{
/**
* Called before child nodes are visited.
*
* @return \Twig_NodeInterface The modified node
*/
public function enterNode(\Twig_NodeInterface $node, Environment $env);
/**
* Called after child nodes are visited.
*
* @return \Twig_NodeInterface|false|null The modified node or null if the node must be removed
*/
public function leaveNode(\Twig_NodeInterface $node, Environment $env);
/**
* Returns the priority for this visitor.
*
* Priority should be between -10 and 10 (0 is the default).
*
* @return int The priority level
*/
public function getPriority();
}
class_alias('Twig\NodeVisitor\NodeVisitorInterface', 'Twig_NodeVisitorInterface');
// Ensure that the aliased name is loaded to keep BC for classes implementing the typehint with the old aliased name.
class_exists('Twig\Environment');

View File

@@ -9,8 +9,28 @@
* file that was distributed with this source code.
*/
namespace Twig\NodeVisitor;
use Twig\Environment;
use Twig\Node\BlockReferenceNode;
use Twig\Node\BodyNode;
use Twig\Node\Expression\AbstractExpression;
use Twig\Node\Expression\BlockReferenceExpression;
use Twig\Node\Expression\ConstantExpression;
use Twig\Node\Expression\FilterExpression;
use Twig\Node\Expression\FunctionExpression;
use Twig\Node\Expression\GetAttrExpression;
use Twig\Node\Expression\NameExpression;
use Twig\Node\Expression\ParentExpression;
use Twig\Node\Expression\TempNameExpression;
use Twig\Node\ForNode;
use Twig\Node\IncludeNode;
use Twig\Node\Node;
use Twig\Node\PrintNode;
use Twig\Node\SetTempNode;
/**
* Twig_NodeVisitor_Optimizer tries to optimizes the AST.
* Tries to optimize the AST.
*
* This visitor is always the last registered one.
*
@@ -21,7 +41,7 @@
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class Twig_NodeVisitor_Optimizer extends Twig_BaseNodeVisitor
class OptimizerNodeVisitor extends AbstractNodeVisitor
{
const OPTIMIZE_ALL = -1;
const OPTIMIZE_NONE = 0;
@@ -29,10 +49,10 @@ class Twig_NodeVisitor_Optimizer extends Twig_BaseNodeVisitor
const OPTIMIZE_RAW_FILTER = 4;
const OPTIMIZE_VAR_ACCESS = 8;
protected $loops = array();
protected $loopsTargets = array();
protected $loops = [];
protected $loopsTargets = [];
protected $optimizers;
protected $prependedNodes = array();
protected $prependedNodes = [];
protected $inABody = false;
/**
@@ -40,29 +60,29 @@ class Twig_NodeVisitor_Optimizer extends Twig_BaseNodeVisitor
*/
public function __construct($optimizers = -1)
{
if (!is_int($optimizers) || $optimizers > (self::OPTIMIZE_FOR | self::OPTIMIZE_RAW_FILTER | self::OPTIMIZE_VAR_ACCESS)) {
throw new InvalidArgumentException(sprintf('Optimizer mode "%s" is not valid.', $optimizers));
if (!\is_int($optimizers) || $optimizers > (self::OPTIMIZE_FOR | self::OPTIMIZE_RAW_FILTER | self::OPTIMIZE_VAR_ACCESS)) {
throw new \InvalidArgumentException(sprintf('Optimizer mode "%s" is not valid.', $optimizers));
}
$this->optimizers = $optimizers;
}
protected function doEnterNode(Twig_Node $node, Twig_Environment $env)
protected function doEnterNode(Node $node, Environment $env)
{
if (self::OPTIMIZE_FOR === (self::OPTIMIZE_FOR & $this->optimizers)) {
$this->enterOptimizeFor($node, $env);
}
if (PHP_VERSION_ID < 50400 && self::OPTIMIZE_VAR_ACCESS === (self::OPTIMIZE_VAR_ACCESS & $this->optimizers) && !$env->isStrictVariables() && !$env->hasExtension('Twig_Extension_Sandbox')) {
if (\PHP_VERSION_ID < 50400 && self::OPTIMIZE_VAR_ACCESS === (self::OPTIMIZE_VAR_ACCESS & $this->optimizers) && !$env->isStrictVariables() && !$env->hasExtension('\Twig\Extension\SandboxExtension')) {
if ($this->inABody) {
if (!$node instanceof Twig_Node_Expression) {
if (get_class($node) !== 'Twig_Node') {
array_unshift($this->prependedNodes, array());
if (!$node instanceof AbstractExpression) {
if ('Twig_Node' !== \get_class($node)) {
array_unshift($this->prependedNodes, []);
}
} else {
$node = $this->optimizeVariables($node, $env);
}
} elseif ($node instanceof Twig_Node_Body) {
} elseif ($node instanceof BodyNode) {
$this->inABody = true;
}
}
@@ -70,9 +90,9 @@ class Twig_NodeVisitor_Optimizer extends Twig_BaseNodeVisitor
return $node;
}
protected function doLeaveNode(Twig_Node $node, Twig_Environment $env)
protected function doLeaveNode(Node $node, Environment $env)
{
$expression = $node instanceof Twig_Node_Expression;
$expression = $node instanceof AbstractExpression;
if (self::OPTIMIZE_FOR === (self::OPTIMIZE_FOR & $this->optimizers)) {
$this->leaveOptimizeFor($node, $env);
@@ -84,18 +104,18 @@ class Twig_NodeVisitor_Optimizer extends Twig_BaseNodeVisitor
$node = $this->optimizePrintNode($node, $env);
if (self::OPTIMIZE_VAR_ACCESS === (self::OPTIMIZE_VAR_ACCESS & $this->optimizers) && !$env->isStrictVariables() && !$env->hasExtension('Twig_Extension_Sandbox')) {
if ($node instanceof Twig_Node_Body) {
if (self::OPTIMIZE_VAR_ACCESS === (self::OPTIMIZE_VAR_ACCESS & $this->optimizers) && !$env->isStrictVariables() && !$env->hasExtension('\Twig\Extension\SandboxExtension')) {
if ($node instanceof BodyNode) {
$this->inABody = false;
} elseif ($this->inABody) {
if (!$expression && get_class($node) !== 'Twig_Node' && $prependedNodes = array_shift($this->prependedNodes)) {
$nodes = array();
if (!$expression && 'Twig_Node' !== \get_class($node) && $prependedNodes = array_shift($this->prependedNodes)) {
$nodes = [];
foreach (array_unique($prependedNodes) as $name) {
$nodes[] = new Twig_Node_SetTemp($name, $node->getTemplateLine());
$nodes[] = new SetTempNode($name, $node->getTemplateLine());
}
$nodes[] = $node;
$node = new Twig_Node($nodes);
$node = new Node($nodes);
}
}
}
@@ -103,12 +123,12 @@ class Twig_NodeVisitor_Optimizer extends Twig_BaseNodeVisitor
return $node;
}
protected function optimizeVariables(Twig_NodeInterface $node, Twig_Environment $env)
protected function optimizeVariables(\Twig_NodeInterface $node, Environment $env)
{
if ('Twig_Node_Expression_Name' === get_class($node) && $node->isSimple()) {
if ('Twig_Node_Expression_Name' === \get_class($node) && $node->isSimple()) {
$this->prependedNodes[0][] = $node->getAttribute('name');
return new Twig_Node_Expression_TempName($node->getAttribute('name'), $node->getTemplateLine());
return new TempNameExpression($node->getAttribute('name'), $node->getTemplateLine());
}
return $node;
@@ -121,18 +141,18 @@ class Twig_NodeVisitor_Optimizer extends Twig_BaseNodeVisitor
*
* * "echo $this->render(Parent)Block()" with "$this->display(Parent)Block()"
*
* @return Twig_NodeInterface
* @return \Twig_NodeInterface
*/
protected function optimizePrintNode(Twig_NodeInterface $node, Twig_Environment $env)
protected function optimizePrintNode(\Twig_NodeInterface $node, Environment $env)
{
if (!$node instanceof Twig_Node_Print) {
if (!$node instanceof PrintNode) {
return $node;
}
$exprNode = $node->getNode('expr');
if (
$exprNode instanceof Twig_Node_Expression_BlockReference ||
$exprNode instanceof Twig_Node_Expression_Parent
$exprNode instanceof BlockReferenceExpression ||
$exprNode instanceof ParentExpression
) {
$exprNode->setAttribute('output', true);
@@ -145,11 +165,11 @@ class Twig_NodeVisitor_Optimizer extends Twig_BaseNodeVisitor
/**
* Removes "raw" filters.
*
* @return Twig_NodeInterface
* @return \Twig_NodeInterface
*/
protected function optimizeRawFilter(Twig_NodeInterface $node, Twig_Environment $env)
protected function optimizeRawFilter(\Twig_NodeInterface $node, Environment $env)
{
if ($node instanceof Twig_Node_Expression_Filter && 'raw' == $node->getNode('filter')->getAttribute('value')) {
if ($node instanceof FilterExpression && 'raw' == $node->getNode('filter')->getAttribute('value')) {
return $node->getNode('node');
}
@@ -159,9 +179,9 @@ class Twig_NodeVisitor_Optimizer extends Twig_BaseNodeVisitor
/**
* Optimizes "for" tag by removing the "loop" variable creation whenever possible.
*/
protected function enterOptimizeFor(Twig_NodeInterface $node, Twig_Environment $env)
protected function enterOptimizeFor(\Twig_NodeInterface $node, Environment $env)
{
if ($node instanceof Twig_Node_For) {
if ($node instanceof ForNode) {
// disable the loop variable by default
$node->setAttribute('with_loop', false);
array_unshift($this->loops, $node);
@@ -175,28 +195,28 @@ class Twig_NodeVisitor_Optimizer extends Twig_BaseNodeVisitor
// when do we need to add the loop variable back?
// the loop variable is referenced for the current loop
elseif ($node instanceof Twig_Node_Expression_Name && 'loop' === $node->getAttribute('name')) {
elseif ($node instanceof NameExpression && 'loop' === $node->getAttribute('name')) {
$node->setAttribute('always_defined', true);
$this->addLoopToCurrent();
}
// optimize access to loop targets
elseif ($node instanceof Twig_Node_Expression_Name && in_array($node->getAttribute('name'), $this->loopsTargets)) {
elseif ($node instanceof NameExpression && \in_array($node->getAttribute('name'), $this->loopsTargets)) {
$node->setAttribute('always_defined', true);
}
// block reference
elseif ($node instanceof Twig_Node_BlockReference || $node instanceof Twig_Node_Expression_BlockReference) {
elseif ($node instanceof BlockReferenceNode || $node instanceof BlockReferenceExpression) {
$this->addLoopToCurrent();
}
// include without the only attribute
elseif ($node instanceof Twig_Node_Include && !$node->getAttribute('only')) {
elseif ($node instanceof IncludeNode && !$node->getAttribute('only')) {
$this->addLoopToAll();
}
// include function without the with_context=false parameter
elseif ($node instanceof Twig_Node_Expression_Function
elseif ($node instanceof FunctionExpression
&& 'include' === $node->getAttribute('name')
&& (!$node->getNode('arguments')->hasNode('with_context')
|| false !== $node->getNode('arguments')->getNode('with_context')->getAttribute('value')
@@ -206,12 +226,12 @@ class Twig_NodeVisitor_Optimizer extends Twig_BaseNodeVisitor
}
// the loop variable is referenced via an attribute
elseif ($node instanceof Twig_Node_Expression_GetAttr
&& (!$node->getNode('attribute') instanceof Twig_Node_Expression_Constant
elseif ($node instanceof GetAttrExpression
&& (!$node->getNode('attribute') instanceof ConstantExpression
|| 'parent' === $node->getNode('attribute')->getAttribute('value')
)
&& (true === $this->loops[0]->getAttribute('with_loop')
|| ($node->getNode('node') instanceof Twig_Node_Expression_Name
|| ($node->getNode('node') instanceof NameExpression
&& 'loop' === $node->getNode('node')->getAttribute('name')
)
)
@@ -223,9 +243,9 @@ class Twig_NodeVisitor_Optimizer extends Twig_BaseNodeVisitor
/**
* Optimizes "for" tag by removing the "loop" variable creation whenever possible.
*/
protected function leaveOptimizeFor(Twig_NodeInterface $node, Twig_Environment $env)
protected function leaveOptimizeFor(\Twig_NodeInterface $node, Environment $env)
{
if ($node instanceof Twig_Node_For) {
if ($node instanceof ForNode) {
array_shift($this->loops);
array_shift($this->loopsTargets);
array_shift($this->loopsTargets);
@@ -250,4 +270,4 @@ class Twig_NodeVisitor_Optimizer extends Twig_BaseNodeVisitor
}
}
class_alias('Twig_NodeVisitor_Optimizer', 'Twig\NodeVisitor\OptimizerNodeVisitor', false);
class_alias('Twig\NodeVisitor\OptimizerNodeVisitor', 'Twig_NodeVisitor_Optimizer');

View File

@@ -9,20 +9,34 @@
* file that was distributed with this source code.
*/
namespace Twig\NodeVisitor;
use Twig\Environment;
use Twig\Node\Expression\BlockReferenceExpression;
use Twig\Node\Expression\ConditionalExpression;
use Twig\Node\Expression\ConstantExpression;
use Twig\Node\Expression\FilterExpression;
use Twig\Node\Expression\FunctionExpression;
use Twig\Node\Expression\GetAttrExpression;
use Twig\Node\Expression\MethodCallExpression;
use Twig\Node\Expression\NameExpression;
use Twig\Node\Expression\ParentExpression;
use Twig\Node\Node;
/**
* @final
*/
class Twig_NodeVisitor_SafeAnalysis extends Twig_BaseNodeVisitor
class SafeAnalysisNodeVisitor extends AbstractNodeVisitor
{
protected $data = array();
protected $safeVars = array();
protected $data = [];
protected $safeVars = [];
public function setSafeVars($safeVars)
{
$this->safeVars = $safeVars;
}
public function getSafe(Twig_NodeInterface $node)
public function getSafe(\Twig_NodeInterface $node)
{
$hash = spl_object_hash($node);
if (!isset($this->data[$hash])) {
@@ -34,7 +48,7 @@ class Twig_NodeVisitor_SafeAnalysis extends Twig_BaseNodeVisitor
continue;
}
if (in_array('html_attr', $bucket['value'])) {
if (\in_array('html_attr', $bucket['value'])) {
$bucket['value'][] = 'html';
}
@@ -42,7 +56,7 @@ class Twig_NodeVisitor_SafeAnalysis extends Twig_BaseNodeVisitor
}
}
protected function setSafe(Twig_NodeInterface $node, array $safe)
protected function setSafe(\Twig_NodeInterface $node, array $safe)
{
$hash = spl_object_hash($node);
if (isset($this->data[$hash])) {
@@ -54,33 +68,33 @@ class Twig_NodeVisitor_SafeAnalysis extends Twig_BaseNodeVisitor
}
}
}
$this->data[$hash][] = array(
$this->data[$hash][] = [
'key' => $node,
'value' => $safe,
);
];
}
protected function doEnterNode(Twig_Node $node, Twig_Environment $env)
protected function doEnterNode(Node $node, Environment $env)
{
return $node;
}
protected function doLeaveNode(Twig_Node $node, Twig_Environment $env)
protected function doLeaveNode(Node $node, Environment $env)
{
if ($node instanceof Twig_Node_Expression_Constant) {
if ($node instanceof ConstantExpression) {
// constants are marked safe for all
$this->setSafe($node, array('all'));
} elseif ($node instanceof Twig_Node_Expression_BlockReference) {
$this->setSafe($node, ['all']);
} elseif ($node instanceof BlockReferenceExpression) {
// blocks are safe by definition
$this->setSafe($node, array('all'));
} elseif ($node instanceof Twig_Node_Expression_Parent) {
$this->setSafe($node, ['all']);
} elseif ($node instanceof ParentExpression) {
// parent block is safe by definition
$this->setSafe($node, array('all'));
} elseif ($node instanceof Twig_Node_Expression_Conditional) {
$this->setSafe($node, ['all']);
} elseif ($node instanceof ConditionalExpression) {
// intersect safeness of both operands
$safe = $this->intersectSafe($this->getSafe($node->getNode('expr2')), $this->getSafe($node->getNode('expr3')));
$this->setSafe($node, $safe);
} elseif ($node instanceof Twig_Node_Expression_Filter) {
} elseif ($node instanceof FilterExpression) {
// filter expression is safe when the filter is safe
$name = $node->getNode('filter')->getAttribute('value');
$args = $node->getNode('arguments');
@@ -91,9 +105,9 @@ class Twig_NodeVisitor_SafeAnalysis extends Twig_BaseNodeVisitor
}
$this->setSafe($node, $safe);
} else {
$this->setSafe($node, array());
$this->setSafe($node, []);
}
} elseif ($node instanceof Twig_Node_Expression_Function) {
} elseif ($node instanceof FunctionExpression) {
// function expression is safe when the function is safe
$name = $node->getAttribute('name');
$args = $node->getNode('arguments');
@@ -101,24 +115,24 @@ class Twig_NodeVisitor_SafeAnalysis extends Twig_BaseNodeVisitor
if (false !== $function) {
$this->setSafe($node, $function->getSafe($args));
} else {
$this->setSafe($node, array());
$this->setSafe($node, []);
}
} elseif ($node instanceof Twig_Node_Expression_MethodCall) {
} elseif ($node instanceof MethodCallExpression) {
if ($node->getAttribute('safe')) {
$this->setSafe($node, array('all'));
$this->setSafe($node, ['all']);
} else {
$this->setSafe($node, array());
$this->setSafe($node, []);
}
} elseif ($node instanceof Twig_Node_Expression_GetAttr && $node->getNode('node') instanceof Twig_Node_Expression_Name) {
} elseif ($node instanceof GetAttrExpression && $node->getNode('node') instanceof NameExpression) {
$name = $node->getNode('node')->getAttribute('name');
// attributes on template instances are safe
if ('_self' == $name || in_array($name, $this->safeVars)) {
$this->setSafe($node, array('all'));
if ('_self' == $name || \in_array($name, $this->safeVars)) {
$this->setSafe($node, ['all']);
} else {
$this->setSafe($node, array());
$this->setSafe($node, []);
}
} else {
$this->setSafe($node, array());
$this->setSafe($node, []);
}
return $node;
@@ -127,14 +141,14 @@ class Twig_NodeVisitor_SafeAnalysis extends Twig_BaseNodeVisitor
protected function intersectSafe(array $a = null, array $b = null)
{
if (null === $a || null === $b) {
return array();
return [];
}
if (in_array('all', $a)) {
if (\in_array('all', $a)) {
return $b;
}
if (in_array('all', $b)) {
if (\in_array('all', $b)) {
return $a;
}
@@ -147,4 +161,4 @@ class Twig_NodeVisitor_SafeAnalysis extends Twig_BaseNodeVisitor
}
}
class_alias('Twig_NodeVisitor_SafeAnalysis', 'Twig\NodeVisitor\SafeAnalysisNodeVisitor', false);
class_alias('Twig\NodeVisitor\SafeAnalysisNodeVisitor', 'Twig_NodeVisitor_SafeAnalysis');

View File

@@ -1,77 +0,0 @@
<?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.
*/
/**
* Twig_NodeVisitor_Sandbox implements sandboxing.
*
* @final
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class Twig_NodeVisitor_Sandbox extends Twig_BaseNodeVisitor
{
protected $inAModule = false;
protected $tags;
protected $filters;
protected $functions;
protected function doEnterNode(Twig_Node $node, Twig_Environment $env)
{
if ($node instanceof Twig_Node_Module) {
$this->inAModule = true;
$this->tags = array();
$this->filters = array();
$this->functions = array();
return $node;
} elseif ($this->inAModule) {
// look for tags
if ($node->getNodeTag() && !isset($this->tags[$node->getNodeTag()])) {
$this->tags[$node->getNodeTag()] = $node;
}
// look for filters
if ($node instanceof Twig_Node_Expression_Filter && !isset($this->filters[$node->getNode('filter')->getAttribute('value')])) {
$this->filters[$node->getNode('filter')->getAttribute('value')] = $node;
}
// look for functions
if ($node instanceof Twig_Node_Expression_Function && !isset($this->functions[$node->getAttribute('name')])) {
$this->functions[$node->getAttribute('name')] = $node;
}
// wrap print to check __toString() calls
if ($node instanceof Twig_Node_Print) {
return new Twig_Node_SandboxedPrint($node->getNode('expr'), $node->getTemplateLine(), $node->getNodeTag());
}
}
return $node;
}
protected function doLeaveNode(Twig_Node $node, Twig_Environment $env)
{
if ($node instanceof Twig_Node_Module) {
$this->inAModule = false;
$node->setNode('display_start', new Twig_Node(array(new Twig_Node_CheckSecurity($this->filters, $this->tags, $this->functions), $node->getNode('display_start'))));
}
return $node;
}
public function getPriority()
{
return 0;
}
}
class_alias('Twig_NodeVisitor_Sandbox', 'Twig\NodeVisitor\SandboxNodeVisitor', false);

View File

@@ -0,0 +1,137 @@
<?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\NodeVisitor;
use Twig\Environment;
use Twig\Node\CheckSecurityNode;
use Twig\Node\CheckToStringNode;
use Twig\Node\Expression\Binary\ConcatBinary;
use Twig\Node\Expression\Binary\RangeBinary;
use Twig\Node\Expression\FilterExpression;
use Twig\Node\Expression\FunctionExpression;
use Twig\Node\Expression\GetAttrExpression;
use Twig\Node\Expression\NameExpression;
use Twig\Node\ModuleNode;
use Twig\Node\Node;
use Twig\Node\PrintNode;
use Twig\Node\SetNode;
/**
* @final
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class SandboxNodeVisitor extends AbstractNodeVisitor
{
protected $inAModule = false;
protected $tags;
protected $filters;
protected $functions;
private $needsToStringWrap = false;
protected function doEnterNode(Node $node, Environment $env)
{
if ($node instanceof ModuleNode) {
$this->inAModule = true;
$this->tags = [];
$this->filters = [];
$this->functions = [];
return $node;
} elseif ($this->inAModule) {
// look for tags
if ($node->getNodeTag() && !isset($this->tags[$node->getNodeTag()])) {
$this->tags[$node->getNodeTag()] = $node;
}
// look for filters
if ($node instanceof FilterExpression && !isset($this->filters[$node->getNode('filter')->getAttribute('value')])) {
$this->filters[$node->getNode('filter')->getAttribute('value')] = $node;
}
// look for functions
if ($node instanceof FunctionExpression && !isset($this->functions[$node->getAttribute('name')])) {
$this->functions[$node->getAttribute('name')] = $node;
}
// the .. operator is equivalent to the range() function
if ($node instanceof RangeBinary && !isset($this->functions['range'])) {
$this->functions['range'] = $node;
}
if ($node instanceof PrintNode) {
$this->needsToStringWrap = true;
$this->wrapNode($node, 'expr');
}
if ($node instanceof SetNode && !$node->getAttribute('capture')) {
$this->needsToStringWrap = true;
}
// wrap outer nodes that can implicitly call __toString()
if ($this->needsToStringWrap) {
if ($node instanceof ConcatBinary) {
$this->wrapNode($node, 'left');
$this->wrapNode($node, 'right');
}
if ($node instanceof FilterExpression) {
$this->wrapNode($node, 'node');
$this->wrapArrayNode($node, 'arguments');
}
if ($node instanceof FunctionExpression) {
$this->wrapArrayNode($node, 'arguments');
}
}
}
return $node;
}
protected function doLeaveNode(Node $node, Environment $env)
{
if ($node instanceof ModuleNode) {
$this->inAModule = false;
$node->getNode('constructor_end')->setNode('_security_check', new Node([new CheckSecurityNode($this->filters, $this->tags, $this->functions), $node->getNode('display_start')]));
} elseif ($this->inAModule) {
if ($node instanceof PrintNode || $node instanceof SetNode) {
$this->needsToStringWrap = false;
}
}
return $node;
}
private function wrapNode(Node $node, $name)
{
$expr = $node->getNode($name);
if ($expr instanceof NameExpression || $expr instanceof GetAttrExpression) {
$node->setNode($name, new CheckToStringNode($expr));
}
}
private function wrapArrayNode(Node $node, $name)
{
$args = $node->getNode($name);
foreach ($args as $name => $_) {
$this->wrapNode($args, $name);
}
}
public function getPriority()
{
return 0;
}
}
class_alias('Twig\NodeVisitor\SandboxNodeVisitor', 'Twig_NodeVisitor_Sandbox');