diff --git a/src/Handlebars/Context.php b/src/Handlebars/Context.php index 7948512..49c390f 100644 --- a/src/Handlebars/Context.php +++ b/src/Handlebars/Context.php @@ -73,7 +73,7 @@ class Context protected $key = array(); /** - * Special variables stack for sections. + * Special variables stack for sections. * * @var array Each stack element can * contain elements with "@index", "@key", "@first" and "@last" keys. @@ -186,7 +186,7 @@ class Context * @param boolean $strict strict search? if not found then throw exception * * @throws \InvalidArgumentException in strict mode and variable not found - * @throws \RuntimeException if supplied argument is a malformed quoted string + * @throws \RuntimeException if supplied argument is a malformed quoted string * @throws \InvalidArgumentException if variable name is invalid * @return mixed */ @@ -252,12 +252,17 @@ class Context } } else { $chunks = $this->_splitVariableName($variableName); - foreach ($chunks as $chunk) { - if (is_string($current) and $current == '') { - return $current; + do { + $current = current($this->stack); + foreach ($chunks as $chunk) { + if (is_string($current) and $current == '') { + return $current; + } + $current = $this->_findVariableInContext($current, $chunk, $strict); } - $current = $this->_findVariableInContext($current, $chunk, $strict); - } + prev($this->stack); + + } while ($current === null && current($this->stack) !== false); } return $current; } @@ -316,18 +321,18 @@ class Context $bad_chars = preg_quote(self::NOT_VALID_NAME_CHARS, '/'); $bad_seg_chars = preg_quote(self::NOT_VALID_SEGMENT_NAME_CHARS, '/'); - $name_pattern = "(?:[^" - . $bad_chars - . "\s]+)|(?:\[[^" - . $bad_seg_chars + $name_pattern = "(?:[^" + . $bad_chars + . "\s]+)|(?:\[[^" + . $bad_seg_chars . "]+\])"; - - $check_pattern = "/^((" - . $name_pattern - . ")\.)*(" - . $name_pattern + + $check_pattern = "/^((" + . $name_pattern + . ")\.)*(" + . $name_pattern . ")\.?$/"; - + $get_pattern = "/(?:" . $name_pattern . ")/"; if (!preg_match($check_pattern, $variableName)) { diff --git a/src/Handlebars/Template.php b/src/Handlebars/Template.php index bd1d91a..28860fe 100644 --- a/src/Handlebars/Template.php +++ b/src/Handlebars/Template.php @@ -24,6 +24,7 @@ namespace Handlebars; use Handlebars\Arguments; +use Traversable; /** * Handlebars base template @@ -447,35 +448,29 @@ class Template ); } $buffer = ''; - if (is_array($sectionVar) || $sectionVar instanceof \Traversable) { - $isList = is_array($sectionVar) && - (array_keys($sectionVar) === range(0, count($sectionVar) - 1)); + if ($this->_checkIterable($sectionVar)) { $index = 0; - $lastIndex = $isList ? (count($sectionVar) - 1) : false; - + $lastIndex = (count($sectionVar) - 1); foreach ($sectionVar as $key => $d) { - $specialVariables = array( - '@index' => $index, - '@first' => ($index === 0), - '@last' => ($index === $lastIndex), + $context->pushSpecialVariables( + array( + '@index' => $index, + '@first' => ($index === 0), + '@last' => ($index === $lastIndex), + '@key' => $key + ) ); - if (!$isList) { - $specialVariables['@key'] = $key; - } - $context->pushSpecialVariables($specialVariables); $context->push($d); $buffer .= $this->render($context); $context->pop(); $context->popSpecialVariables(); $index++; } - } elseif (is_object($sectionVar)) { + } elseif ($sectionVar) { //Act like with $context->push($sectionVar); $buffer = $this->render($context); $context->pop(); - } elseif ($sectionVar) { - $buffer = $this->render($context); } return $buffer; @@ -712,4 +707,31 @@ class Template return $args->getPositionalArguments(); } + + /** + * Tests whether a value should be iterated over (e.g. in a section context). + * + * @param mixed $value Value to check if iterable. + * + * @return bool True if the value is 'iterable' + * + * @see https://github.com/bobthecow/mustache.php/blob/18a2adc/src/Mustache/Template.php#L85-L113 + */ + private function _checkIterable($value) + { + switch (gettype($value)) { + case 'object': + return $value instanceof Traversable; + case 'array': + $i = 0; + foreach ($value as $k => $v) { + if ($k !== $i++) { + return false; + } + } + return true; + default: + return false; + } + } } diff --git a/tests/Xamin/HandlebarsTest.php b/tests/Xamin/HandlebarsTest.php index ea756e2..e892163 100644 --- a/tests/Xamin/HandlebarsTest.php +++ b/tests/Xamin/HandlebarsTest.php @@ -689,12 +689,18 @@ class HandlebarsTest extends \PHPUnit_Framework_TestCase $this->assertEquals('', $engine->render('{{^x}}yes{{/x}}', array('x' => true))); $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)))); - $this->assertEquals('the_only_key', $engine->render('{{#x}}{{@key}}{{/x}}', array('x' => array('the_only_key' => 1)))); + $this->assertEquals('123', $engine->render('{{#x}}{{a}}{{b}}{{c}}{{/x}}', array('x' => array('a' => 1, 'b' => 2, 'c' => 3)))); + $this->assertEquals('1', $engine->render('{{#x}}{{the_only_key}}{{/x}}', array('x' => array('the_only_key' => 1)))); $std = new stdClass(); $std->value = 1; + $std->other = 4; $this->assertEquals('1', $engine->render('{{#x}}{{value}}{{/x}}', array('x' => $std))); $this->assertEquals('1', $engine->render('{{{x}}}', array('x' => 1))); + $this->assertEquals('1 2', $engine->render('{{#x}}{{value}} {{parent}}{{/x}}', array('x' => $std, 'parent' => 2))); + + $y = new stdClass(); + $y->value = 2; + $this->assertEquals('2 1 3 4', $engine->render('{{#x}}{{#y}}{{value}} {{x.value}} {{from_root}} {{other}}{{/y}}{{/x}}', array('x' => $std, 'y' => $y, 'from_root' => 3))); } /** @@ -962,6 +968,7 @@ EOM; $this->setExpectedException('RuntimeException'); $engine->render('{{>foo-again}}', array()); + } /**