From 0d6686ec3029d7411eb6fe813714ba6458dde59a Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 24 Oct 2016 15:21:07 -0700 Subject: [PATCH 1/2] moved tests handling in Twig_ParserExpression instead of Twig_Extension_Core --- lib/Twig/ExpressionParser.php | 74 ++++++++++++++++++++++++++++++++++- lib/Twig/Extension/Core.php | 72 +--------------------------------- 2 files changed, 75 insertions(+), 71 deletions(-) diff --git a/lib/Twig/ExpressionParser.php b/lib/Twig/ExpressionParser.php index 3c4cadee0..577617d53 100644 --- a/lib/Twig/ExpressionParser.php +++ b/lib/Twig/ExpressionParser.php @@ -44,7 +44,11 @@ class Twig_ExpressionParser $op = $this->binaryOperators[$token->getValue()]; $this->parser->getStream()->next(); - if (isset($op['callable'])) { + if ('is not' === $token->getValue()) { + $expr = $this->parseNotTestExpression($expr); + } elseif ('is' === $token->getValue()) { + $expr = $this->parseTestExpression($expr); + } elseif (isset($op['callable'])) { $expr = call_user_func($op['callable'], $this->parser, $expr); } else { $expr1 = $this->parseExpression(self::OPERATOR_LEFT === $op['associativity'] ? $op['precedence'] + 1 : $op['precedence']); @@ -567,6 +571,74 @@ class Twig_ExpressionParser return new Twig_Node($targets); } + private function parseNotTestExpression(Twig_NodeInterface $node) + { + return new Twig_Node_Expression_Unary_Not($this->parseTestExpression($node), $this->parser->getCurrentToken()->getLine()); + } + + private function parseTestExpression(Twig_NodeInterface $node) + { + $stream = $this->parser->getStream(); + list($name, $test) = $this->getTest($node->getTemplateLine()); + + $class = $this->getTestNodeClass($test); + $arguments = null; + if ($stream->test(Twig_Token::PUNCTUATION_TYPE, '(')) { + $arguments = $this->parser->getExpressionParser()->parseArguments(true); + } + + return new $class($node, $name, $arguments, $this->parser->getCurrentToken()->getLine()); + } + + private function getTest($line) + { + $stream = $this->parser->getStream(); + $name = $stream->expect(Twig_Token::NAME_TYPE)->getValue(); + $env = $this->parser->getEnvironment(); + + if ($test = $env->getTest($name)) { + return array($name, $test); + } + + if ($stream->test(Twig_Token::NAME_TYPE)) { + // try 2-words tests + $name = $name.' '.$this->parser->getCurrentToken()->getValue(); + + if ($test = $env->getTest($name)) { + $stream->next(); + + return array($name, $test); + } + } + + $e = new Twig_Error_Syntax(sprintf('Unknown "%s" test.', $name), $line, $stream->getSourceContext()->getName()); + $e->addSuggestions($name, array_keys($env->getTests())); + + throw $e; + } + + private function getTestNodeClass($test) + { + if ($test instanceof Twig_SimpleTest && $test->isDeprecated()) { + $message = sprintf('Twig Test "%s" is deprecated', $name); + if (!is_bool($test->getDeprecatedVersion())) { + $message .= sprintf(' since version %s', $test->getDeprecatedVersion()); + } + if ($test->getAlternative()) { + $message .= sprintf('. Use "%s" instead', $test->getAlternative()); + } + $message .= sprintf(' in %s at line %d.', $stream->getSourceContext()->getName(), $stream->getCurrent()->getLine()); + + @trigger_error($message, E_USER_DEPRECATED); + } + + if ($test instanceof Twig_SimpleTest) { + return $test->getNodeClass(); + } + + return $test instanceof Twig_Test_Node ? $test->getClass() : 'Twig_Node_Expression_Test'; + } + protected function getFunctionNodeClass($name, $line) { $env = $this->parser->getEnvironment(); diff --git a/lib/Twig/Extension/Core.php b/lib/Twig/Extension/Core.php index f8aca2cac..67b55dde5 100644 --- a/lib/Twig/Extension/Core.php +++ b/lib/Twig/Extension/Core.php @@ -258,82 +258,14 @@ class Twig_Extension_Core extends Twig_Extension '/' => 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), + 'is' => array('precedence' => 100, 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + 'is not' => array('precedence' => 100, 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), '**' => array('precedence' => 200, 'class' => 'Twig_Node_Expression_Binary_Power', 'associativity' => Twig_ExpressionParser::OPERATOR_RIGHT), '??' => array('precedence' => 300, 'class' => 'Twig_Node_Expression_NullCoalesce', 'associativity' => Twig_ExpressionParser::OPERATOR_RIGHT), ), ); } - public function parseNotTestExpression(Twig_Parser $parser, Twig_NodeInterface $node) - { - return new Twig_Node_Expression_Unary_Not($this->parseTestExpression($parser, $node), $parser->getCurrentToken()->getLine()); - } - - public function parseTestExpression(Twig_Parser $parser, Twig_NodeInterface $node) - { - $stream = $parser->getStream(); - list($name, $test) = $this->getTest($parser, $node->getTemplateLine()); - - if ($test instanceof Twig_SimpleTest && $test->isDeprecated()) { - $message = sprintf('Twig Test "%s" is deprecated', $name); - if (!is_bool($test->getDeprecatedVersion())) { - $message .= sprintf(' since version %s', $test->getDeprecatedVersion()); - } - if ($test->getAlternative()) { - $message .= sprintf('. Use "%s" instead', $test->getAlternative()); - } - $message .= sprintf(' in %s at line %d.', $stream->getSourceContext()->getName(), $stream->getCurrent()->getLine()); - - @trigger_error($message, E_USER_DEPRECATED); - } - - $class = $this->getTestNodeClass($parser, $test); - $arguments = null; - if ($stream->test(Twig_Token::PUNCTUATION_TYPE, '(')) { - $arguments = $parser->getExpressionParser()->parseArguments(true); - } - - return new $class($node, $name, $arguments, $parser->getCurrentToken()->getLine()); - } - - protected function getTest(Twig_Parser $parser, $line) - { - $stream = $parser->getStream(); - $name = $stream->expect(Twig_Token::NAME_TYPE)->getValue(); - $env = $parser->getEnvironment(); - - if ($test = $env->getTest($name)) { - return array($name, $test); - } - - if ($stream->test(Twig_Token::NAME_TYPE)) { - // try 2-words tests - $name = $name.' '.$parser->getCurrentToken()->getValue(); - - if ($test = $env->getTest($name)) { - $parser->getStream()->next(); - - return array($name, $test); - } - } - - $e = new Twig_Error_Syntax(sprintf('Unknown "%s" test.', $name), $line, $stream->getSourceContext()->getName()); - $e->addSuggestions($name, array_keys($env->getTests())); - - throw $e; - } - - protected function getTestNodeClass(Twig_Parser $parser, $test) - { - if ($test instanceof Twig_SimpleTest) { - return $test->getNodeClass(); - } - - return $test instanceof Twig_Test_Node ? $test->getClass() : 'Twig_Node_Expression_Test'; - } - public function getName() { return 'core'; From 4deb03aeb142399b1fc41e9433fb326915a9be2b Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 24 Oct 2016 16:11:10 -0700 Subject: [PATCH 2/2] deprecated Twig_Parser::getEnvironment() --- CHANGELOG | 1 + doc/deprecated.rst | 2 ++ lib/Twig/ExpressionParser.php | 39 +++++++++++++++++++++-------------- lib/Twig/Parser.php | 7 ++++++- 4 files changed, 33 insertions(+), 16 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index f0d04dfe5..d027ad590 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,6 @@ * 1.27.0 (2016-XX-XX) + * deprecated Twig_Parser::getEnvironment() * deprecated Twig_Parser::addHandler() and Twig_Parser::addNodeVisitor() * deprecated Twig_Compiler::addIndentation() * fixed regression when registering two extensions having the same class name diff --git a/doc/deprecated.rst b/doc/deprecated.rst index 377c52543..3ebe3a229 100644 --- a/doc/deprecated.rst +++ b/doc/deprecated.rst @@ -23,6 +23,8 @@ Token Parsers * As of Twig 1.27, ``Twig_Parser::getFilename()`` is deprecated. From a token parser, use ``$this->parser->getStream()->getSourceContext()->getPath()`` instead. +* As of Twig 1.27, ``Twig_Parser::getEnvironment()`` is deprecated. + Extensions ---------- diff --git a/lib/Twig/ExpressionParser.php b/lib/Twig/ExpressionParser.php index 577617d53..54404b1a3 100644 --- a/lib/Twig/ExpressionParser.php +++ b/lib/Twig/ExpressionParser.php @@ -19,6 +19,8 @@ * @see http://en.wikipedia.org/wiki/Operator-precedence_parser * * @author Fabien Potencier + * + * @internal */ class Twig_ExpressionParser { @@ -29,11 +31,23 @@ class Twig_ExpressionParser protected $unaryOperators; protected $binaryOperators; - public function __construct(Twig_Parser $parser, array $unaryOperators, array $binaryOperators) + private $env; + + public function __construct(Twig_Parser $parser, Twig_Environment $env = null) { $this->parser = $parser; - $this->unaryOperators = $unaryOperators; - $this->binaryOperators = $binaryOperators; + + if ($env instanceof Twig_Environment) { + $this->env = $env; + $this->unaryOperators = $env->getUnaryOperators(); + $this->binaryOperators = $env->getBinaryOperators(); + } else { + @trigger_error('Passing the operators as constructor arguments to '.__METHOD__.' is deprecated since version 1.27. Pass the environment instead.', E_USER_DEPRECATED); + + $this->env = $parser->getEnvironment(); + $this->unaryOperators = func_get_arg(1); + $this->binaryOperators = func_get_arg(2); + } } public function parseExpression($precedence = 0) @@ -594,9 +608,8 @@ class Twig_ExpressionParser { $stream = $this->parser->getStream(); $name = $stream->expect(Twig_Token::NAME_TYPE)->getValue(); - $env = $this->parser->getEnvironment(); - if ($test = $env->getTest($name)) { + if ($test = $this->env->getTest($name)) { return array($name, $test); } @@ -604,7 +617,7 @@ class Twig_ExpressionParser // try 2-words tests $name = $name.' '.$this->parser->getCurrentToken()->getValue(); - if ($test = $env->getTest($name)) { + if ($test = $this->env->getTest($name)) { $stream->next(); return array($name, $test); @@ -612,7 +625,7 @@ class Twig_ExpressionParser } $e = new Twig_Error_Syntax(sprintf('Unknown "%s" test.', $name), $line, $stream->getSourceContext()->getName()); - $e->addSuggestions($name, array_keys($env->getTests())); + $e->addSuggestions($name, array_keys($this->env->getTests())); throw $e; } @@ -641,11 +654,9 @@ class Twig_ExpressionParser protected function getFunctionNodeClass($name, $line) { - $env = $this->parser->getEnvironment(); - - if (false === $function = $env->getFunction($name)) { + if (false === $function = $this->env->getFunction($name)) { $e = new Twig_Error_Syntax(sprintf('Unknown "%s" function.', $name), $line, $this->parser->getStream()->getSourceContext()->getName()); - $e->addSuggestions($name, array_keys($env->getFunctions())); + $e->addSuggestions($name, array_keys($this->env->getFunctions())); throw $e; } @@ -672,11 +683,9 @@ class Twig_ExpressionParser protected function getFilterNodeClass($name, $line) { - $env = $this->parser->getEnvironment(); - - if (false === $filter = $env->getFilter($name)) { + if (false === $filter = $this->env->getFilter($name)) { $e = new Twig_Error_Syntax(sprintf('Unknown "%s" filter.', $name), $line, $this->parser->getStream()->getSourceContext()->getName()); - $e->addSuggestions($name, array_keys($env->getFilters())); + $e->addSuggestions($name, array_keys($this->env->getFilters())); throw $e; } diff --git a/lib/Twig/Parser.php b/lib/Twig/Parser.php index cb87ae333..a1d6f099a 100644 --- a/lib/Twig/Parser.php +++ b/lib/Twig/Parser.php @@ -42,8 +42,13 @@ class Twig_Parser implements Twig_ParserInterface $this->env = $env; } + /** + * @deprecated since 1.27 (to be removed in 2.0) + */ public function getEnvironment() { + @trigger_error('The '.__METHOD__.' method is deprecated since version 1.27 and will be removed in 2.0.', E_USER_DEPRECATED); + return $this->env; } @@ -90,7 +95,7 @@ class Twig_Parser implements Twig_ParserInterface } if (null === $this->expressionParser) { - $this->expressionParser = new Twig_ExpressionParser($this, $this->env->getUnaryOperators(), $this->env->getBinaryOperators()); + $this->expressionParser = new Twig_ExpressionParser($this, $this->env); } $this->stream = $stream;