made auto-escaping more configurable with the new is_escaper option
authorfabien <fabien@93ef8e89-cb99-4229-a87c-7fa0fa45744b>
Tue, 15 Dec 2009 07:15:46 +0000 (07:15 +0000)
committerfabien <fabien@93ef8e89-cb99-4229-a87c-7fa0fa45744b>
Tue, 15 Dec 2009 07:15:46 +0000 (07:15 +0000)
git-svn-id: http://svn.twig-project.org/trunk@186 93ef8e89-cb99-4229-a87c-7fa0fa45744b

CHANGELOG
doc/03-Twig-for-Developers.markdown
doc/04-Extending-Twig.markdown
lib/Twig/Extension/Core.php
lib/Twig/Extension/Escaper.php
lib/Twig/Filter.php
lib/Twig/NodeTransformer/Escaper.php

index 6949c3b..e0c9b58 100644 (file)
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -12,7 +12,7 @@ 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)),
 
- * changed the automatic-escaping rules to be more sensible (the documentation lists all the rules)
+ * 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
  * changed the Array and String loaders to actually make use of the cache mechanism
  * included the default filter function definitions in the extension class files directly (Core, Escaper)
index b17cd10..1a9a0da 100644 (file)
@@ -341,6 +341,10 @@ The escaping rules are implemented as follows:
        {{ var|foo(bar) }} {# bar will be escaped #}
        {{ var|foo(bar|safe) }} {# bar won't be escaped #}
 
+ * Automatic escaping is not applied if one of the filter in the chain has the
+   `is_escaper` option set to `true` (this is the case for the built-in
+   `escaper`, `safe`, and `urlencode` filters for instance).
+
 ### Sandbox Extension
 
 The `sandbox` extension can be used to evaluate untrusted code. Access to
index 7bc1ee6..ca6a1cc 100644 (file)
@@ -115,25 +115,6 @@ method:
       }
     }
 
-The `Twig_Filter` classes take flags as their last argument. For instance, if
-you want access to the current environment instance in your filter, set the
-`needs_environment` option to `true`:
-
-    [php]
-    $filter = new Twig_Filter_Function('str_rot13', array('needs_environment' => true));
-
-Twig will then pass the current environment as the first argument to the
-filter call:
-
-    [php]
-    function twig_compute_rot13(Twig_Environment $env, $string)
-    {
-      // get the current charset for instance
-      $charset = $env->getCharset();
-
-      return str_rot13($string);
-    }
-
 Parameters passed to the filter are available as extra arguments to the
 function call:
 
@@ -143,11 +124,8 @@ function call:
 -
 
     [php]
-    function twig_compute_rot13(Twig_Environment $env, $string, $prefix = '')
+    function twig_compute_rot13($string, $prefix = '')
     {
-      // get the current charset for instance
-      $charset = $env->getCharset();
-
       return $prefix.str_rot13($string);
     }
 
@@ -207,6 +185,41 @@ Using methods for filters is a great way to package your filter without
 polluting the global namespace. This also gives the developer more flexibility
 at the cost of a small overhead.
 
+### Environment aware Filters
+
+The `Twig_Filter` classes take options as their last argument. For instance, if
+you want access to the current environment instance in your filter, set the
+`needs_environment` option to `true`:
+
+    [php]
+    $filter = new Twig_Filter_Function('str_rot13', array('needs_environment' => true));
+
+Twig will then pass the current environment as the first argument to the
+filter call:
+
+    [php]
+    function twig_compute_rot13(Twig_Environment $env, $string)
+    {
+      // get the current charset for instance
+      $charset = $env->getCharset();
+
+      return str_rot13($string);
+    }
+
+### Automatic Escaping
+
+If automatic escaping is enabled, the main value passed to the filters is
+automatically escaped. If your filter acts as an escaper, you will want the
+raw variable value. In such a case, set the `is_escaper` option to `true`:
+
+    [php]
+    $filter = new Twig_Filter_Function('urlencode', array('is_escaper' => true));
+
+>**NOTE**
+>The parameters passed as extra arguments to the filters are not affected by
+>the `is_escaper` option and they are always escaped according to the
+>automatic escaping rules.
+
 Overriding default Filters
 --------------------------
 
index 3da9c72..b8154d2 100644 (file)
@@ -70,7 +70,7 @@ class Twig_Extension_Core extends Twig_Extension
       'floor' => new Twig_Filter_Function('twig_floor_filter'),
 
       // encoding
-      'urlencode' => new Twig_Filter_Function('twig_urlencode_filter'),
+      'urlencode' => new Twig_Filter_Function('twig_urlencode_filter', array('is_escaper' => true)),
 
       // string filters
       'title'      => new Twig_Filter_Function('twig_title_string_filter', array('needs_environment' => true)),
@@ -93,8 +93,8 @@ class Twig_Extension_Core extends Twig_Extension
       'items'   => new Twig_Filter_Function('twig_get_array_items_filter'),
 
       // escaping
-      'escape' => new Twig_Filter_Function('twig_escape_filter', array('needs_environment' => true)),
-      'e'      => new Twig_Filter_Function('twig_escape_filter', array('needs_environment' => true)),
+      'escape' => new Twig_Filter_Function('twig_escape_filter', array('needs_environment' => true, 'is_escaper' => true)),
+      'e'      => new Twig_Filter_Function('twig_escape_filter', array('needs_environment' => true, 'is_escaper' => true)),
     );
 
     if (function_exists('mb_get_info'))
index 681989d..05eaec0 100644 (file)
@@ -54,7 +54,7 @@ class Twig_Extension_Escaper extends Twig_Extension
   public function getFilters()
   {
     return array(
-      'safe' => new Twig_Filter_Function('twig_safe_filter'),
+      'safe' => new Twig_Filter_Function('twig_safe_filter', array('is_escaper' => true)),
     );
   }
 
index f4249a2..698e01e 100644 (file)
@@ -24,6 +24,7 @@ abstract class Twig_Filter
   {
     $this->options = array_merge(array(
       'needs_environment' => false,
+      'is_escaper'        => false,
     ), $options);
   }
 
@@ -33,4 +34,9 @@ abstract class Twig_Filter
   {
     return $this->options['needs_environment'];
   }
+
+  public function isEscaper()
+  {
+    return $this->options['is_escaper'];
+  }
 }
index 1b1c9a3..3943186 100644 (file)
@@ -46,19 +46,22 @@ class Twig_NodeTransformer_Escaper extends Twig_NodeTransformer
 
     if ($expression instanceof Twig_Node_Expression_Filter)
     {
-      // don't escape if escape has already been called
-      // or if we want the safe string
-      if ($expression->hasFilter('escape') || $expression->hasFilter('safe'))
-      {
-        return $node;
-      }
-
       // don't escape if the primary node of the filter is not a variable
       $nodes = $expression->getNodes();
       if (!$nodes[0] instanceof Twig_Node_Expression_Name)
       {
         return $node;
       }
+
+      // don't escape if there is already an "escaper" in the filter chain
+      $filterMap = $this->env->getFilters();
+      foreach ($expression->getFilters() as $filter)
+      {
+        if (isset($filterMap[$filter[0]]) && $filterMap[$filter[0]]->isEscaper())
+        {
+          return $node;
+        }
+      }
     }
     elseif (!$expression instanceof Twig_Node_Expression_Name)
     {