.. note::
A sequence can be either an array or an object implementing the
- ``Iterator`` interface.
+ ``Traversable`` interface.
If you do need to iterate over a sequence of numbers, you can use the ``..``
operator (as of Twig 0.9.5):
}
}
-function twig_iterator_to_array($seq, $useKeys = true)
+function twig_ensure_traversable($seq)
{
- if (is_array($seq)) {
- return $seq;
- } elseif (is_object($seq) && $seq instanceof Traversable) {
+ if (is_array($seq) || (is_object($seq) && $seq instanceof Traversable)) {
return $seq;
} else {
return array();
$compiler
->addDebugInfo($this)
// the (array) cast bypasses a PHP 5.2.6 bug
- ->write('$context[\'_parent\'] = (array) $context;'."\n")
+ ->write("\$context['_parent'] = (array) \$context;\n")
+ ->write("\$context['_seq'] = twig_ensure_traversable(")
+ ->subcompile($this->getNode('seq'))
+ ->raw(");\n")
;
- if (null !== $this->getNode('else')) {
+ if (null !== $this->getNode('else') || null !== $this->getNode('joined_with')) {
$compiler->write("\$context['_iterated'] = false;\n");
}
- $compiler
- ->write("\$context['_seq'] = twig_iterator_to_array(")
- ->subcompile($this->getNode('seq'))
- ->raw(");\n")
- ;
-
if ($this->getAttribute('with_loop')) {
$compiler
- ->write("\$countable = is_array(\$context['_seq']) || (is_object(\$context['_seq']) && \$context['_seq'] instanceof Countable);\n")
- ->write("\$length = \$countable ? count(\$context['_seq']) : null;\n")
-
->write("\$context['loop'] = array(\n")
->write(" 'parent' => \$context['_parent'],\n")
->write(" 'index0' => 0,\n")
->write(" 'index' => 1,\n")
->write(" 'first' => true,\n")
->write(");\n")
- ->write("if (\$countable) {\n")
+ ->write("if (is_array(\$context['_seq']) || (is_object(\$context['_seq']) && \$context['_seq'] instanceof Countable)) {\n")
->indent()
+ ->write("\$length = count(\$context['_seq']);\n")
->write("\$context['loop']['revindex0'] = \$length - 1;\n")
->write("\$context['loop']['revindex'] = \$length;\n")
->write("\$context['loop']['length'] = \$length;\n")
;
}
- if (null !== $this->getNode('joined_with')) {
- $compiler->write("\$context['_first_iteration'] = true;\n");
- }
-
$compiler
->write("foreach (\$context['_seq'] as ")
->subcompile($this->getNode('key_target'))
->indent()
;
- if (null !== $this->getNode('else')) {
- $compiler->write("\$context['_iterated'] = true;\n");
- }
-
if (null !== $this->getNode('joined_with')) {
$compiler
- ->write("if (\$context['_first_iteration']) {\n")
- ->indent()
- ->write("\$context['_first_iteration'] = false;\n")
- ->outdent()
- ->write("} else {\n")
+ ->write("if (\$context['_iterated']) {\n")
->indent()
->write("echo ")
->subcompile($this->getNode('joined_with'))
$compiler->subcompile($this->getNode('body'));
+ if (null !== $this->getNode('else') || null !== $this->getNode('joined_with')) {
+ $compiler->write("\$context['_iterated'] = true;\n");
+ }
+
if ($this->getAttribute('with_loop')) {
$compiler
->write("++\$context['loop']['index0'];\n")
->write("++\$context['loop']['index'];\n")
->write("\$context['loop']['first'] = false;\n")
- ->write("if (\$countable) {\n")
+ ->write("if (isset(\$context['loop']['length'])) {\n")
->indent()
->write("--\$context['loop']['revindex0'];\n")
->write("--\$context['loop']['revindex'];\n")
;
}
- $compiler->write('$_parent = $context[\'_parent\'];'."\n");
+ $compiler->write("\$_parent = \$context['_parent'];\n");
// remove some "private" loop variables (needed for nested loops)
$compiler->write('unset($context[\'_seq\'], $context[\'_iterated\'], $context[\''.$this->getNode('key_target')->getAttribute('name').'\'], $context[\''.$this->getNode('value_target')->getAttribute('name').'\'], $context[\'_parent\'], $context[\'loop\']);'."\n");
- /// keep the values set in the inner context for variables defined in the outer context
- $compiler->write('$context = array_merge($_parent, array_intersect_key($context, $_parent));'."\n");
+ // keep the values set in the inner context for variables defined in the outer context
+ $compiler->write("\$context = array_merge(\$_parent, array_intersect_key(\$context, \$_parent));\n");
}
}
$tests[] = array($node, <<<EOF
\$context['_parent'] = (array) \$context;
-\$context['_seq'] = twig_iterator_to_array((isset(\$context['items']) ? \$context['items'] : null));
+\$context['_seq'] = twig_ensure_traversable((isset(\$context['items']) ? \$context['items'] : null));
foreach (\$context['_seq'] as \$context['key'] => \$context['item']) {
echo (isset(\$context['foo']) ? \$context['foo'] : null);
}
$tests[] = array($node, <<<EOF
\$context['_parent'] = (array) \$context;
-\$context['_seq'] = twig_iterator_to_array((isset(\$context['values']) ? \$context['values'] : null));
-\$countable = is_array(\$context['_seq']) || (is_object(\$context['_seq']) && \$context['_seq'] instanceof Countable);
-\$length = \$countable ? count(\$context['_seq']) : null;
+\$context['_seq'] = twig_ensure_traversable((isset(\$context['values']) ? \$context['values'] : null));
+\$context['_iterated'] = false;
\$context['loop'] = array(
'parent' => \$context['_parent'],
'index0' => 0,
'index' => 1,
'first' => true,
);
-if (\$countable) {
+if (is_array(\$context['_seq']) || (is_object(\$context['_seq']) && \$context['_seq'] instanceof Countable)) {
+ \$length = count(\$context['_seq']);
\$context['loop']['revindex0'] = \$length - 1;
\$context['loop']['revindex'] = \$length;
\$context['loop']['length'] = \$length;
\$context['loop']['last'] = 1 === \$length;
}
-\$context['_first_iteration'] = true;
foreach (\$context['_seq'] as \$context['k'] => \$context['v']) {
- if (\$context['_first_iteration']) {
- \$context['_first_iteration'] = false;
- } else {
+ if (\$context['_iterated']) {
echo ", ";
}
echo (isset(\$context['foo']) ? \$context['foo'] : null);
+ \$context['_iterated'] = true;
++\$context['loop']['index0'];
++\$context['loop']['index'];
\$context['loop']['first'] = false;
- if (\$countable) {
+ if (isset(\$context['loop']['length'])) {
--\$context['loop']['revindex0'];
--\$context['loop']['revindex'];
\$context['loop']['last'] = 0 === \$context['loop']['revindex0'];
$tests[] = array($node, <<<EOF
\$context['_parent'] = (array) \$context;
+\$context['_seq'] = twig_ensure_traversable((isset(\$context['values']) ? \$context['values'] : null));
\$context['_iterated'] = false;
-\$context['_seq'] = twig_iterator_to_array((isset(\$context['values']) ? \$context['values'] : null));
-\$countable = is_array(\$context['_seq']) || (is_object(\$context['_seq']) && \$context['_seq'] instanceof Countable);
-\$length = \$countable ? count(\$context['_seq']) : null;
\$context['loop'] = array(
'parent' => \$context['_parent'],
'index0' => 0,
'index' => 1,
'first' => true,
);
-if (\$countable) {
+if (is_array(\$context['_seq']) || (is_object(\$context['_seq']) && \$context['_seq'] instanceof Countable)) {
+ \$length = count(\$context['_seq']);
\$context['loop']['revindex0'] = \$length - 1;
\$context['loop']['revindex'] = \$length;
\$context['loop']['length'] = \$length;
\$context['loop']['last'] = 1 === \$length;
}
foreach (\$context['_seq'] as \$context['k'] => \$context['v']) {
- \$context['_iterated'] = true;
echo (isset(\$context['foo']) ? \$context['foo'] : null);
+ \$context['_iterated'] = true;
++\$context['loop']['index0'];
++\$context['loop']['index'];
\$context['loop']['first'] = false;
- if (\$countable) {
+ if (isset(\$context['loop']['length'])) {
--\$context['loop']['revindex0'];
--\$context['loop']['revindex'];
\$context['loop']['last'] = 0 === \$context['loop']['revindex0'];