--- /dev/null
+<?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 body node.
+ *
+ * @package twig
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class Twig_Node_Body extends Twig_Node
+{
+}
*/
class Twig_Node_Expression_Name extends Twig_Node_Expression
{
+ protected $specialVars = array(
+ '_self' => '$this',
+ '_context' => '$context',
+ '_charset' => '$this->env->getCharset()',
+ );
+
public function __construct($name, $lineno)
{
- parent::__construct(array(), array('name' => $name, 'output' => false), $lineno);
+ parent::__construct(array(), array('name' => $name), $lineno);
}
public function compile(Twig_Compiler $compiler)
{
- static $specialVars = array(
- '_self' => '$this',
- '_context' => '$context',
- '_charset' => '$this->env->getCharset()',
- );
-
$name = $this->getAttribute('name');
if ($this->hasAttribute('is_defined_test')) {
- if (isset($specialVars[$name])) {
+ if ($this->isSpecial()) {
$compiler->repr(true);
} else {
$compiler->raw('array_key_exists(')->repr($name)->raw(', $context)');
}
- } elseif (isset($specialVars[$name])) {
- $compiler->raw($specialVars[$name]);
- } elseif ($this->getAttribute('output')) {
- $compiler
- ->addDebugInfo($this)
- ->write('if (isset($context[')
- ->string($name)
- ->raw("])) {\n")
- ->indent()
- ->write('echo $context[')
- ->string($name)
- ->raw("];\n")
- ->outdent()
- ->write("}\n")
- ;
+ } elseif ($this->isSpecial()) {
+ $compiler->raw($this->specialVars[$name]);
} else {
$compiler
->raw('$this->getContext($context, ')
;
}
}
+
+ public function isSpecial()
+ {
+ return isset($this->specialVars[$this->getAttribute('name')]);
+ }
+
+ public function isSimple()
+ {
+ return !$this->isSpecial() && !$this->hasAttribute('is_defined_test');
+ }
}
--- /dev/null
+<?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_TempName extends Twig_Node_Expression
+{
+ public function __construct($name, $lineno)
+ {
+ parent::__construct(array(), array('name' => $name), $lineno);
+ }
+
+ public function compile(Twig_Compiler $compiler)
+ {
+ $compiler->raw('$_')->raw($this->getAttribute('name'))->raw('_');
+ }
+}
// only contains blocks and use statements.
$traitable = null === $this->getNode('parent') && 0 === count($this->getNode('macros'));
if ($traitable) {
- if (!count($nodes = $this->getNode('body'))) {
- $nodes = new Twig_Node(array($this->getNode('body')));
+ if ($this->getNode('body') instanceof Twig_Node_Body) {
+ $nodes = $this->getNode('body')->getNode(0);
+ } else {
+ $nodes = $this->getNode('body');
+ }
+
+ if (!count($nodes)) {
+ $nodes = new Twig_Node(array($nodes));
}
foreach ($nodes as $node) {
--- /dev/null
+<?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_SetTemp extends Twig_Node
+{
+ public function __construct($name, $lineno)
+ {
+ parent::__construct(array(), array('name' => $name), $lineno);
+ }
+
+ public function compile(Twig_Compiler $compiler)
+ {
+ $name = $this->getAttribute('name');
+ $compiler
+ ->addDebugInfo($this)
+ ->write('if (isset($context[')
+ ->string($name)
+ ->raw('])) { $_')
+ ->raw($name)
+ ->raw('_ = $context[')
+ ->repr($name)
+ ->raw(']; } else { $_')
+ ->raw($name)
+ ->raw("_ = null; }\n")
+ ;
+ }
+}
const OPTIMIZE_NONE = 0;
const OPTIMIZE_FOR = 2;
const OPTIMIZE_RAW_FILTER = 4;
+ const OPTIMIZE_VAR_ACCESS = 8;
protected $loops = array();
protected $optimizers;
+ protected $prependedNodes = array();
+ protected $inABody = false;
/**
* Constructor.
$this->enterOptimizeFor($node, $env);
}
+ if (self::OPTIMIZE_VAR_ACCESS === (self::OPTIMIZE_VAR_ACCESS & $this->optimizers) && !$env->isStrictVariables() && !$env->hasExtension('sandbox')) {
+ if ($this->inABody) {
+ if (!$node instanceof Twig_Node_Expression) {
+ if (get_class($node) !== 'Twig_Node') {
+ array_unshift($this->prependedNodes, array());
+ }
+ } else {
+ $node = $this->optimizeVariables($node, $env);
+ }
+ } elseif ($node instanceof Twig_Node_Body) {
+ $this->inABody = true;
+ }
+ }
+
return $node;
}
*/
public function leaveNode(Twig_NodeInterface $node, Twig_Environment $env)
{
+ $expression = $node instanceof Twig_Node_Expression;
+
if (self::OPTIMIZE_FOR === (self::OPTIMIZE_FOR & $this->optimizers)) {
$this->leaveOptimizeFor($node, $env);
}
$node = $this->optimizePrintNode($node, $env);
+ if (self::OPTIMIZE_VAR_ACCESS === (self::OPTIMIZE_VAR_ACCESS & $this->optimizers) && !$env->isStrictVariables() && !$env->hasExtension('sandbox')) {
+ if ($node instanceof Twig_Node_Body) {
+ $this->inABody = false;
+ } elseif ($this->inABody) {
+ if (!$expression && get_class($node) !== 'Twig_Node' && $prependedNodes = array_shift($this->prependedNodes)) {
+ $prependedNodes[] = $node;
+ $node = new Twig_Node($prependedNodes);
+ }
+ }
+ }
+
+ return $node;
+ }
+
+ protected function optimizeVariables($node, $env)
+ {
+ if ('Twig_Node_Expression_Name' === get_class($node) && $node->isSimple()) {
+ $this->prependedNodes[0][] = new Twig_Node_SetTemp($node->getAttribute('name'), $node->getLine());
+
+ return new Twig_Node_Expression_TempName($node->getAttribute('name'), $node->getLine());
+ }
+
return $node;
}
* It replaces:
*
* * "echo $this->render(Parent)Block()" with "$this->display(Parent)Block()"
- * * "echo $this->getContext('...')" with "if (isset('...')) { echo '...' }"
*
* @param Twig_NodeInterface $node A Node
* @param Twig_Environment $env The current Twig environment
if (
$node->getNode('expr') instanceof Twig_Node_Expression_BlockReference ||
- $node->getNode('expr') instanceof Twig_Node_Expression_Parent ||
- ($node->getNode('expr') instanceof Twig_Node_Expression_Name && !$env->hasExtension('sandbox') && !$env->isStrictVariables())
+ $node->getNode('expr') instanceof Twig_Node_Expression_Parent
) {
$node->getNode('expr')->setAttribute('output', true);
throw $e;
}
- $node = new Twig_Node_Module($body, $this->parent, new Twig_Node($this->blocks), new Twig_Node($this->macros), new Twig_Node($this->traits), $this->stream->getFilename());
+ $node = new Twig_Node_Module(new Twig_Node_Body(array($body)), $this->parent, new Twig_Node($this->blocks), new Twig_Node($this->macros), new Twig_Node($this->traits), $this->stream->getFilename());
$traverser = new Twig_NodeTraverser($this->env, $this->visitors);
public function setBlock($name, $value)
{
- $this->blocks[$name] = $value;
+ $this->blocks[$name] = new Twig_Node_Body(array($value));
}
public function hasMacro($name)
$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()));
+ $this->parser->setMacro($name, new Twig_Node_Macro($name, new Twig_Node_Body(array($body)), $arguments, $lineno, $this->getTag()));
return null;
}
$stream = $env->tokenize($template, 'index');
$parser = new Twig_Parser($env);
- $this->assertEquals($expected, $parser->parse($stream)->getNode('body')->getNode('expr'));
+ $this->assertEquals($expected, $parser->parse($stream)->getNode('body')->getNode(0)->getNode('expr'));
}
/**
$stream = $env->parse($env->tokenize('{{ block("foo") }}', 'index'));
- $node = $stream->getNode('body');
+ $node = $stream->getNode('body')->getNode(0);
$this->assertInstanceOf('Twig_Node_Expression_BlockReference', $node);
$this->assertTrue($node->getAttribute('output'));
$stream = $env->parse($env->tokenize('{% extends "foo" %}{% block content %}{{ parent() }}{% endblock %}', 'index'));
- $node = $stream->getNode('blocks')->getNode('content')->getNode('body');
+ $node = $stream->getNode('blocks')->getNode('content')->getNode(0)->getNode('body');
$this->assertInstanceOf('Twig_Node_Expression_Parent', $node);
$this->assertTrue($node->getAttribute('output'));
$env->addExtension(new Twig_Extension_Optimizer());
$stream = $env->parse($env->tokenize('{{ block(name|lower) }}', 'index'));
- $node = $stream->getNode('body');
+ $node = $stream->getNode('body')->getNode(0)->getNode(1);
$this->assertInstanceOf('Twig_Node_Expression_BlockReference', $node);
$this->assertTrue($node->getAttribute('output'));
}
- public function testRenderNameOptimizer()
- {
- $env = new Twig_Environment(new Twig_Loader_String(), array('cache' => false, 'autoescape' => false));
- $env->addExtension(new Twig_Extension_Optimizer());
- $stream = $env->parse($env->tokenize('{{ name }}', 'index'));
-
- $node = $stream->getNode('body');
-
- $this->assertInstanceOf('Twig_Node_Expression_Name', $node);
- $this->assertTrue($node->getAttribute('output'));
- }
-
/**
* @dataProvider getTestsForForOptimizer
*/