refactored node transformers to node visitors

git-svn-id: http://svn.twig-project.org/trunk@201 93ef8e89-cb99-4229-a87c-7fa0fa45744b
This commit is contained in:
fabien
2009-12-22 14:13:11 +00:00
parent e246460930
commit 92d96e2eab
14 changed files with 154 additions and 174 deletions
+5
View File
@@ -12,6 +12,11 @@ environment constant by the "needs_environment" option:
'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
+6 -6
View File
@@ -25,7 +25,7 @@ class Twig_Environment
protected $baseTemplateClass;
protected $extensions;
protected $parsers;
protected $transformers;
protected $visitors;
protected $filters;
protected $runtimeInitialized;
protected $loadedTemplates;
@@ -353,18 +353,18 @@ class Twig_Environment
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()
+3 -3
View File
@@ -30,11 +30,11 @@ abstract class Twig_Extension implements Twig_ExtensionInterface
}
/**
* 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();
}
+6 -4
View File
@@ -43,13 +43,15 @@ class Twig_Extension_Core extends Twig_Extension
}
/**
* 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(),
);
}
/**
+5 -5
View File
@@ -37,13 +37,13 @@ class Twig_Extension_Escaper extends Twig_Extension
}
/**
* 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());
}
/**
@@ -74,7 +74,7 @@ class Twig_Extension_Escaper extends Twig_Extension
}
}
// 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;
+4 -4
View File
@@ -21,13 +21,13 @@ class Twig_Extension_Sandbox extends Twig_Extension
}
/**
* 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()
+3 -3
View File
@@ -33,11 +33,11 @@ interface Twig_ExtensionInterface
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.
-42
View File
@@ -1,42 +0,0 @@
<?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;
}
}
-39
View File
@@ -1,39 +0,0 @@
<?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;
}
}
+39
View File
@@ -0,0 +1,39 @@
<?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);
}
}
@@ -8,39 +8,44 @@
* 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->visitDeep($node);
return $this->escapeNode($node, $env);
}
if (false === $this->needEscaping())
elseif ($node instanceof Twig_Node_Block)
{
return $node;
$this->statusStack[] = isset($this->blocks[$node->getName()]) ? $this->blocks[$node->getName()] : $this->needEscaping($env);
}
return $this->escapeNode($node);
return $node;
}
protected function escapeNode($node)
public function leaveNode(Twig_Node $node, Twig_Environment $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->getName()] = $this->needEscaping($env);
}
return $node;
}
protected function escapeNode(Twig_Node $node, Twig_Environment $env)
{
$expression = $node instanceof Twig_Node_Print ? $node->getExpression() : $node;
@@ -54,7 +59,7 @@ class Twig_NodeTransformer_Escaper extends Twig_NodeTransformer
}
// 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())
@@ -78,7 +83,7 @@ class Twig_NodeTransformer_Escaper extends Twig_NodeTransformer
{
foreach ($filter[1] as $j => $argument)
{
$filters[$i][1][$j] = $this->escapeNode($argument);
$filters[$i][1][$j] = $this->escapeNode($argument, $env);
}
}
@@ -100,7 +105,7 @@ class Twig_NodeTransformer_Escaper extends Twig_NodeTransformer
}
}
protected function needEscaping()
protected function needEscaping(Twig_Environment $env)
{
if (count($this->statusStack))
{
@@ -108,7 +113,7 @@ class Twig_NodeTransformer_Escaper extends Twig_NodeTransformer
}
else
{
return $this->env->hasExtension('escaper') ? $this->env->getExtension('escaper')->isGlobal() : false;
return $env->hasExtension('escaper') ? $env->getExtension('escaper')->isGlobal() : false;
}
}
@@ -8,29 +8,36 @@
* 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();
$node = $this->visitDeep($node);
array_pop($this->statusStack);
return $node;
}
if (!$node instanceof Twig_Node_Print && !$node instanceof Twig_Node_Text)
elseif ($node instanceof Twig_Node_Print || $node instanceof Twig_Node_Text)
{
return $this->visitDeep($node);
return $this->applyFilters($node);
}
return $node;
}
public function leaveNode(Twig_Node $node, Twig_Environment $env)
{
if ($node instanceof Twig_Node_Filter)
{
array_pop($this->statusStack);
}
return $node;
}
protected function applyFilters(Twig_Node $node)
{
if (false === $filters = $this->getCurrentFilters())
{
return $node;
@@ -8,13 +8,13 @@
* 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)
{
@@ -22,36 +22,37 @@ class Twig_NodeTransformer_Sandbox extends Twig_NodeTransformer
$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)
elseif ($this->inAModule)
{
return $node;
}
// 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 tags
if ($node->getTag())
{
$this->filters[$filter[0]] = true;
$this->tags[$node->getTag()] = true;
}
// look for filters
if ($node instanceof Twig_Node_Expression_Filter)
{
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;
}
+11 -9
View File
@@ -14,7 +14,7 @@ class Twig_Parser
protected $stream;
protected $extends;
protected $handlers;
protected $transformers;
protected $visitors;
protected $expressionParser;
protected $blocks;
protected $currentBlock;
@@ -31,7 +31,7 @@ class Twig_Parser
$this->env = $env;
$this->handlers = array();
$this->transformers = array();
$this->visitors = array();
// tag handlers
foreach ($this->env->getTokenParsers() as $handler)
@@ -41,8 +41,8 @@ class Twig_Parser
$this->handlers[$handler->getTag()] = $handler;
}
// node transformers
$this->transformers = $env->getNodeTransformers();
// node visitors
$this->visitors = $env->getNodeVisitors();
}
/**
@@ -89,9 +89,11 @@ class Twig_Parser
$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;
}
@@ -163,9 +165,9 @@ class Twig_Parser
$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()