merged branch nikic/optimizeVariableAccess2 (PR #382)

Commits
-------

5b91f12 Update tests
a5ef326 Use Template->getContext in non-strict mode too

Discussion
----------

Use Template->getContext() when in non-strict mode, too

As described in #380 using the current `isset($context['test']) ? $context['test'] : null` approach can cause problems in some cases: The ternary operator always returns by value and thus PHP's copy-on-write concept does not apply. So `$context['test']` needs to be copied in any case, even though a write never happens to it. Basically Twig is copying the values of all variables ever used inside Twig if it operates in non-strict mode. This is problematic when `$context['test']` contains big arrays as the whole array is copied.

In this patch I change Twig to use `Template->getContext` when in non-strict mode too and add an `isStrictVariables` check in there.
This commit is contained in:
Fabien Potencier
2011-07-12 09:49:06 +02:00
7 changed files with 25 additions and 21 deletions
+1 -3
View File
@@ -34,10 +34,8 @@ class Twig_Node_Expression_Name extends Twig_Node_Expression
}
} elseif (isset($specialVars[$name])) {
$compiler->raw($specialVars[$name]);
} elseif ($compiler->getEnvironment()->isStrictVariables()) {
$compiler->raw(sprintf('$this->getContext($context, \'%s\')', $name));
} else {
$compiler->raw(sprintf('(isset($context[\'%s\']) ? $context[\'%s\'] : null)', $name, $name));
$compiler->raw(sprintf('$this->getContext($context, \'%s\')', $name));
}
}
}
+7 -1
View File
@@ -221,11 +221,17 @@ abstract class Twig_Template implements Twig_TemplateInterface
* @param array $context The context
* @param string $item The variable to return from the context
*
* @throws Twig_Error_Runtime if the variable does not exist
* @return The content of the context variable
*
* @throws Twig_Error_Runtime if the variable does not exist and Twig is running in strict mode
*/
protected function getContext($context, $item)
{
if (!array_key_exists($item, $context)) {
if (!$this->env->isStrictVariables()) {
return null;
}
throw new Twig_Error_Runtime(sprintf('Variable "%s" does not exist', $item));
}
@@ -49,10 +49,10 @@ class Twig_Tests_Node_Expression_GetAttrTest extends Twig_Tests_Node_TestCase
$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((isset($context[\'foo\']) ? $context[\'foo\'] : null), "bar", array(), "any", false)');
$tests[] = array($node, '$this->getAttribute($this->getContext($context, \'foo\'), "bar", array(), "any", false)');
$node = new Twig_Node_Expression_GetAttr($expr, $attr, $args, Twig_TemplateInterface::ARRAY_CALL, 0);
$tests[] = array($node, '$this->getAttribute((isset($context[\'foo\']) ? $context[\'foo\'] : null), "bar", array(), "array", false)');
$tests[] = array($node, '$this->getAttribute($this->getContext($context, \'foo\'), "bar", array(), "array", false)');
$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),
));
$node = new Twig_Node_Expression_GetAttr($expr, $attr, $args, Twig_TemplateInterface::METHOD_CALL, 0);
$tests[] = array($node, '$this->getAttribute((isset($context[\'foo\']) ? $context[\'foo\'] : null), "bar", array((isset($context[\'foo\']) ? $context[\'foo\'] : null), "bar", ), "method", false)');
$tests[] = array($node, '$this->getAttribute($this->getContext($context, \'foo\'), "bar", array($this->getContext($context, \'foo\'), "bar", ), "method", false)');
return $tests;
}
+1 -1
View File
@@ -42,7 +42,7 @@ class Twig_Tests_Node_Expression_NameTest extends Twig_Tests_Node_TestCase
return array(
array($node, '$this->getContext($context, \'foo\')', $env),
array($node, '(isset($context[\'foo\']) ? $context[\'foo\'] : null)'),
array($node, '$this->getContext($context, \'foo\')'),
array($self, '$this'),
array($context, '$context'),
);
+7 -7
View File
@@ -61,9 +61,9 @@ class Twig_Tests_Node_ForTest extends Twig_Tests_Node_TestCase
$tests[] = array($node, <<<EOF
\$context['_parent'] = (array) \$context;
\$context['_seq'] = twig_ensure_traversable((isset(\$context['items']) ? \$context['items'] : null));
\$context['_seq'] = twig_ensure_traversable(\$this->getContext(\$context, 'items'));
foreach (\$context['_seq'] as \$context['key'] => \$context['item']) {
echo (isset(\$context['foo']) ? \$context['foo'] : null);
echo \$this->getContext(\$context, 'foo');
}
\$_parent = \$context['_parent'];
unset(\$context['_seq'], \$context['_iterated'], \$context['key'], \$context['item'], \$context['_parent'], \$context['loop']);
@@ -81,7 +81,7 @@ EOF
$tests[] = array($node, <<<EOF
\$context['_parent'] = (array) \$context;
\$context['_seq'] = twig_ensure_traversable((isset(\$context['values']) ? \$context['values'] : null));
\$context['_seq'] = twig_ensure_traversable(\$this->getContext(\$context, 'values'));
\$context['loop'] = array(
'parent' => \$context['_parent'],
'index0' => 0,
@@ -96,7 +96,7 @@ if (is_array(\$context['_seq']) || (is_object(\$context['_seq']) && \$context['_
\$context['loop']['last'] = 1 === \$length;
}
foreach (\$context['_seq'] as \$context['k'] => \$context['v']) {
echo (isset(\$context['foo']) ? \$context['foo'] : null);
echo \$this->getContext(\$context, 'foo');
++\$context['loop']['index0'];
++\$context['loop']['index'];
\$context['loop']['first'] = false;
@@ -122,7 +122,7 @@ EOF
$tests[] = array($node, <<<EOF
\$context['_parent'] = (array) \$context;
\$context['_seq'] = twig_ensure_traversable((isset(\$context['values']) ? \$context['values'] : null));
\$context['_seq'] = twig_ensure_traversable(\$this->getContext(\$context, 'values'));
\$context['_iterated'] = false;
\$context['loop'] = array(
'parent' => \$context['_parent'],
@@ -138,7 +138,7 @@ if (is_array(\$context['_seq']) || (is_object(\$context['_seq']) && \$context['_
\$context['loop']['last'] = 1 === \$length;
}
foreach (\$context['_seq'] as \$context['k'] => \$context['v']) {
echo (isset(\$context['foo']) ? \$context['foo'] : null);
echo \$this->getContext(\$context, 'foo');
\$context['_iterated'] = true;
++\$context['loop']['index0'];
++\$context['loop']['index'];
@@ -150,7 +150,7 @@ foreach (\$context['_seq'] as \$context['k'] => \$context['v']) {
}
}
if (!\$context['_iterated']) {
echo (isset(\$context['foo']) ? \$context['foo'] : null);
echo \$this->getContext(\$context, 'foo');
}
\$_parent = \$context['_parent'];
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
if (true) {
echo (isset(\$context['foo']) ? \$context['foo'] : null);
echo \$this->getContext(\$context, 'foo');
}
EOF
);
@@ -71,9 +71,9 @@ EOF
$tests[] = array($node, <<<EOF
if (true) {
echo (isset(\$context['foo']) ? \$context['foo'] : null);
echo \$this->getContext(\$context, 'foo');
} elseif (false) {
echo (isset(\$context['bar']) ? \$context['bar'] : null);
echo \$this->getContext(\$context, 'bar');
}
EOF
);
@@ -87,9 +87,9 @@ EOF
$tests[] = array($node, <<<EOF
if (true) {
echo (isset(\$context['foo']) ? \$context['foo'] : null);
echo \$this->getContext(\$context, 'foo');
} else {
echo (isset(\$context['bar']) ? \$context['bar'] : null);
echo \$this->getContext(\$context, 'bar');
}
EOF
);
+1 -1
View File
@@ -59,7 +59,7 @@ 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", (isset(\$context['bar']) ? \$context['bar'] : null));
list(\$context['foo'], \$context['bar']) = array("foo", \$this->getContext(\$context, 'bar'));
EOF
);