refactored node transformers to node visitors
authorfabien <fabien@93ef8e89-cb99-4229-a87c-7fa0fa45744b>
Tue, 22 Dec 2009 14:13:11 +0000 (14:13 +0000)
committerfabien <fabien@93ef8e89-cb99-4229-a87c-7fa0fa45744b>
Tue, 22 Dec 2009 14:13:11 +0000 (14:13 +0000)
git-svn-id: http://svn.twig-project.org/trunk@201 93ef8e89-cb99-4229-a87c-7fa0fa45744b

14 files changed:
CHANGELOG
lib/Twig/Environment.php
lib/Twig/Extension.php
lib/Twig/Extension/Core.php
lib/Twig/Extension/Escaper.php
lib/Twig/Extension/Sandbox.php
lib/Twig/ExtensionInterface.php
lib/Twig/NodeTransformer.php [deleted file]
lib/Twig/NodeTransformer/Chain.php [deleted file]
lib/Twig/NodeTraverser.php [new file with mode: 0644]
lib/Twig/NodeVisitor/Escaper.php [moved from lib/Twig/NodeTransformer/Escaper.php with 66% similarity]
lib/Twig/NodeVisitor/Filter.php [moved from lib/Twig/NodeTransformer/Filter.php with 71% similarity]
lib/Twig/NodeVisitor/Sandbox.php [moved from lib/Twig/NodeTransformer/Sandbox.php with 52% similarity]
lib/Twig/Parser.php

index a1c83f3..c455abc 100644 (file)
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -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
index daabe84..c691d97 100644 (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()
index 82046e9..a3c8156 100644 (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();
   }
index b8154d2..9c2ed90 100644 (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(),
+    );
   }
 
   /**
index 05eaec0..4a8a7cc 100644 (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;
index 0baee4b..ddca886 100644 (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()
index cbfb96c..d18c3c3 100644 (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.
diff --git a/lib/Twig/NodeTransformer.php b/lib/Twig/NodeTransformer.php
deleted file mode 100644 (file)
index 82c4fbc..0000000
+++ /dev/null
@@ -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;
-  }
-}
diff --git a/lib/Twig/NodeTransformer/Chain.php b/lib/Twig/NodeTransformer/Chain.php
deleted file mode 100644 (file)
index 6fd6fa0..0000000
+++ /dev/null
@@ -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;
-  }
-}
diff --git a/lib/Twig/NodeTraverser.php b/lib/Twig/NodeTraverser.php
new file mode 100644 (file)
index 0000000..a781504
--- /dev/null
@@ -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);
+  }
+}
similarity index 66%
rename from lib/Twig/NodeTransformer/Escaper.php
rename to lib/Twig/NodeVisitor/Escaper.php
index 646a04d..098345a 100644 (file)
@@ -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->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;
 
@@ -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;
     }
   }
 
similarity index 71%
rename from lib/Twig/NodeTransformer/Filter.php
rename to lib/Twig/NodeVisitor/Filter.php
index 105456d..02a27eb 100644 (file)
@@ -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();
+    }
+    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;
similarity index 52%
rename from lib/Twig/NodeTransformer/Sandbox.php
rename to lib/Twig/NodeVisitor/Sandbox.php
index 0b08819..3739376 100644 (file)
@@ -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)
-    {
       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;
   }
index 4eea96d..29d60ef 100644 (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()