optimized variable access when using PHP 5.4

This commit is contained in:
Fabien Potencier
2011-11-18 21:54:44 +01:00
parent 8ac3603a8b
commit 1b32617b97
10 changed files with 56 additions and 30 deletions
+1
View File
@@ -1,5 +1,6 @@
* 1.4.0 * 1.4.0
* optimized variable access when using PHP 5.4
* changed the precedence of the .. operator to be more consistent with languages that implements such a feature like Ruby * changed the precedence of the .. operator to be more consistent with languages that implements such a feature like Ruby
* added an Exception to Twig_Loader_Array::isFresh() method when the template does not exist to be consistent with other loaders * added an Exception to Twig_Loader_Array::isFresh() method when the template does not exist to be consistent with other loaders
* added Twig_Function_Node to allow more complex functions to have their own Node class * added Twig_Function_Node to allow more complex functions to have their own Node class
+21 -10
View File
@@ -35,18 +35,29 @@ class Twig_Node_Expression_Name extends Twig_Node_Expression
} elseif ($this->isSpecial()) { } elseif ($this->isSpecial()) {
$compiler->raw($this->specialVars[$name]); $compiler->raw($this->specialVars[$name]);
} else { } else {
$compiler if (version_compare(phpversion(), '5.4.0RC1', '>=') && ($this->getAttribute('ignore_strict_check') || !$compiler->getEnvironment()->isStrictVariables())) {
->raw('$this->getContext($context, ') // PHP 5.4 ternary operator performance was optimized
->string($name) $compiler
; ->raw('(isset($context[')
->string($name)
->raw(']) ? $context[')
->string($name)
->raw('] : null)')
;
} else {
$compiler
->raw('$this->getContext($context, ')
->string($name)
;
if ($this->getAttribute('ignore_strict_check')) { if ($this->getAttribute('ignore_strict_check')) {
$compiler->raw(', true'); $compiler->raw(', true');
}
$compiler
->raw(')')
;
} }
$compiler
->raw(')')
;
} }
} }
+1 -1
View File
@@ -56,7 +56,7 @@ class Twig_NodeVisitor_Optimizer implements Twig_NodeVisitorInterface
$this->enterOptimizeFor($node, $env); $this->enterOptimizeFor($node, $env);
} }
if (self::OPTIMIZE_VAR_ACCESS === (self::OPTIMIZE_VAR_ACCESS & $this->optimizers) && !$env->isStrictVariables() && !$env->hasExtension('sandbox')) { if (!version_compare(phpversion(), '5.4.0RC1', '>=') && self::OPTIMIZE_VAR_ACCESS === (self::OPTIMIZE_VAR_ACCESS & $this->optimizers) && !$env->isStrictVariables() && !$env->hasExtension('sandbox')) {
if ($this->inABody) { if ($this->inABody) {
if (!$node instanceof Twig_Node_Expression) { if (!$node instanceof Twig_Node_Expression) {
if (get_class($node) !== 'Twig_Node') { if (get_class($node) !== 'Twig_Node') {
@@ -49,10 +49,10 @@ class Twig_Tests_Node_Expression_GetAttrTest extends Twig_Tests_Node_TestCase
$attr = new Twig_Node_Expression_Constant('bar', 0); $attr = new Twig_Node_Expression_Constant('bar', 0);
$args = new Twig_Node(); $args = new Twig_Node();
$node = new Twig_Node_Expression_GetAttr($expr, $attr, $args, Twig_TemplateInterface::ANY_CALL, 0); $node = new Twig_Node_Expression_GetAttr($expr, $attr, $args, Twig_TemplateInterface::ANY_CALL, 0);
$tests[] = array($node, '$this->getAttribute($this->getContext($context, "foo"), "bar")'); $tests[] = array($node, sprintf('$this->getAttribute(%s, "bar")', $this->getVariableGetter('foo')));
$node = new Twig_Node_Expression_GetAttr($expr, $attr, $args, Twig_TemplateInterface::ARRAY_CALL, 0); $node = new Twig_Node_Expression_GetAttr($expr, $attr, $args, Twig_TemplateInterface::ARRAY_CALL, 0);
$tests[] = array($node, '$this->getAttribute($this->getContext($context, "foo"), "bar", array(), "array")'); $tests[] = array($node, sprintf('$this->getAttribute(%s, "bar", array(), "array")', $this->getVariableGetter('foo')));
$args = new Twig_Node(array( $args = new Twig_Node(array(
@@ -60,7 +60,7 @@ class Twig_Tests_Node_Expression_GetAttrTest extends Twig_Tests_Node_TestCase
new Twig_Node_Expression_Constant('bar', 0), new Twig_Node_Expression_Constant('bar', 0),
)); ));
$node = new Twig_Node_Expression_GetAttr($expr, $attr, $args, Twig_TemplateInterface::METHOD_CALL, 0); $node = new Twig_Node_Expression_GetAttr($expr, $attr, $args, Twig_TemplateInterface::METHOD_CALL, 0);
$tests[] = array($node, '$this->getAttribute($this->getContext($context, "foo"), "bar", array($this->getContext($context, "foo"), "bar", ), "method")'); $tests[] = array($node, sprintf('$this->getAttribute(%s, "bar", array(%s, "bar", ), "method")', $this->getVariableGetter('foo'), $this->getVariableGetter('foo')));
return $tests; return $tests;
} }
+2 -1
View File
@@ -39,10 +39,11 @@ class Twig_Tests_Node_Expression_NameTest extends Twig_Tests_Node_TestCase
$context = new Twig_Node_Expression_Name('_context', 0); $context = new Twig_Node_Expression_Name('_context', 0);
$env = new Twig_Environment(null, array('strict_variables' => true)); $env = new Twig_Environment(null, array('strict_variables' => true));
$env1 = new Twig_Environment(null, array('strict_variables' => false));
return array( return array(
array($node, '$this->getContext($context, "foo")', $env), array($node, '$this->getContext($context, "foo")', $env),
array($node, '$this->getContext($context, "foo")'), array($node, $this->getVariableGetter('foo'), $env1),
array($self, '$this'), array($self, '$this'),
array($context, '$context'), array($context, '$context'),
); );
+9 -9
View File
@@ -65,9 +65,9 @@ class Twig_Tests_Node_ForTest extends Twig_Tests_Node_TestCase
$tests[] = array($node, <<<EOF $tests[] = array($node, <<<EOF
\$context['_parent'] = (array) \$context; \$context['_parent'] = (array) \$context;
\$context['_seq'] = twig_ensure_traversable(\$this->getContext(\$context, "items")); \$context['_seq'] = twig_ensure_traversable({$this->getVariableGetter('items')});
foreach (\$context['_seq'] as \$context["key"] => \$context["item"]) { foreach (\$context['_seq'] as \$context["key"] => \$context["item"]) {
echo \$this->getContext(\$context, "foo"); echo {$this->getVariableGetter('foo')};
} }
\$_parent = \$context['_parent']; \$_parent = \$context['_parent'];
unset(\$context['_seq'], \$context['_iterated'], \$context['key'], \$context['item'], \$context['_parent'], \$context['loop']); unset(\$context['_seq'], \$context['_iterated'], \$context['key'], \$context['item'], \$context['_parent'], \$context['loop']);
@@ -86,7 +86,7 @@ EOF
$tests[] = array($node, <<<EOF $tests[] = array($node, <<<EOF
\$context['_parent'] = (array) \$context; \$context['_parent'] = (array) \$context;
\$context['_seq'] = twig_ensure_traversable(\$this->getContext(\$context, "values")); \$context['_seq'] = twig_ensure_traversable({$this->getVariableGetter('values')});
\$context['loop'] = array( \$context['loop'] = array(
'parent' => \$context['_parent'], 'parent' => \$context['_parent'],
'index0' => 0, 'index0' => 0,
@@ -101,7 +101,7 @@ if (is_array(\$context['_seq']) || (is_object(\$context['_seq']) && \$context['_
\$context['loop']['last'] = 1 === \$length; \$context['loop']['last'] = 1 === \$length;
} }
foreach (\$context['_seq'] as \$context["k"] => \$context["v"]) { foreach (\$context['_seq'] as \$context["k"] => \$context["v"]) {
echo \$this->getContext(\$context, "foo"); echo {$this->getVariableGetter('foo')};
++\$context['loop']['index0']; ++\$context['loop']['index0'];
++\$context['loop']['index']; ++\$context['loop']['index'];
\$context['loop']['first'] = false; \$context['loop']['first'] = false;
@@ -128,7 +128,7 @@ EOF
$tests[] = array($node, <<<EOF $tests[] = array($node, <<<EOF
\$context['_parent'] = (array) \$context; \$context['_parent'] = (array) \$context;
\$context['_seq'] = twig_ensure_traversable(\$this->getContext(\$context, "values")); \$context['_seq'] = twig_ensure_traversable({$this->getVariableGetter('values')});
\$context['loop'] = array( \$context['loop'] = array(
'parent' => \$context['_parent'], 'parent' => \$context['_parent'],
'index0' => 0, 'index0' => 0,
@@ -137,7 +137,7 @@ EOF
); );
foreach (\$context['_seq'] as \$context["k"] => \$context["v"]) { foreach (\$context['_seq'] as \$context["k"] => \$context["v"]) {
if (true) { if (true) {
echo \$this->getContext(\$context, "foo"); echo {$this->getVariableGetter('foo')};
++\$context['loop']['index0']; ++\$context['loop']['index0'];
++\$context['loop']['index']; ++\$context['loop']['index'];
\$context['loop']['first'] = false; \$context['loop']['first'] = false;
@@ -160,7 +160,7 @@ EOF
$tests[] = array($node, <<<EOF $tests[] = array($node, <<<EOF
\$context['_parent'] = (array) \$context; \$context['_parent'] = (array) \$context;
\$context['_seq'] = twig_ensure_traversable(\$this->getContext(\$context, "values")); \$context['_seq'] = twig_ensure_traversable({$this->getVariableGetter('values')});
\$context['_iterated'] = false; \$context['_iterated'] = false;
\$context['loop'] = array( \$context['loop'] = array(
'parent' => \$context['_parent'], 'parent' => \$context['_parent'],
@@ -176,7 +176,7 @@ if (is_array(\$context['_seq']) || (is_object(\$context['_seq']) && \$context['_
\$context['loop']['last'] = 1 === \$length; \$context['loop']['last'] = 1 === \$length;
} }
foreach (\$context['_seq'] as \$context["k"] => \$context["v"]) { foreach (\$context['_seq'] as \$context["k"] => \$context["v"]) {
echo \$this->getContext(\$context, "foo"); echo {$this->getVariableGetter('foo')};
\$context['_iterated'] = true; \$context['_iterated'] = true;
++\$context['loop']['index0']; ++\$context['loop']['index0'];
++\$context['loop']['index']; ++\$context['loop']['index'];
@@ -188,7 +188,7 @@ foreach (\$context['_seq'] as \$context["k"] => \$context["v"]) {
} }
} }
if (!\$context['_iterated']) { if (!\$context['_iterated']) {
echo \$this->getContext(\$context, "foo"); echo {$this->getVariableGetter('foo')};
} }
\$_parent = \$context['_parent']; \$_parent = \$context['_parent'];
unset(\$context['_seq'], \$context['_iterated'], \$context['k'], \$context['v'], \$context['_parent'], \$context['loop']); unset(\$context['_seq'], \$context['_iterated'], \$context['k'], \$context['v'], \$context['_parent'], \$context['loop']);
+5 -5
View File
@@ -55,7 +55,7 @@ class Twig_Tests_Node_IfTest extends Twig_Tests_Node_TestCase
$tests[] = array($node, <<<EOF $tests[] = array($node, <<<EOF
if (true) { if (true) {
echo \$this->getContext(\$context, "foo"); echo {$this->getVariableGetter('foo')};
} }
EOF EOF
); );
@@ -71,9 +71,9 @@ EOF
$tests[] = array($node, <<<EOF $tests[] = array($node, <<<EOF
if (true) { if (true) {
echo \$this->getContext(\$context, "foo"); echo {$this->getVariableGetter('foo')};
} elseif (false) { } elseif (false) {
echo \$this->getContext(\$context, "bar"); echo {$this->getVariableGetter('bar')};
} }
EOF EOF
); );
@@ -87,9 +87,9 @@ EOF
$tests[] = array($node, <<<EOF $tests[] = array($node, <<<EOF
if (true) { if (true) {
echo \$this->getContext(\$context, "foo"); echo {$this->getVariableGetter('foo')};
} else { } else {
echo \$this->getContext(\$context, "bar"); echo {$this->getVariableGetter('bar')};
} }
EOF EOF
); );
+1 -1
View File
@@ -64,7 +64,7 @@ EOF
$values = new Twig_Node(array(new Twig_Node_Expression_Constant('foo', 0), new Twig_Node_Expression_Name('bar', 0)), array(), 0); $values = new Twig_Node(array(new Twig_Node_Expression_Constant('foo', 0), new Twig_Node_Expression_Name('bar', 0)), array(), 0);
$node = new Twig_Node_Set(false, $names, $values, 0); $node = new Twig_Node_Set(false, $names, $values, 0);
$tests[] = array($node, <<<EOF $tests[] = array($node, <<<EOF
list(\$context["foo"], \$context["bar"]) = array("foo", \$this->getContext(\$context, "bar")); list(\$context["foo"], \$context["bar"]) = array("foo", {$this->getVariableGetter('bar')});
EOF EOF
); );
+9
View File
@@ -37,4 +37,13 @@ abstract class Twig_Tests_Node_TestCase extends PHPUnit_Framework_TestCase
{ {
return new Twig_Environment(); return new Twig_Environment();
} }
protected function getVariableGetter($name)
{
if (version_compare(phpversion(), '5.4.0RC1', '>=')) {
return sprintf('(isset($context["%s"]) ? $context["%s"] : null)', $name, $name);
}
return sprintf('$this->getContext($context, "%s")', $name);
}
} }
@@ -38,6 +38,10 @@ class Twig_Tests_NodeVisitor_OptimizerTest extends PHPUnit_Framework_TestCase
public function testRenderVariableBlockOptimizer() public function testRenderVariableBlockOptimizer()
{ {
if (version_compare(phpversion(), '5.4.0RC1', '>=')) {
return;
}
$env = new Twig_Environment(new Twig_Loader_String(), array('cache' => false, 'autoescape' => false)); $env = new Twig_Environment(new Twig_Loader_String(), array('cache' => false, 'autoescape' => false));
$env->addExtension(new Twig_Extension_Optimizer()); $env->addExtension(new Twig_Extension_Optimizer());
$stream = $env->parse($env->tokenize('{{ block(name|lower) }}', 'index')); $stream = $env->parse($env->tokenize('{{ block(name|lower) }}', 'index'));