added Twig_Filter_Node to allow more complex tests to have their own Node class ...
authorFabien Potencier <fabien.potencier@gmail.com>
Tue, 1 Nov 2011 08:31:49 +0000 (09:31 +0100)
committerFabien Potencier <fabien.potencier@gmail.com>
Tue, 1 Nov 2011 10:48:05 +0000 (11:48 +0100)
CHANGELOG
lib/Twig/ExpressionParser.php
lib/Twig/Extension/Core.php
lib/Twig/Filter/Node.php [new file with mode: 0644]
lib/Twig/Node/Expression/DefaultFilter.php [deleted file]
lib/Twig/Node/Expression/Filter/Default.php [new file with mode: 0644]

index 341b57c..40b99c7 100644 (file)
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,8 +1,9 @@
 * 1.4.0
 
+ * added Twig_Filter_Node to allow more complex filters to have their own Node class
  * added Twig_Test_Node to allow more complex tests to have their own Node class
  * added a better error message when a template is empty but contain a BOM
- * fixed in operator for empty strings
+ * fixed "in" operator for empty strings
  * fixed the "defined" test and the "default" filter (now works with more than one call (foo.bar.foo) and for both values of the strict_variables option)
  * changed the way extensions are loaded (addFilter/addFunction/addGlobal/addTest/addNodeVisitor/addTokenParser/addExtension can now be called in any order)
  * added Twig_Environment::display()
index e218e40..c1e384e 100644 (file)
@@ -318,12 +318,15 @@ class Twig_ExpressionParser
                 $arguments = $this->parseArguments();
             }
 
-            $node = new Twig_Node_Expression_Filter($node, $name, $arguments, $token->getLine(), $tag);
-
-            if (Twig_Node_Expression_DefaultFilter::isDefaultFilter($node)) {
-                $node = new Twig_Node_Expression_DefaultFilter($node);
+            $filterMap = $this->parser->getEnvironment()->getFilters();
+            if (isset($filterMap[$name->getAttribute('value')]) && $filterMap[$name->getAttribute('value')] instanceof Twig_Filter_Node) {
+                $class = $filterMap[$name->getAttribute('value')]->getClass();
+            } else {
+                $class = 'Twig_Node_Expression_Filter';
             }
 
+            $node = new $class($node, $name, $arguments, $token->getLine(), $tag);
+
             if (!$this->parser->getStream()->test(Twig_Token::PUNCTUATION_TYPE, '|')) {
                 break;
             }
index feb8122..5e5fdee 100644 (file)
@@ -70,7 +70,9 @@ class Twig_Extension_Core extends Twig_Extension
             'merge'   => new Twig_Filter_Function('twig_array_merge'),
 
             // iteration and runtime
-            'default' => new Twig_Filter_Function('twig_default_filter'),
+            'default' => new Twig_Filter_Node('Twig_Node_Expression_Filter_Default'),
+            '_default' => new Twig_Filter_Function('_twig_default_filter'),
+
             'keys'    => new Twig_Filter_Function('twig_get_array_keys_filter'),
 
             // escaping
@@ -361,21 +363,10 @@ function twig_join_filter($value, $glue = '')
     return implode($glue, (array) $value);
 }
 
