mirror of
https://github.com/twigphp/Twig.git
synced 2026-09-15 11:56:50 +00:00
optimized variable access when possible
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
<?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
|
||||
{
|
||||
}
|
||||
@@ -11,42 +11,29 @@
|
||||
*/
|
||||
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, ')
|
||||
@@ -55,4 +42,14 @@ class Twig_Node_Expression_Name extends Twig_Node_Expression
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
public function isSpecial()
|
||||
{
|
||||
return isset($this->specialVars[$this->getAttribute('name')]);
|
||||
}
|
||||
|
||||
public function isSimple()
|
||||
{
|
||||
return !$this->isSpecial() && !$this->hasAttribute('is_defined_test');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
<?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('_');
|
||||
}
|
||||
}
|
||||
@@ -257,8 +257,14 @@ class Twig_Node_Module extends Twig_Node
|
||||
// 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) {
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
<?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")
|
||||
;
|
||||
}
|
||||
}
|
||||
@@ -26,9 +26,12 @@ class Twig_NodeVisitor_Optimizer implements Twig_NodeVisitorInterface
|
||||
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.
|
||||
@@ -53,6 +56,20 @@ class Twig_NodeVisitor_Optimizer implements Twig_NodeVisitorInterface
|
||||
$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;
|
||||
}
|
||||
|
||||
@@ -61,6 +78,8 @@ class Twig_NodeVisitor_Optimizer implements Twig_NodeVisitorInterface
|
||||
*/
|
||||
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);
|
||||
}
|
||||
@@ -71,6 +90,28 @@ class Twig_NodeVisitor_Optimizer implements Twig_NodeVisitorInterface
|
||||
|
||||
$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;
|
||||
}
|
||||
|
||||
@@ -80,7 +121,6 @@ class Twig_NodeVisitor_Optimizer implements Twig_NodeVisitorInterface
|
||||
* 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
|
||||
@@ -93,8 +133,7 @@ class Twig_NodeVisitor_Optimizer implements Twig_NodeVisitorInterface
|
||||
|
||||
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);
|
||||
|
||||
|
||||
+2
-2
@@ -93,7 +93,7 @@ class Twig_Parser implements Twig_ParserInterface
|
||||
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);
|
||||
|
||||
@@ -204,7 +204,7 @@ class Twig_Parser implements Twig_ParserInterface
|
||||
|
||||
public function setBlock($name, $value)
|
||||
{
|
||||
$this->blocks[$name] = $value;
|
||||
$this->blocks[$name] = new Twig_Node_Body(array($value));
|
||||
}
|
||||
|
||||
public function hasMacro($name)
|
||||
|
||||
@@ -47,7 +47,7 @@ class Twig_TokenParser_Macro extends Twig_TokenParser
|
||||
$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;
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ class Twig_Tests_ExpressionParserTest extends PHPUnit_Framework_TestCase
|
||||
$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'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -17,7 +17,7 @@ class Twig_Tests_NodeVisitor_OptimizerTest extends PHPUnit_Framework_TestCase
|
||||
|
||||
$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'));
|
||||
@@ -30,7 +30,7 @@ class Twig_Tests_NodeVisitor_OptimizerTest extends PHPUnit_Framework_TestCase
|
||||
|
||||
$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'));
|
||||
@@ -42,24 +42,12 @@ class Twig_Tests_NodeVisitor_OptimizerTest extends PHPUnit_Framework_TestCase
|
||||
$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
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user