From 9d62ffd58161455f440a1fa07ea3dae80eb57a66 Mon Sep 17 00:00:00 2001
From: Dmitriy Simushev <simushevds@gmail.com>
Date: Tue, 25 Feb 2014 12:40:47 +0000
Subject: [PATCH] Use both "@index" and "@key" for associative arrays

---
 src/Handlebars/Context.php     | 77 ++++++++++++++--------------------
 src/Handlebars/Helpers.php     | 18 ++++----
 src/Handlebars/Template.php    | 14 +++++--
 tests/Xamin/HandlebarsTest.php | 13 +++++-
 4 files changed, 63 insertions(+), 59 deletions(-)

diff --git a/src/Handlebars/Context.php b/src/Handlebars/Context.php
index d9c2b73..2cbbfc6 100644
--- a/src/Handlebars/Context.php
+++ b/src/Handlebars/Context.php
@@ -65,6 +65,12 @@ class Context
      */
     protected $key = array();
 
+    /**
+     * @var array Special variables stack for sections. Each stack element can
+     * contain elements with "@index" and "@key" keys.
+     */
+    protected $specialVariables = array();
+
     /**
      * Mustache rendering Context constructor.
      *
@@ -90,27 +96,17 @@ class Context
     }
 
     /**
-     * Push an Index onto the index stack
+     * Push an array of special variables to stack.
      *
-     * @param integer $index Index of the current section item.
+     * @param array $variables An associative array of special variables.
      *
      * @return void
-     */
-    public function pushIndex($index)
-    {
-        array_push($this->index, $index);
-    }
-
-    /**
-     * Push a Key onto the key stack
      *
-     * @param string $key Key of the current object property.
-     *
-     * @return void
+     * @see \Handlebars\Context::$specialVariables
      */
-    public function pushKey($key)
+    public function pushSpecialVariables($variables)
     {
-        array_push($this->key, $key);
+        array_push($this->specialVariables, $variables);
     }
 
     /**
@@ -124,23 +120,15 @@ class Context
     }
 
     /**
-     * Pop the last index from the stack.
+     * Pop the last special variables set from the stack.
      *
-     * @return int Last index
-     */
-    public function popIndex()
-    {
-        return array_pop($this->index);
-    }
-
-    /**
-     * Pop the last key from the stack.
+     * @return array Associative array of special variables.
      *
-     * @return string Last key
+     * @see \Handlebars\Context::$specialVariables
      */
-    public function popKey()
+    public function popSpecialVariables()
     {
-        return array_pop($this->key);
+        return array_pop($this->specialVariables);
     }
 
     /**
@@ -154,23 +142,15 @@ class Context
     }
 
     /**
-     * Get the index of current section item.
+     * Get the last special variables set from the stack.
      *
-     * @return mixed Last index
-     */
-    public function lastIndex()
-    {
-        return end($this->index);
-    }
-
-    /**
-     * Get the key of current object property.
+     * @return array Associative array of special variables.
      *
-     * @return mixed Last key
+     * @see \Handlebars\Context::$specialVariables
      */