-/**
- * Returns the value or the default value when it is undefined or empty.
- *
- * <pre>
- *
- *  {{ var.foo|default('foo item on var is not defined') }}
- *
- * </pre>
- *
- * @param mixed $value   A value
- * @param mixed $default The default value
- *
- * @param mixed The value or the default value;
- */
-function twig_default_filter($value, $default = '')
+// The '_default' filter is used internally to avoid using the ternary operator
+// which costs a lot for big contexts (before PHP 5.4). So, on average,
+// a function call is cheaper.
+function _twig_default_filter($value, $default = '')
 {
     if (twig_test_empty($value)) {
         return $default;
diff --git a/lib/Twig/Filter/Node.php b/lib/Twig/Filter/Node.php
new file mode 100644 (file)
index 0000000..7481c05
--- /dev/null
@@ -0,0 +1,37 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) 2011 Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Represents a template filter as a node.
+ *
+ * @package    twig
+ * @author     Fabien Potencier <fabien@symfony.com>
+ */
+class Twig_Filter_Node extends Twig_Filter
+{
+    protected $class;
+
+    public function __construct($class, array $options = array())
+    {
+        parent::__construct($options);
+
+        $this->class = $class;
+    }
+
+    public function getClass()
+    {
+        return $this->class;
+    }
+
+    public function compile()
+    {
+    }
+}
diff --git a/lib/Twig/Node/Expression/DefaultFilter.php b/lib/Twig/Node/Expression/DefaultFilter.php
deleted file mode 100644 (file)
index e2d8cac..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-<?php
-
-/*
- * This file is part of Twig.
- *
- * (c) 2011 Fabien Potencier
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-class Twig_Node_Expression_DefaultFilter extends Twig_Node_Expression
-{
-    public function __construct(Twig_Node_Expression_Filter $node)
-    {
-        if (!self::isDefaultFilter($node)) {
-            throw new Twig_Error('The default filter node cannot be created from the given node.', $node->getLine());
-        }
-
-        $test = new Twig_Node_Expression_Test_Defined(clone $node->getNode('node'), 'defined', new Twig_Node(), $node->getLine());
-        $default = count($node->getNode('arguments')) ? $node->getNode('arguments')->getNode(0) : new Twig_Node_Expression_Constant('', $node->getLine());
-
-        $node = new Twig_Node_Expression_Conditional($test, $node, $default, $node->getLine());
-
-        parent::__construct(array('node' => $node), array(), $node->getLine());
-    }
-
-    public function compile(Twig_Compiler $compiler)
-    {
-        $compiler->subcompile($this->getNode('node'));
-    }
-
-    /**
-     * Checks whether a node is a default filter that needs to be wrapped with Twig_Node_Expression_DefaultFilter.
-     *
-     * The default filter needs to be wrapped with an instance of Twig_Node_Expression_DefaultFilter
-     * when the filtered value is a name (like obj) or an attribute (like obj.attr).
-     *
-     * In such a case, it's compiled to {{ obj is defined ? obj|default('bar') : 'bar' }}
-     *
-     * @param Twig_NodeInterface $node A Twig_NodeInterface instance
-     *
-     * @return Boolean true if the node must be wrapped with a Twig_Node_Expression_DefaultFilter, false otherwise
-     */
-    static public function isDefaultFilter(Twig_NodeInterface $node)
-    {
-        return $node instanceof Twig_Node_Expression_Filter && 'default' === $node->getNode('filter')->getAttribute('value') && ($node->getNode('node') instanceof Twig_Node_Expression_Name || $node->getNode('node') instanceof Twig_Node_Expression_GetAttr);
-    }
-}
diff --git a/lib/Twig/Node/Expression/Filter/Default.php b/lib/Twig/Node/Expression/Filter/Default.php
new file mode 100644 (file)
index 0000000..1cb3342
--- /dev/null
@@ -0,0 +1,44 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) 2011 Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Returns the value or the default value when it is undefined or empty.
+ *
+ * <pre>
+ *  {{ var.foo|default('foo item on var is not defined') }}
+ * </pre>
+ *
+ * @package twig
+ * @author  Fabien Potencier <fabien@symfony.com>
+ */
+class Twig_Node_Expression_Filter_Default extends Twig_Node_Expression_Filter
+{
+    public function __construct(Twig_NodeInterface $node, Twig_Node_Expression_Constant $filterName, Twig_NodeInterface $arguments, $lineno, $tag = null)
+    {
+        $default = new Twig_Node_Expression_Filter($node, new Twig_Node_Expression_Constant('_default', $node->getLine()), $arguments, $node->getLine());
+
+        if ('default' === $filterName->getAttribute('value') && ($node instanceof Twig_Node_Expression_Name || $node instanceof Twig_Node_Expression_GetAttr)) {
+            $test = new Twig_Node_Expression_Test_Defined(clone $node, 'defined', new Twig_Node(), $node->getLine());
+            $false = count($arguments) ? $arguments->getNode(0) : new Twig_Node_Expression_Constant('', $node->getLine());
+
+            $node = new Twig_Node_Expression_Conditional($test, $default, $false, $node->getLine());
+        } else {
+            $node = $default;
+        }
+
+        parent::__construct($node, $filterName, $arguments, $lineno, $tag);
+    }
+
+    public function compile(Twig_Compiler $compiler)
+    {
+        $compiler->subcompile($this->getNode('node'));
+    }
+}