'even' => new Twig_Filter_Function('twig_is_even_filter'),
'escape' => new Twig_Filter_Function('twig_escape_filter', array('needs_environment' => true)),
+If you have used the undocumented NodeTransformer classes, you will need to
+upgrade to the new interface (please not that the interface is not yet stable).
+
+ * refactored node transformers to node visitors
+ * fixed automatic-escaping for blocks
* added a way to specify variables to pass to an included template
* changed the automatic-escaping rules to be more sensible and more configurable in custom filters (the documentation lists all the rules)
* improved the filter system to allow object methods to be used as filters
protected $baseTemplateClass;
protected $extensions;
protected $parsers;
- protected $transformers;
+ protected $visitors;
protected $filters;
protected $runtimeInitialized;
protected $loadedTemplates;
return $this->parsers;
}
- public function getNodeTransformers()
+ public function getNodeVisitors()
{
- if (null === $this->transformers)
+ if (null === $this->visitors)
{
- $this->transformers = array();
+ $this->visitors = array();
foreach ($this->getExtensions() as $extension)
{
- $this->transformers = array_merge($this->transformers, $extension->getNodeTransformers());
+ $this->visitors = array_merge($this->visitors, $extension->getNodeVisitors());
}
}
- return $this->transformers;
+ return $this->visitors;
}
public function getFilters()
}
/**
- * Returns the node transformer instances to add to the existing list.
+ * Returns the node visitor instances to add to the existing list.
*
- * @return array An array of Twig_NodeTransformer instances
+ * @return array An array of Twig_NodeVisitorInterface instances
*/
- public function getNodeTransformers()
+ public function getNodeVisitors()
{
return array();
}
}
/**
- * Returns the node transformer instances to add to the existing list.
+ * Returns the node visitor instances to add to the existing list.
*
- * @return array An array of Twig_NodeTransformer instances
+ * @return array An array of Twig_NodeVisitorInterface instances
*/
- public function getNodeTransformers()
+ public function getNodeVisitors()
{
- return array(new Twig_NodeTransformer_Filter());
+ return array(
+ new Twig_NodeVisitor_Filter(),
+ );
}
/**
}
/**
- * Returns the node transformer instances to add to the existing list.
+ * Returns the node visitor instances to add to the existing list.
*
- * @return array An array of Twig_NodeTransformer instances
+ * @return array An array of Twig_NodeVisitorInterface instances
*/
- public function getNodeTransformers()
+ public function getNodeVisitors()
{
- return array(new Twig_NodeTransformer_Escaper());
+ return array(new Twig_NodeVisitor_Escaper());
}
/**
}
}
-// tells the escaper node transformer that the string is safe
+// tells the escaper node visitor that the string is safe
function twig_safe_filter($string)
{
return $string;
}
/**
- * Returns the node transformer instances to add to the existing list.
+ * Returns the node visitor instances to add to the existing list.
*
- * @return array An array of Twig_NodeTransformer instances
+ * @return array An array of Twig_NodeVisitorInterface instances
*/
- public function getNodeTransformers()
+ public function getNodeVisitors()
{
- return array(new Twig_NodeTransformer_Sandbox());
+ return array(new Twig_NodeVisitor_Sandbox());
}
public function enableSandbox()
public function getTokenParsers();
/**
- * Returns the node transformer instances to add to the existing list.
+ * Returns the node visitor instances to add to the existing list.
*
- * @return array An array of Twig_NodeTransformer instances
+ * @return array An array of Twig_NodeVisitorInterface instances
*/
- public function getNodeTransformers();
+ public function getNodeVisitors();
/**
* Returns a list of filters to add to the existing list.
+++ /dev/null
-<?php
-
-/*
- * This file is part of Twig.
- *
- * (c) 2009 Fabien Potencier
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-abstract class Twig_NodeTransformer
-{
- protected $env;
-
- public function setEnvironment(Twig_Environment $env)
- {
- $this->env = $env;
- }
-
- abstract public function visit(Twig_Node $node);
-
- protected function visitDeep(Twig_Node $node)
- {
- if (!$node instanceof Twig_NodeListInterface)
- {
- return $node;
- }
-
- $newNodes = array();
- foreach ($nodes = $node->getNodes() as $k => $n)
- {
- if (null !== $n = $this->visit($n))
- {
- $newNodes[$k] = $n;
- }
- }
-
- $node->setNodes($newNodes);
-
- return $node;
- }
-}
+++ /dev/null
-<?php
-
-/*
- * This file is part of Twig.
- *
- * (c) 2009 Fabien Potencier
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-class Twig_NodeTransformer_Chain extends Twig_NodeTransformer
-{
- protected $transformers;
-
- public function __construct(array $transformers)
- {
- $this->transformers = $transformers;
- }
-
- public function setEnvironment(Twig_Environment $env)
- {
- parent::setEnvironment($env);
-
- foreach ($this->transformers as $transformer)
- {
- $transformer->setEnvironment($env);
- }
- }
-
- public function visit(Twig_Node $node)
- {
- foreach ($this->transformers as $transformer)
- {
- $node = $transformer->visit($node);
- }
-
- return $node;
- }
-}
--- /dev/null
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) 2009 Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+class Twig_NodeTraverser
+{
+ protected $env;
+
+ public function __construct(Twig_Environment $env)
+ {
+ $this->env = $env;
+ }
+
+ public function traverse(Twig_Node $node, Twig_NodeVisitorInterface $visitor)
+ {
+ $node = $visitor->enterNode($node, $this->env);
+
+ if ($node instanceof Twig_NodeListInterface)
+ {
+ $newNodes = array();
+ foreach ($nodes = $node->getNodes() as $k => $n)
+ {
+ if (null !== $n = $this->traverse($n, $visitor))
+ {
+ $newNodes[$k] = $n;
+ }
+ }
+ $node->setNodes($newNodes);
+ }
+
+ return $visitor->leaveNode($node, $this->env);
+ }
+}
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
-class Twig_NodeTransformer_Escaper extends Twig_NodeTransformer
+class Twig_NodeVisitor_Escaper implements Twig_NodeVisitorInterface
{
protected $statusStack = array();
+ protected $blocks = array();
- public function visit(Twig_Node $node)
+ public function enterNode(Twig_Node $node, Twig_Environment $env)
{
- // autoescape?
if ($node instanceof Twig_Node_AutoEscape)
{
$this->statusStack[] = $node->getValue();
-
- $node = $this->visitDeep($node);
-
- array_pop($this->statusStack);
-
- // remove the node
- return $node;
}
-
- if (!$node instanceof Twig_Node_Print)
+ elseif ($node instanceof Twig_Node_Print && true === $this->needEscaping($env))
+ {
+ return $this->escapeNode($node, $env);
+ }
+ elseif ($node instanceof Twig_Node_Block)
{
- return $this->visitDeep($node);
+ $this->statusStack[] = isset($this->blocks[$node->getName()]) ? $this->blocks[$node->getName()] : $this->needEscaping($env);
}
- if (false === $this->needEscaping())
+ return $node;
+ }
+
+ public function leaveNode(Twig_Node $node, Twig_Environment $env)
+ {
+ if ($node instanceof Twig_Node_AutoEscape || $node instanceof Twig_Node_Block)
{
- return $node;
+ array_pop($this->statusStack);
+ }
+ elseif ($node instanceof Twig_Node_BlockReference)
+ {
+ $this->blocks[$node->getName()] = $this->needEscaping($env);
}
- return $this->escapeNode($node);
+ return $node;
}
- protected function escapeNode($node)
+ protected function escapeNode(Twig_Node $node, Twig_Environment $env)
{
$expression = $node instanceof Twig_Node_Print ? $node->getExpression() : $node;
}
// don't escape if there is already an "escaper" in the filter chain
- $filterMap = $this->env->getFilters();
+ $filterMap = $env->getFilters();
foreach ($expression->getFilters() as $filter)
{
if (isset($filterMap[$filter[0]]) && $filterMap[$filter[0]]->isEscaper())
{
foreach ($filter[1] as $j => $argument)
{
- $filters[$i][1][$j] = $this->escapeNode($argument);
+ $filters[$i][1][$j] = $this->escapeNode($argument, $env);
}
}
}
}
- protected function needEscaping()
+ protected function needEscaping(Twig_Environment $env)
{
if (count($this->statusStack))
{
}
else
{
- return $this->env->hasExtension('escaper') ? $this->env->getExtension('escaper')->isGlobal() : false;
+ return $env->hasExtension('escaper') ? $env->getExtension('escaper')->isGlobal() : false;
}
}
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
-class Twig_NodeTransformer_Filter extends Twig_NodeTransformer
+class Twig_NodeVisitor_Filter implements Twig_NodeVisitorInterface
{
protected $statusStack = array();
- public function visit(Twig_Node $node)
+ public function enterNode(Twig_Node $node, Twig_Environment $env)
{
- // filter?
if ($node instanceof Twig_Node_Filter)
{
$this->statusStack[] = $node->getFilters();
+ }
+ elseif ($node instanceof Twig_Node_Print || $node instanceof Twig_Node_Text)
+ {
+ return $this->applyFilters($node);
+ }
- $node = $this->visitDeep($node);
+ return $node;
+ }
+ public function leaveNode(Twig_Node $node, Twig_Environment $env)
+ {
+ if ($node instanceof Twig_Node_Filter)
+ {
array_pop($this->statusStack);
-
- return $node;
}
- if (!$node instanceof Twig_Node_Print && !$node instanceof Twig_Node_Text)
- {
- return $this->visitDeep($node);
- }
+ return $node;
+ }
+ protected function applyFilters(Twig_Node $node)
+ {
if (false === $filters = $this->getCurrentFilters())
{
return $node;
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
-class Twig_NodeTransformer_Sandbox extends Twig_NodeTransformer
+class Twig_NodeVisitor_Sandbox implements Twig_NodeVisitorInterface
{
protected $inAModule = false;
protected $tags;
protected $filters;
- public function visit(Twig_Node $node)
+ public function enterNode(Twig_Node $node, Twig_Environment $env)
{
if ($node instanceof Twig_Node_Module)
{
$this->tags = array();
$this->filters = array();
- $node = $this->visitDeep($node);
-
- $node->setUsedFilters(array_keys($this->filters));
- $node->setUsedTags(array_keys($this->tags));
- $this->inAModule = false;
-
- return $node;
- }
-
- if (!$this->inAModule)
- {
return $node;
}
-
- // look for tags
- if ($node->getTag())
+ elseif ($this->inAModule)
{
- $this->tags[$node->getTag()] = true;
- }
+ // look for tags
+ if ($node->getTag())
+ {
+ $this->tags[$node->getTag()] = true;
+ }
- // look for filters
- if ($node instanceof Twig_Node_Expression_Filter)
- {
- foreach ($node->getFilters() as $filter)
+ // look for filters
+ if ($node instanceof Twig_Node_Expression_Filter)
{
- $this->filters[$filter[0]] = true;
+ foreach ($node->getFilters() as $filter)
+ {
+ $this->filters[$filter[0]] = true;
+ }
}
}
- $this->visitDeep($node);
+ return $node;
+ }
+
+ public function leaveNode(Twig_Node $node, Twig_Environment $env)
+ {
+ if ($node instanceof Twig_Node_Module)
+ {
+ $node->setUsedFilters(array_keys($this->filters));
+ $node->setUsedTags(array_keys($this->tags));
+ $this->inAModule = false;
+ }
return $node;
}
protected $stream;
protected $extends;
protected $handlers;
- protected $transformers;
+ protected $visitors;
protected $expressionParser;
protected $blocks;
protected $currentBlock;
$this->env = $env;
$this->handlers = array();
- $this->transformers = array();
+ $this->visitors = array();
// tag handlers
foreach ($this->env->getTokenParsers() as $handler)
$this->handlers[$handler->getTag()] = $handler;
}
- // node transformers
- $this->transformers = $env->getNodeTransformers();
+ // node visitors
+ $this->visitors = $env->getNodeVisitors();
}
/**
$node = new Twig_Node_Module($body, $this->extends, $this->blocks, $this->macros, $this->stream->getFilename());
- $transformer = new Twig_NodeTransformer_Chain($this->transformers);
- $transformer->setEnvironment($this->env);
- $node = $transformer->visit($node);
+ $t = new Twig_NodeTraverser($this->env);
+ foreach ($this->visitors as $visitor)
+ {
+ $node = $t->traverse($node, $visitor);
+ }
return $node;
}
$this->handlers[$name] = $class;
}
- public function addTransformer(Twig_NodeTransformer $transformer)
+ public function addNodeVisitor(Twig_NodeVisitorInterface $visitor)
{
- $this->transformers[] = $transformer;
+ $this->visitors[] = $visitor;
}
public function getCurrentBlock()