From 588fb930974b387ce573244b0ea2bdec95307ced Mon Sep 17 00:00:00 2001 From: fabien Date: Sat, 7 Nov 2009 22:18:56 +0000 Subject: [PATCH] made a huge optimization for loops (again) git-svn-id: http://svn.twig-project.org/trunk@118 93ef8e89-cb99-4229-a87c-7fa0fa45744b --- doc/02-Twig-for-Template-Designers.markdown | 46 +++++++++++++----- lib/Twig/Extension/Core.php | 1 - lib/Twig/Node/For.php | 56 ++++++++++++++--------- lib/Twig/runtime.php | 23 +++++++++ lib/Twig/runtime_for.php | 66 --------------------------- 5 files changed, 90 insertions(+), 102 deletions(-) delete mode 100644 lib/Twig/runtime_for.php diff --git a/doc/02-Twig-for-Template-Designers.markdown b/doc/02-Twig-for-Template-Designers.markdown index 544f8d4..014a2e5 100644 --- a/doc/02-Twig-for-Template-Designers.markdown +++ b/doc/02-Twig-for-Template-Designers.markdown @@ -390,7 +390,11 @@ provided in a variable called `users`: {% endfor %} -Inside of a for loop block you can access some special variables: +>**NOTE** +>A sequence can be either an array or an object implementing the `Iterator` +>interface. + +Inside of a `for` loop block you can access some special variables: | Variable | Description | --------------------- | ------------------------------------------------------------- @@ -402,7 +406,8 @@ Inside of a for loop block you can access some special variables: | `loop.last` | True if last iteration | `loop.length` | The number of items in the sequence -Unlike in PHP it's not possible to `break` or `continue` in a loop. +>**NOTE** +>Unlike in PHP it's not possible to `break` or `continue` in a loop. If no iteration took place because the sequence was empty, you can render a replacement block by using `else`: @@ -412,10 +417,35 @@ replacement block by using `else`: {% for user in users %}
  • {{ user.username|e }}
  • {% else %} -
  • no users found
  • +
  • no user found
  • {% endif %} +By default, a loop iterates over the values of the sequence. You can iterate +on keys by using the `keys` filter: + + [twig] +

    Members

    + + +You can also access both keys and values: + + [twig] +

    Members

    + + +>**NOTE** +>On Twig before 0.9.3, you need to use the `items` filter to access both the +>keys and values (`{% for key, value in users|items %}`). + ### If The `if` statement in Twig is comparable with the if statements of PHP. In the @@ -801,16 +831,6 @@ iterate over the keys of an array: ... {% endfor %} -### `items` - -The `items` filter is mainly useful when using the `for` tag to iterate over -both the keys and the values of an array: - - [twig] - {% for key, value in array|items %} - ... - {% endfor %} - ### `escape`, `e` The `escape` filter converts the characters `&`, `<`, `>`, `'`, and `"` in diff --git a/lib/Twig/Extension/Core.php b/lib/Twig/Extension/Core.php index b0b625a..e93e08f 100644 --- a/lib/Twig/Extension/Core.php +++ b/lib/Twig/Extension/Core.php @@ -18,7 +18,6 @@ class Twig_Extension_Core extends Twig_Extension public function initRuntime() { require_once dirname(__FILE__).'/../runtime.php'; - require_once dirname(__FILE__).'/../runtime_for.php'; } /** diff --git a/lib/Twig/Node/For.php b/lib/Twig/Node/For.php index 184e470..fb3f127 100644 --- a/lib/Twig/Node/For.php +++ b/lib/Twig/Node/For.php @@ -58,13 +58,38 @@ class Twig_Node_For extends Twig_Node implements Twig_NodeListInterface $compiler->write("\$context['_iterated'] = false;\n"); } + if ($this->isMultitarget) + { + $loopVars = array($this->item[0]->getName(), $this->item[1]->getName()); + } + else + { + $loopVars = array('_key', $this->item->getName()); + } + $var = rand(1, 999999); $compiler ->write("\$seq$var = twig_iterator_to_array(") ->subcompile($this->seq) ->raw(");\n") ->write("\$context['loop']['length'] = count(\$seq$var);\n") - ->write("for (\$i$var = 0; \$i$var < \$context['loop']['length']; \$i$var++)\n") + + ->write("\$context['loop'] = array(\n") + ->write(" 'parent' => \$context['_parent'],\n") + ->write(" 'length' => \$context['loop']['length'],\n") + ->write(" 'index0' => 0,\n") + ->write(" 'index' => 1,\n") + ->write(" 'revindex0' => \$context['loop']['length'] - 1,\n") + ->write(" 'revindex' => \$context['loop']['length'],\n") + ->write(" 'first' => true,\n") + ->write(" 'last' => false,\n") + ->write(");\n") + + ->write("foreach (\$seq$var as \$context[") + ->repr($loopVars[0]) + ->raw("] => \$context[") + ->repr($loopVars[1]) + ->raw("])\n") ->write("{\n") ->indent() ; @@ -74,29 +99,16 @@ class Twig_Node_For extends Twig_Node implements Twig_NodeListInterface $compiler->write("\$context['_iterated'] = true;\n"); } - $compiler->write("twig_set_loop_context(\$context, \$seq$var, \$i$var, "); - - if ($this->isMultitarget) - { - $compiler->raw('array('); - foreach ($this->item as $idx => $node) - { - if ($idx) - { - $compiler->raw(', '); - } - $compiler->repr($node->getName()); - } - $compiler->raw(')'); - } - else - { - $compiler->repr($this->item->getName()); - } - $compiler - ->raw(");\n") ->subcompile($this->body) + + ->write("++\$context['loop']['index0'];\n") + ->write("++\$context['loop']['index'];\n") + ->write("--\$context['loop']['revindex0'];\n") + ->write("--\$context['loop']['revindex'];\n") + ->write("\$context['loop']['first'] = false;\n") + ->write("\$context['loop']['last'] = 0 === \$context['loop']['revindex0'];\n") + ->outdent() ->write("}\n") ; diff --git a/lib/Twig/runtime.php b/lib/Twig/runtime.php index c41f60f..6bb32cc 100644 --- a/lib/Twig/runtime.php +++ b/lib/Twig/runtime.php @@ -188,3 +188,26 @@ else return ucfirst(strtolower($string)); } } + +function twig_iterator_to_array($seq) +{ + if (is_array($seq)) + { + return $seq; + } + elseif (is_object($seq) && $seq instanceof Iterator) + { + return iterator_to_array($seq); + } + else + { + return array(); + } +} + +// only for backward compatibility +function twig_get_array_items_filter($array) +{ + // noop + return $array; +} diff --git a/lib/Twig/runtime_for.php b/lib/Twig/runtime_for.php deleted file mode 100644 index c526153..0000000 --- a/lib/Twig/runtime_for.php +++ /dev/null @@ -1,66 +0,0 @@ - $context['_parent'], - 'length' => $context['loop']['length'], - 'index0' => $idx, - 'index' => $idx + 1, - 'revindex0' => $context['loop']['length'] - $idx - 1, - 'revindex' => $context['loop']['length'] - $idx, - 'first' => $idx == 0, - 'last' => $idx + 1 == $context['loop']['length'], - ); -} - -function twig_get_array_items_filter($array) -{ - if (!is_array($array) && (!is_object($array) || !$array instanceof Iterator)) - { - return false; - } - - $result = array(); - foreach ($array as $key => $value) - { - $result[] = array($key, $value); - } - - return $result; -} -- 1.7.2.5