* 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
* 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
} elseif ($this->isSpecial()) {
$compiler->raw($this->specialVars[$name]);
} else {
- $compiler
- ->raw('$this->getContext($context, ')
- ->string($name)
- ;
+ if (version_compare(phpversion(), '5.4.0RC1', '>=') && ($this->getAttribute('ignore_strict_check') || !$compiler->getEnvironment()->isStrictVariables())) {
+ // PHP 5.4 ternary operator performance was optimized
+ $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')) {
- $compiler->raw(', true');
- }
+ if ($this->getAttribute('ignore_strict_check')) {
+ $compiler->raw(', true');
+ }
- $compiler
- ->raw(')')
- ;
+ $compiler
+ ->raw(')')
+ ;
+ }
}
}
$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 (!$node instanceof Twig_Node_Expression) {
if (get_class($node) !== 'Twig_Node') {
$attr = new Twig_Node_Expression_Constant('bar', 0);
$args = new Twig_Node();
$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);
- $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(
new Twig_Node_Expression_Constant('bar', 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;
}
$context = new Twig_Node_Expression_Name('_context', 0);
$env = new Twig_Environment(null, array('strict_variables' => true));
+ $env1 = new Twig_Environment(null, array('strict_variables' => false));
return array(
array($node, '$this->getContext($context, "foo")', $env),
- array($node, '$this->getContext($context, "foo")'),
+ array($node, $this->getVariableGetter('foo'), $env1),
array($self, '$this'),
array($context, '$context'),
);
$tests[] = array($node, <<<EOF
\$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"]) {
- echo \$this->getContext(\$context, "foo");
+ echo {$this->getVariableGetter('foo')};
}
\$_parent = \$context['_parent'];
unset(\$context['_seq'], \$context['_iterated'], \$context['key'], \$context['item'], \$context['_parent'], \$context['loop']);
$tests[] = array($node, <<<EOF
\$context['_parent'] = (array) \$context;
-\$context['_seq'] = twig_ensure_traversable(\$this->getContext(\$context, "values"));
+\$context['_seq'] = twig_ensure_traversable({$this->getVariableGetter('values')});
\$context['loop'] = array(
'parent' => \$context['_parent'],
'index0' => 0,
\$context['loop']['last'] = 1 === \$length;
}
foreach (\$context['_seq'] as \$context["k"] => \$context["v"]) {
- echo \$this->getContext(\$context, "foo");
+ echo {$this->getVariableGetter('foo')};
++\$context['loop']['index0'];
++\$context['loop']['index'];
\$context['loop']['first'] = false;
$tests[] = array($node, <<<EOF
\$context['_parent'] = (array) \$context;
-\$context['_seq'] = twig_ensure_traversable(\$this->getContext(\$context, "values"));
+\$context['_seq'] = twig_ensure_traversable({$this->getVariableGetter('values')});
\$context['loop'] = array(
'parent' => \$context['_parent'],
'index0' => 0,
);
foreach (\$context['_seq'] as \$context["k"] => \$context["v"]) {
if (true) {
- echo \$this->getContext(\$context, "foo");
+ echo {$this->getVariableGetter('foo')};
++\$context['loop']['index0'];
++\$context['loop']['index'];
\$context['loop']['first'] = false;
$tests[] = array($node, <<<EOF
\$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['loop'] = array(
'parent' => \$context['_parent'],
\$context['loop']['last'] = 1 === \$length;
}
foreach (\$context['_seq'] as \$context["k"] => \$context["v"]) {
- echo \$this->getContext(\$context, "foo");
+ echo {$this->getVariableGetter('foo')};
\$context['_iterated'] = true;
++\$context['loop']['index0'];
++\$context['loop']['index'];
}
}
if (!\$context['_iterated']) {
- echo \$this->getContext(\$context, "foo");
+ echo {$this->getVariableGetter('foo')};
}
\$_parent = \$context['_parent'];
unset(\$context['_seq'], \$context['_iterated'], \$context['k'], \$context['v'], \$context['_parent'], \$context['loop']);
$tests[] = array($node, <<<EOF
if (true) {
- echo \$this->getContext(\$context, "foo");
+ echo {$this->getVariableGetter('foo')};
}
EOF
);
$tests[] = array($node, <<<EOF
if (true) {
- echo \$this->getContext(\$context, "foo");
+ echo {$this->getVariableGetter('foo')};
} elseif (false) {
- echo \$this->getContext(\$context, "bar");
+ echo {$this->getVariableGetter('bar')};
}
EOF
);
$tests[] = array($node, <<<EOF
if (true) {
- echo \$this->getContext(\$context, "foo");
+ echo {$this->getVariableGetter('foo')};
} else {
- echo \$this->getContext(\$context, "bar");
+ echo {$this->getVariableGetter('bar')};
}
EOF
);
$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);
$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
);
{
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);
+ }
}
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->addExtension(new Twig_Extension_Optimizer());
$stream = $env->parse($env->tokenize('{{ block(name|lower) }}', 'index'));