From dc71bf1a824f616a4cadcf666ce5d1c19014ad48 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Fri, 26 Nov 2010 20:57:53 +0100 Subject: [PATCH] made operators configurable --- lib/Twig/Environment.php | 40 +++++++++++++++++++++++++ lib/Twig/ExpressionParser.php | 61 ++------------------------------------ lib/Twig/Extension.php | 10 ++++++ lib/Twig/Extension/Core.php | 56 +++++++++++++++++++++++++++++++++++ lib/Twig/ExtensionInterface.php | 7 ++++ lib/Twig/Parser.php | 2 +- 6 files changed, 118 insertions(+), 58 deletions(-) diff --git a/lib/Twig/Environment.php b/lib/Twig/Environment.php index b8b6a9c..d03984b 100644 --- a/lib/Twig/Environment.php +++ b/lib/Twig/Environment.php @@ -30,6 +30,8 @@ class Twig_Environment protected $runtimeInitialized; protected $loadedTemplates; protected $strictVariables; + protected $unaryOperators; + protected $binaryOperators; /** * Constructor. @@ -414,6 +416,44 @@ class Twig_Environment return $this->tests; } + public function getUnaryOperators() + { + if (null === $this->unaryOperators) { + $this->initOperators(); + } + + return $this->unaryOperators; + } + + public function getBinaryOperators() + { + if (null === $this->binaryOperators) { + $this->initOperators(); + } + + return $this->binaryOperators; + } + + protected function initOperators() + { + $this->unaryOperators = array(); + $this->binaryOperators = array(); + foreach ($this->getExtensions() as $extension) { + $operators = $extension->getOperators(); + + if (!$operators) { + continue; + } + + if (2 !== count($operators)) { + throw new InvalidArgumentException(sprintf('"%s::getOperators()" does not return a valid operators array.', get_class($extension))); + } + + $this->unaryOperators = array_merge($this->unaryOperators, $operators[0]); + $this->binaryOperators = array_merge($this->binaryOperators, $operators[1]); + } + } + protected function writeCacheFile($file, $content) { $tmpFile = tempnam(dirname($file), basename($file)); diff --git a/lib/Twig/ExpressionParser.php b/lib/Twig/ExpressionParser.php index 80f8d1d..a014c94 100644 --- a/lib/Twig/ExpressionParser.php +++ b/lib/Twig/ExpressionParser.php @@ -30,47 +30,11 @@ class Twig_ExpressionParser protected $unaryOperators; protected $binaryOperators; - public function __construct(Twig_Parser $parser) + public function __construct(Twig_Parser $parser, array $unaryOperators, array $binaryOperators) { $this->parser = $parser; - $this->unaryOperators = $this->getUnaryOperators(); - $this->binaryOperators = $this->getBinaryOperators(); - } - - public function getUnaryOperators() - { - return array( - 'not' => array('precedence' => 50, 'class' => 'Twig_Node_Expression_Unary_Not'), - '-' => array('precedence' => 50, 'class' => 'Twig_Node_Expression_Unary_Neg'), - '+' => array('precedence' => 50, 'class' => 'Twig_Node_Expression_Unary_Pos'), - ); - } - - public function getBinaryOperators() - { - return array( - 'or' => array('precedence' => 10, 'class' => 'Twig_Node_Expression_Binary_Or', 'associativity' => self::OPERATOR_LEFT), - 'and' => array('precedence' => 15, 'class' => 'Twig_Node_Expression_Binary_And', 'associativity' => self::OPERATOR_LEFT), - '==' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Equal', 'associativity' => self::OPERATOR_LEFT), - '!=' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_NotEqual', 'associativity' => self::OPERATOR_LEFT), - '<' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Less', 'associativity' => self::OPERATOR_LEFT), - '>' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Greater', 'associativity' => self::OPERATOR_LEFT), - '>=' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_GreaterEqual', 'associativity' => self::OPERATOR_LEFT), - '<=' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_LessEqual', 'associativity' => self::OPERATOR_LEFT), - 'not in' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_NotIn', 'associativity' => self::OPERATOR_LEFT), - 'in' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_In', 'associativity' => self::OPERATOR_LEFT), - '+' => array('precedence' => 30, 'class' => 'Twig_Node_Expression_Binary_Add', 'associativity' => self::OPERATOR_LEFT), - '-' => array('precedence' => 30, 'class' => 'Twig_Node_Expression_Binary_Sub', 'associativity' => self::OPERATOR_LEFT), - '~' => array('precedence' => 40, 'class' => 'Twig_Node_Expression_Binary_Concat', 'associativity' => self::OPERATOR_LEFT), - '*' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_Mul', 'associativity' => self::OPERATOR_LEFT), - '/' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_Div', 'associativity' => self::OPERATOR_LEFT), - '//' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_FloorDiv', 'associativity' => self::OPERATOR_LEFT), - '%' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_Mod', 'associativity' => self::OPERATOR_LEFT), - 'is' => array('precedence' => 100, 'callable' => array($this, 'parseTestExpression'), 'associativity' => self::OPERATOR_LEFT), - 'is not' => array('precedence' => 100, 'callable' => array($this, 'parseNotTestExpression'), 'associativity' => self::OPERATOR_LEFT), - '..' => array('precedence' => 110, 'class' => 'Twig_Node_Expression_Binary_Range', 'associativity' => self::OPERATOR_LEFT), - '**' => array('precedence' => 200, 'class' => 'Twig_Node_Expression_Binary_Power', 'associativity' => self::OPERATOR_RIGHT), - ); + $this->unaryOperators = $unaryOperators; + $this->binaryOperators = $binaryOperators; } public function parseExpression($precedence = 0) @@ -82,7 +46,7 @@ class Twig_ExpressionParser $this->parser->getStream()->next(); if (isset($op['callable'])) { - $expr = call_user_func($op['callable'], $expr); + $expr = call_user_func($op['callable'], $this->parser, $expr); } else { $expr1 = $this->parseExpression(self::OPERATOR_LEFT === $op['associativity'] ? $op['precedence'] + 1 : $op['precedence']); $class = $op['class']; @@ -250,23 +214,6 @@ class Twig_ExpressionParser return $node; } - public function parseNotTestExpression($node) - { - return new Twig_Node_Expression_Unary_Not($this->parseTestExpression($node), $this->parser->getCurrentToken()->getLine()); - } - - public function parseTestExpression($node) - { - $stream = $this->parser->getStream(); - $name = $stream->expect(Twig_Token::NAME_TYPE); - $arguments = null; - if ($stream->test(Twig_Token::PUNCTUATION_TYPE, '(')) { - $arguments = $this->parseArguments($node); - } - - return new Twig_Node_Expression_Test($node, $name->getValue(), $arguments, $this->parser->getCurrentToken()->getLine()); - } - public function parseSubscriptExpression($node) { $token = $this->parser->getStream()->next(); diff --git a/lib/Twig/Extension.php b/lib/Twig/Extension.php index dc1d34a..9294fc5 100644 --- a/lib/Twig/Extension.php +++ b/lib/Twig/Extension.php @@ -60,4 +60,14 @@ abstract class Twig_Extension implements Twig_ExtensionInterface { return array(); } + + /** + * Returns a list of operators to add to the existing list. + * + * @return array An array of operators + */ + public function getOperators() + { + return array(); + } } diff --git a/lib/Twig/Extension/Core.php b/lib/Twig/Extension/Core.php index e19fff5..cb3a110 100644 --- a/lib/Twig/Extension/Core.php +++ b/lib/Twig/Extension/Core.php @@ -103,6 +103,62 @@ class Twig_Extension_Core extends Twig_Extension } /** + * Returns a list of operators to add to the existing list. + * + * @return array An array of operators + */ + public function getOperators() + { + return array( + array( + 'not' => array('precedence' => 50, 'class' => 'Twig_Node_Expression_Unary_Not'), + '-' => array('precedence' => 50, 'class' => 'Twig_Node_Expression_Unary_Neg'), + '+' => array('precedence' => 50, 'class' => 'Twig_Node_Expression_Unary_Pos'), + ), + array( + 'or' => array('precedence' => 10, 'class' => 'Twig_Node_Expression_Binary_Or', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + 'and' => array('precedence' => 15, 'class' => 'Twig_Node_Expression_Binary_And', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + '==' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Equal', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + '!=' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_NotEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + '<' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Less', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + '>' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Greater', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + '>=' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_GreaterEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + '<=' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_LessEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + 'not in' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_NotIn', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + 'in' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_In', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + '+' => array('precedence' => 30, 'class' => 'Twig_Node_Expression_Binary_Add', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + '-' => array('precedence' => 30, 'class' => 'Twig_Node_Expression_Binary_Sub', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + '~' => array('precedence' => 40, 'class' => 'Twig_Node_Expression_Binary_Concat', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + '*' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_Mul', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + '/' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_Div', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + '//' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_FloorDiv', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + '%' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_Mod', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + 'is' => array('precedence' => 100, 'callable' => array($this, 'parseTestExpression'), 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + 'is not' => array('precedence' => 100, 'callable' => array($this, 'parseNotTestExpression'), 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + '..' => array('precedence' => 110, 'class' => 'Twig_Node_Expression_Binary_Range', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + '**' => array('precedence' => 200, 'class' => 'Twig_Node_Expression_Binary_Power', 'associativity' => Twig_ExpressionParser::OPERATOR_RIGHT), + ), + ); + } + + public function parseNotTestExpression($parser, $node) + { + return new Twig_Node_Expression_Unary_Not($this->parseTestExpression($parser, $node), $parser->getCurrentToken()->getLine()); + } + + public function parseTestExpression($parser, $node) + { + $stream = $parser->getStream(); + $name = $stream->expect(Twig_Token::NAME_TYPE); + $arguments = null; + if ($stream->test(Twig_Token::PUNCTUATION_TYPE, '(')) { + $arguments = $parser->getExpressionParser()->parseArguments($node); + } + + return new Twig_Node_Expression_Test($node, $name->getValue(), $arguments, $parser->getCurrentToken()->getLine()); + } + + /** * Returns the name of the extension. * * @return string The extension name diff --git a/lib/Twig/ExtensionInterface.php b/lib/Twig/ExtensionInterface.php index 69f9683..f3684f9 100644 --- a/lib/Twig/ExtensionInterface.php +++ b/lib/Twig/ExtensionInterface.php @@ -55,6 +55,13 @@ interface Twig_ExtensionInterface public function getTests(); /** + * Returns a list of operators to add to the existing list. + * + * @return array An array of operators + */ + public function getOperators(); + + /** * Returns the name of the extension. * * @return string The extension name diff --git a/lib/Twig/Parser.php b/lib/Twig/Parser.php index cb224d0..acc17e6 100644 --- a/lib/Twig/Parser.php +++ b/lib/Twig/Parser.php @@ -50,7 +50,7 @@ class Twig_Parser implements Twig_ParserInterface $this->visitors = $this->env->getNodeVisitors(); if (null === $this->expressionParser) { - $this->expressionParser = new Twig_ExpressionParser($this); + $this->expressionParser = new Twig_ExpressionParser($this, $this->env->getUnaryOperators(), $this->env->getBinaryOperators()); } $this->stream = $stream; -- 1.7.2.5