From 3427adc7ecf36f58a1fb473a2472e46311b34311 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Tue, 1 Nov 2011 09:31:49 +0100 Subject: [PATCH] added Twig_Filter_Node to allow more complex tests to have their own Node class (the default filter has been converted to use this new feature) --- CHANGELOG | 3 +- lib/Twig/ExpressionParser.php | 11 ++++-- lib/Twig/Extension/Core.php | 23 ++++--------- lib/Twig/Filter/Node.php | 37 ++++++++++++++++++++ lib/Twig/Node/Expression/DefaultFilter.php | 48 --------------------------- lib/Twig/Node/Expression/Filter/Default.php | 44 ++++++++++++++++++++++++ 6 files changed, 97 insertions(+), 69 deletions(-) create mode 100644 lib/Twig/Filter/Node.php delete mode 100644 lib/Twig/Node/Expression/DefaultFilter.php create mode 100644 lib/Twig/Node/Expression/Filter/Default.php diff --git a/CHANGELOG b/CHANGELOG index 341b57c..40b99c7 100644 --- 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() diff --git a/lib/Twig/ExpressionParser.php b/lib/Twig/ExpressionParser.php index e218e40..c1e384e 100644 --- a/lib/Twig/ExpressionParser.php +++ b/lib/Twig/ExpressionParser.php @@ -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; } diff --git a/lib/Twig/Extension/Core.php b/lib/Twig/Extension/Core.php index feb8122..5e5fdee 100644 --- a/lib/Twig/Extension/Core.php +++ b/lib/Twig/Extension/Core.php @@ -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. - * - *
- *
- *  {{ var.foo|default('foo item on var is not defined') }}
- *
- * 
- * - * @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 index 0000000..7481c05 --- /dev/null +++ b/lib/Twig/Filter/Node.php @@ -0,0 +1,37 @@ + + */ +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 index e2d8cac..0000000 --- a/lib/Twig/Node/Expression/DefaultFilter.php +++ /dev/null @@ -1,48 +0,0 @@ -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 index 0000000..1cb3342 --- /dev/null +++ b/lib/Twig/Node/Expression/Filter/Default.php @@ -0,0 +1,44 @@ + + * {{ var.foo|default('foo item on var is not defined') }} + * + * + * @package twig + * @author Fabien Potencier + */ +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')); + } +} -- 1.7.2.5