-    public function lastKey()
+    public function lastSpecialVariables()
     {
-        return end($this->key);
+        return end($this->specialVariables);
     }
 
     /**
@@ -236,10 +216,17 @@ class Context
             return '';
         } elseif ($variableName == '.' || $variableName == 'this') {
             return $current;
-        } elseif ($variableName == '@index') {
-            $current = $this->lastIndex();    
-        } elseif ($variableName == '@key') {
-            $current = $this->lastKey();
+        } elseif ($variableName[0] == '@') {
+            $specialVariables = $this->lastSpecialVariables();
+            if (isset($specialVariables[$variableName])) {
+                return $specialVariables[$variableName];
+            } elseif ($strict) {
+                throw new \InvalidArgumentException(
+                    'can not find variable in context'
+                );
+            } else {
+                return '';
+            }
         } else {
             $chunks = $this->_splitVariableName($variableName);
             foreach ($chunks as $chunk) {
diff --git a/src/Handlebars/Helpers.php b/src/Handlebars/Helpers.php
index 467b150..288de00 100644
--- a/src/Handlebars/Helpers.php
+++ b/src/Handlebars/Helpers.php
@@ -124,24 +124,22 @@ class Helpers
                     $template->setStopToken(false);
                     $buffer = $template->render($context);
                 } elseif (is_array($tmp) || $tmp instanceof \Traversable) {
-                    $islist = ($tmp instanceof \Generator) || (array_keys($tmp) == range(0, count($tmp) - 1));
+                    $isList = is_array($tmp) && (array_keys($tmp) == range(0, count($tmp) - 1));
+                    $index = 0;
 
                     foreach ($tmp as $key => $var) {
-                        if ($islist) {
-                            $context->pushIndex($key);
-                        } else {
-                            $context->pushKey($key);
+                        $specialVariables = array('@index' => $index);
+                        if (!$isList) {
+                            $specialVariables['@key'] = $key;
                         }
+                        $context->pushSpecialVariables($specialVariables);
                         $context->push($var);
                         $template->setStopToken('else');
                         $template->rewind();
                         $buffer .= $template->render($context);
                         $context->pop();
-                        if ($islist) {
-                            $context->popIndex();
-                        } else {
-                            $context->popKey();
-                        }
+                        $context->popSpecialVariables();
+                        $index++;
                     }
 
                     $template->setStopToken(false);
diff --git a/src/Handlebars/Template.php b/src/Handlebars/Template.php
index 0240e9a..c53bb28 100644
--- a/src/Handlebars/Template.php
+++ b/src/Handlebars/Template.php
@@ -291,12 +291,20 @@ class Template
             }
             $buffer = '';
             if (is_array($sectionVar) || $sectionVar instanceof \Traversable) {
-                foreach ($sectionVar as $index => $d) {
-                    $context->pushIndex($index);
+                $isList = is_array($sectionVar) && (array_keys($sectionVar) == range(0, count($sectionVar) - 1));
+                $index = 0;
+
+                foreach ($sectionVar as $key => $d) {
+                    $specialVariables = array('@index' => $index);
+                    if (!$isList) {
+                        $specialVariables['@key'] = $key;
+                    }
+                    $context->pushSpecialVariables($specialVariables);
                     $context->push($d);
                     $buffer .= $this->render($context);
                     $context->pop();
-                    $context->popIndex();
+                    $context->popSpecialVariables();
+                    $index++;
                 }
             } elseif (is_object($sectionVar)) {
                 //Act like with
diff --git a/tests/Xamin/HandlebarsTest.php b/tests/Xamin/HandlebarsTest.php
index 97ad045..9dd22ed 100644
--- a/tests/Xamin/HandlebarsTest.php
+++ b/tests/Xamin/HandlebarsTest.php
@@ -173,6 +173,16 @@ class HandlebarsTest extends \PHPUnit_Framework_TestCase
                 array('data' => array('key1'=>1, 'key2'=>2,)),
                 'key1=>1key2=>2'
             ),
+            array(
+                '{{#each data}}{{@key}}=>{{this}}{{/each}}',
+                array('data' => new \ArrayIterator(array('key1'=>1, 'key2'=>2))),
+                'key1=>1key2=>2'
+            ),
+            array(
+                '{{#each data}}{{@index}}=>{{this}},{{/each}}',
+                array('data' => array('key1'=>1, 'key2'=>2,)),
+                '0=>1,1=>2,'
+            ),
             array(
                 '{{#each data}}{{this}}{{else}}fail{{/each}}',
                 array('data' => array(1, 2, 3, 4)),
@@ -274,6 +284,8 @@ class HandlebarsTest extends \PHPUnit_Framework_TestCase
         $this->assertEquals('', $engine->render('{{#x}}yes{{/x}}', array ('x' => false)));
         $this->assertEquals('yes', $engine->render('{{^x}}yes{{/x}}', array ('x' => false)));
         $this->assertEquals('1234', $engine->render('{{#x}}{{this}}{{/x}}', array ('x' => array (1,2,3,4))));
+        $this->assertEquals('012', $engine->render('{{#x}}{{@index}}{{/x}}', array ('x' => array ('a','b','c'))));
+        $this->assertEquals('abc', $engine->render('{{#x}}{{@key}}{{/x}}', array ('x' => array ('a'=>1,'b'=>2,'c'=>3))));
         $std = new stdClass();
         $std->value = 1;
         $this->assertEquals('1', $engine->render('{{#x}}{{value}}{{/x}}', array ('x' => $std)));
@@ -512,7 +524,6 @@ class HandlebarsTest extends \PHPUnit_Framework_TestCase
         $context->pop();
         $this->assertEquals('value', $context->get('value'));
         $this->assertEquals('value', $context->get('value', true));
-        $this->assertFalse($context->lastIndex());
     }
 
     /**