From: fabien Date: Tue, 15 Dec 2009 07:15:46 +0000 (+0000) Subject: made auto-escaping more configurable with the new is_escaper option X-Git-Url: http://git.silmor.de/gitweb/?a=commitdiff_plain;h=2f0d54d0c904cad931908d3039c635d28bb0f4b5;p=konrad%2Ftwig.git made auto-escaping more configurable with the new is_escaper option git-svn-id: http://svn.twig-project.org/trunk@186 93ef8e89-cb99-4229-a87c-7fa0fa45744b --- diff --git a/CHANGELOG b/CHANGELOG index 6949c3b..e0c9b58 100644 --- 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) diff --git a/doc/03-Twig-for-Developers.markdown b/doc/03-Twig-for-Developers.markdown index b17cd10..1a9a0da 100644 --- a/doc/03-Twig-for-Developers.markdown +++ b/doc/03-Twig-for-Developers.markdown @@ -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 diff --git a/doc/04-Extending-Twig.markdown b/doc/04-Extending-Twig.markdown index 7bc1ee6..ca6a1cc 100644 --- a/doc/04-Extending-Twig.markdown +++ b/doc/04-Extending-Twig.markdown @@ -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 -------------------------- diff --git a/lib/Twig/Extension/Core.php b/lib/Twig/Extension/Core.php index 3da9c72..b8154d2 100644 --- a/lib/Twig/Extension/Core.php +++ b/lib/Twig/Extension/Core.php @@ -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')) diff --git a/lib/Twig/Extension/Escaper.php b/lib/Twig/Extension/Escaper.php index 681989d..05eaec0 100644 --- a/lib/Twig/Extension/Escaper.php +++ b/lib/Twig/Extension/Escaper.php @@ -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)), ); } diff --git a/lib/Twig/Filter.php b/lib/Twig/Filter.php index f4249a2..698e01e 100644 --- a/lib/Twig/Filter.php +++ b/lib/Twig/Filter.php @@ -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']; + } } diff --git a/lib/Twig/NodeTransformer/Escaper.php b/lib/Twig/NodeTransformer/Escaper.php index 1b1c9a3..3943186 100644 --- a/lib/Twig/NodeTransformer/Escaper.php +++ b/lib/Twig/NodeTransformer/Escaper.php @@ -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) {