From b247452ec575899c7bc7f19ab8e648276d953119 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Thu, 23 Dec 2010 22:10:18 +0100 Subject: [PATCH] Resolve imported functions at compile time {% from %} is compiled as {% import %}, the imported module is placed in a local variable, and calls to imported functions are compiled as calls to module functions. --- lib/Twig/ExpressionParser.php | 6 +++++- lib/Twig/Node/From.php | 28 ++-------------------------- lib/Twig/Node/Import.php | 2 +- lib/Twig/Parser.php | 28 ++++++++++++++++++++++++++++ lib/Twig/TokenParser/Block.php | 2 ++ lib/Twig/TokenParser/From.php | 9 ++++++++- lib/Twig/TokenParser/Macro.php | 2 ++ 7 files changed, 48 insertions(+), 29 deletions(-) diff --git a/lib/Twig/ExpressionParser.php b/lib/Twig/ExpressionParser.php index 01a6e70..c988722 100644 --- a/lib/Twig/ExpressionParser.php +++ b/lib/Twig/ExpressionParser.php @@ -213,7 +213,11 @@ class Twig_ExpressionParser } elseif ('|' == $token->getValue()) { $node = $this->parseFilterExpression($node); } elseif ($firstPass && $node instanceof Twig_Node_Expression_Name && '(' == $token->getValue()) { - $node = new Twig_Node_Expression_Function($node, $this->parseArguments(), $node->getLine()); + if (null !== $alias = $this->parser->getImportedFunction($node->getAttribute('name'))) { + $node = new Twig_Node_Expression_GetAttr($alias['node'], new Twig_Node_Expression_Constant($alias['name'], $node->getLine()), $this->parseArguments(), $node->getLine(), Twig_Node_Expression_GetAttr::TYPE_METHOD); + } else { + $node = new Twig_Node_Expression_Function($node, $this->parseArguments(), $node->getLine()); + } } else { break; } diff --git a/lib/Twig/Node/From.php b/lib/Twig/Node/From.php index f75392a..0af0fea 100644 --- a/lib/Twig/Node/From.php +++ b/lib/Twig/Node/From.php @@ -17,32 +17,8 @@ */ class Twig_Node_From extends Twig_Node_Import { - public function __construct(Twig_Node_Expression $expr, array $imports, $lineno, $tag = null) + public function __construct(Twig_Node_Expression $expr, $lineno, $tag = null) { - parent::__construct($expr, new Twig_Node_Expression_AssignName('_imported_'.rand(10000, 99999), $lineno), $lineno, $tag); - - $this->setAttribute('imports', $imports); - } - - /** - * Compiles the node to PHP. - * - * @param Twig_Compiler A Twig_Compiler instance - */ - public function compile($compiler) - { - parent::compile($compiler); - - foreach ($this->getAttribute('imports') as $name => $alias) { - $compiler - ->write('$context[') - ->repr('fn_'.$alias) - ->raw('] = new Twig_Function(') - ->subcompile($this->getNode('var')) - ->raw(', ') - ->repr($name) - ->raw(");\n") - ; - } + parent::__construct($expr, new Twig_Node_Expression_AssignLocalName(null, $lineno), $lineno, $tag); } } diff --git a/lib/Twig/Node/Import.php b/lib/Twig/Node/Import.php index f586371..1314b3f 100644 --- a/lib/Twig/Node/Import.php +++ b/lib/Twig/Node/Import.php @@ -17,7 +17,7 @@ */ class Twig_Node_Import extends Twig_Node { - public function __construct(Twig_Node_Expression $expr, Twig_Node_Expression_AssignName $var, $lineno, $tag = null) + public function __construct(Twig_Node_Expression $expr, Twig_Node_Expression $var, $lineno, $tag = null) { parent::__construct(array('expr' => $expr, 'var' => $var), array(), $lineno, $tag); } diff --git a/lib/Twig/Parser.php b/lib/Twig/Parser.php index 2696754..0d13798 100644 --- a/lib/Twig/Parser.php +++ b/lib/Twig/Parser.php @@ -21,6 +21,7 @@ class Twig_Parser implements Twig_ParserInterface protected $macros; protected $env; protected $reservedMacroNames; + protected $importedFunctions; public function __construct(Twig_Environment $env) { @@ -52,6 +53,7 @@ class Twig_Parser implements Twig_ParserInterface $this->blocks = array(); $this->macros = array(); $this->blockStack = array(); + $this->importedFunctions = array(array()); try { $body = $this->subparse(null); @@ -191,6 +193,32 @@ class Twig_Parser implements Twig_ParserInterface $this->macros[$name] = $node; } + public function addImportedFunction($alias, $name, Twig_Node_Expression $node) + { + $this->importedFunctions[0][$alias] = array( + 'name' => $name, + 'node' => $node, + ); + } + + public function getImportedFunction($alias) + { + if (!isset($this->importedFunctions[0][$alias])) { + return null; + } + return $this->importedFunctions[0][$alias]; + } + + public function pushLocalScope() + { + array_unshift($this->importedFunctions, array()); + } + + public function popLocalScope() + { + array_shift($this->importedFunctions); + } + public function getExpressionParser() { return $this->expressionParser; diff --git a/lib/Twig/TokenParser/Block.php b/lib/Twig/TokenParser/Block.php index 37d6457..5ff104d 100644 --- a/lib/Twig/TokenParser/Block.php +++ b/lib/Twig/TokenParser/Block.php @@ -26,6 +26,7 @@ class Twig_TokenParser_Block extends Twig_TokenParser if ($this->parser->hasBlock($name)) { throw new Twig_Error_Syntax("The block '$name' has already been defined", $lineno); } + $this->parser->pushLocalScope(); $this->parser->pushBlockStack($name); if ($stream->test(Twig_Token::BLOCK_END_TYPE)) { @@ -49,6 +50,7 @@ class Twig_TokenParser_Block extends Twig_TokenParser $block = new Twig_Node_Block($name, $body, $lineno); $this->parser->setBlock($name, $block); $this->parser->popBlockStack(); + $this->parser->popLocalScope(); return new Twig_Node_BlockReference($name, $lineno, $this->getTag()); } diff --git a/lib/Twig/TokenParser/From.php b/lib/Twig/TokenParser/From.php index 9398f1d..f71027a 100644 --- a/lib/Twig/TokenParser/From.php +++ b/lib/Twig/TokenParser/From.php @@ -45,7 +45,14 @@ class Twig_TokenParser_From extends Twig_TokenParser $stream->expect(Twig_Token::BLOCK_END_TYPE); - return new Twig_Node_From($macro, $targets, $token->getLine(), $this->getTag()); + $node = new Twig_Node_From($macro, $token->getLine(), $this->getTag()); + + foreach($targets as $name => $alias) + { + $this->parser->addImportedFunction($alias, $name, $node->getNode('var')); + } + + return $node; } /** diff --git a/lib/Twig/TokenParser/Macro.php b/lib/Twig/TokenParser/Macro.php index 20186c6..a6e4733 100644 --- a/lib/Twig/TokenParser/Macro.php +++ b/lib/Twig/TokenParser/Macro.php @@ -25,7 +25,9 @@ class Twig_TokenParser_Macro extends Twig_TokenParser $arguments = $this->parser->getExpressionParser()->parseArguments(); $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); + $this->parser->pushLocalScope(); $body = $this->parser->subparse(array($this, 'decideBlockEnd'), true); + $this->parser->popLocalScope(); $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); $this->parser->setMacro($name, new Twig_Node_Macro($name, $body, $arguments, $lineno, $this->getTag())); -- 1.7.2.5