From af8a62c5e22cf126801e839ad578998ef1967271 Mon Sep 17 00:00:00 2001 From: Dan Miller Date: Mon, 7 Apr 2014 20:05:33 +0000 Subject: [PATCH] Separate stacks are necessary for blocks to be resolved independently of variables. Renamed parent_* to block_*. --- src/Mustache/Compiler.php | 35 +++++++++-------- src/Mustache/Context.php | 15 +++++++- src/Mustache/Parser.php | 4 +- src/Mustache/Template.php | 5 ++- src/Mustache/Tokenizer.php | 6 +-- .../Test/Functional/InheritanceTest.php | 38 +++++++++++++++++++ test/Mustache/Test/ParserTest.php | 8 ++-- test/Mustache/Test/TokenizerTest.php | 2 +- 8 files changed, 82 insertions(+), 31 deletions(-) diff --git a/src/Mustache/Compiler.php b/src/Mustache/Compiler.php index f0b6b71..9c32fa0 100644 --- a/src/Mustache/Compiler.php +++ b/src/Mustache/Compiler.php @@ -110,8 +110,8 @@ class Mustache_Compiler ); break; - case Mustache_Tokenizer::T_PARENT_ARG: - $code .= $this->parentArg( + case Mustache_Tokenizer::T_BLOCK_ARG: + $code .= $this->blockArg( $node[Mustache_Tokenizer::NODES], $node[Mustache_Tokenizer::NAME], $node[Mustache_Tokenizer::INDEX], @@ -122,8 +122,8 @@ class Mustache_Compiler ); break; - case Mustache_Tokenizer::T_PARENT_VAR: - $code .= $this->parentVar( + case Mustache_Tokenizer::T_BLOCK_VAR: + $code .= $this->blockVar( $node[Mustache_Tokenizer::NODES], $node[Mustache_Tokenizer::NAME], $node[Mustache_Tokenizer::INDEX], @@ -154,7 +154,6 @@ class Mustache_Compiler throw new Mustache_Exception_SyntaxException(sprintf('Unknown token type: %s', $node[Mustache_Tokenizer::TYPE]), $node); } } - return $code; } @@ -211,7 +210,7 @@ class Mustache_Compiler return sprintf($this->prepare($klass, 0, false, true), $name, $callable, $code, $sections); } - const PARENT_VAR = ' + const BLOCK_VAR = ' $value = $this->resolveValue($context->%s(%s), $context, $indent); if($value && !is_array($value) && !is_object($value)) { $buffer .= %s; @@ -220,22 +219,22 @@ class Mustache_Compiler } '; - private function parentVar($nodes, $id, $start, $end, $otag, $ctag, $level) + private function blockVar($nodes, $id, $start, $end, $otag, $ctag, $level) { - $method = 'findFromParent'; + $method = 'findInBlock'; $id_str = var_export($id, true); $value = $this->getEscape(); - return sprintf($this->prepare(self::PARENT_VAR, $level), $method, $id_str, $value, $this->walk($nodes, 2)); + return sprintf($this->prepare(self::BLOCK_VAR, $level), $method, $id_str, $value, $this->walk($nodes, 2)); } - const PARENT_ARG = ' - // %s parent_arg + const BLOCK_ARG = ' + // %s block_arg $value = $this->section%s($context, $indent, true); $newContext[%s] = %s$value; '; - private function parentArg($nodes, $id, $start, $end, $otag, $ctag, $level) + private function blockArg($nodes, $id, $start, $end, $otag, $ctag, $level) { $key = $this->section($nodes, $id, $start, $end, $otag, $ctag, $level, true); $filters = ''; @@ -247,7 +246,7 @@ class Mustache_Compiler $method = $this->getFindMethod($id); $id = var_export($id, true); - return sprintf($this->prepare(self::PARENT_ARG, $level), $key, $key, $id, $this->flushIndent()); + return sprintf($this->prepare(self::BLOCK_ARG, $level), $key, $key, $id, $this->flushIndent()); } const SECTION_CALL = ' @@ -384,9 +383,9 @@ class Mustache_Compiler const PARENT = ' if ($parent = $this->mustache->LoadPartial(%s)) { - $context->push($newContext); + $context->pushBlockContext($newContext); $buffer .= $parent->renderInternal($context, $indent); - $context->pop(); + $context->popBlockContext(); } '; @@ -394,7 +393,7 @@ class Mustache_Compiler { $block = ''; - $real_children = array_filter($children, array(__CLASS__, 'return_only_parent_args')); + $real_children = array_filter($children, array(__CLASS__, 'return_only_block_args')); $block = $this->walk($real_children, $level); @@ -405,9 +404,9 @@ class Mustache_Compiler ); } - private static function return_only_parent_args($child) + private static function return_only_block_args($child) { - return $child[Mustache_Tokenizer::TYPE] == Mustache_Tokenizer::T_PARENT_ARG; + return $child[Mustache_Tokenizer::TYPE] == Mustache_Tokenizer::T_BLOCK_ARG; } const VARIABLE = ' diff --git a/src/Mustache/Context.php b/src/Mustache/Context.php index 58461e5..ca82c35 100644 --- a/src/Mustache/Context.php +++ b/src/Mustache/Context.php @@ -15,6 +15,7 @@ class Mustache_Context { private $stack = array(); + private $block_stack = array(); /** * Mustache rendering Context constructor. @@ -38,6 +39,11 @@ class Mustache_Context array_push($this->stack, $value); } + public function pushBlockContext($value) + { + array_push($this->block_stack, $value); + } + /** * Pop the last Context frame from the stack. * @@ -48,6 +54,11 @@ class Mustache_Context return array_pop($this->stack); } + public function popBlockContext() + { + return array_pop($this->block_stack); + } + /** * Get the last Context frame. * @@ -120,9 +131,9 @@ class Mustache_Context return $value; } - public function findFromParent($id) + public function findInBlock($id) { - foreach($this->stack as $context) { + foreach($this->block_stack as $context) { if (is_array($context) && array_key_exists($id, $context)) { return $context[$id]; } diff --git a/src/Mustache/Parser.php b/src/Mustache/Parser.php index 83f71f1..448e229 100644 --- a/src/Mustache/Parser.php +++ b/src/Mustache/Parser.php @@ -108,9 +108,9 @@ class Mustache_Parser $nodes[] = $this->buildTree($tokens, $token); break; - case Mustache_Tokenizer::T_PARENT_VAR: + case Mustache_Tokenizer::T_BLOCK_VAR: if ($parent[Mustache_Tokenizer::TYPE] == Mustache_Tokenizer::T_PARENT) { - $token[Mustache_Tokenizer::TYPE] = Mustache_Tokenizer::T_PARENT_ARG; + $token[Mustache_Tokenizer::TYPE] = Mustache_Tokenizer::T_BLOCK_ARG; } $this->clearStandaloneLines($nodes, $tokens); $nodes[] = $this->buildTree($tokens, $token); diff --git a/src/Mustache/Template.php b/src/Mustache/Template.php index 8c76916..07f49ab 100644 --- a/src/Mustache/Template.php +++ b/src/Mustache/Template.php @@ -61,10 +61,13 @@ abstract class Mustache_Template * @param mixed $context Array or object rendering context (default: array()) * * @return string Rendered template + * TODO construct a parent context stack here and pass it in */ public function render($context = array()) { - return $this->renderInternal($this->prepareContextStack($context)); + return $this->renderInternal( + $this->prepareContextStack($context) + ); } /** diff --git a/src/Mustache/Tokenizer.php b/src/Mustache/Tokenizer.php index 1dac502..c851434 100644 --- a/src/Mustache/Tokenizer.php +++ b/src/Mustache/Tokenizer.php @@ -35,8 +35,8 @@ class Mustache_Tokenizer const T_UNESCAPED_2 = '&'; const T_TEXT = '_t'; const T_PRAGMA = '%'; - const T_PARENT_VAR = '$'; - const T_PARENT_ARG = '$arg'; + const T_BLOCK_VAR = '$'; + const T_BLOCK_ARG = '$arg'; // Valid token types private static $tagTypes = array( @@ -51,7 +51,7 @@ class Mustache_Tokenizer self::T_UNESCAPED => true, self::T_UNESCAPED_2 => true, self::T_PRAGMA => true, - self::T_PARENT_VAR => true, + self::T_BLOCK_VAR => true, ); // Interpolated tags diff --git a/test/Mustache/Test/Functional/InheritanceTest.php b/test/Mustache/Test/Functional/InheritanceTest.php index 799246b..d1e01af 100644 --- a/test/Mustache/Test/Functional/InheritanceTest.php +++ b/test/Mustache/Test/Functional/InheritanceTest.php @@ -137,6 +137,44 @@ class Mustache_Test_Functional_InheritanceTest extends PHPUnit_Framework_TestCas $this->assertEquals('test |override1 default| |override2 default|', $tpl->render($data)); } + public function testDataDoesNotOverrideBlock() + { + $partials = array( + 'include' => '{{$var}}var in include{{/var}}' + ); + + $this->mustache->setPartials($partials); + + $tpl = $this->mustache->loadTemplate( + '{{ 'var in data' + ); + + $this->assertEquals('var in template', $tpl->render($data)); + } + + public function testDataDoesNotOverrideDefaultBlockValue() + { + $partials = array( + 'include' => '{{$var}}var in include{{/var}}' + ); + + $this->mustache->setPartials($partials); + + $tpl = $this->mustache->loadTemplate( + '{{ 'var in data' + ); + + $this->assertEquals('var in include', $tpl->render($data)); + } + public function testOverridePartialWithNewlines() { $partials = array( diff --git a/test/Mustache/Test/ParserTest.php b/test/Mustache/Test/ParserTest.php index b780dca..b54e427 100644 --- a/test/Mustache/Test/ParserTest.php +++ b/test/Mustache/Test/ParserTest.php @@ -128,7 +128,7 @@ class Mustache_Test_ParserTest extends PHPUnit_Framework_TestCase Mustache_Tokenizer::INDEX => 8 ), array( - Mustache_Tokenizer::TYPE => Mustache_Tokenizer::T_PARENT_VAR, + Mustache_Tokenizer::TYPE => Mustache_Tokenizer::T_BLOCK_VAR, Mustache_Tokenizer::NAME => 'bar', Mustache_Tokenizer::OTAG => '{{', Mustache_Tokenizer::CTAG => '}}', @@ -168,7 +168,7 @@ class Mustache_Test_ParserTest extends PHPUnit_Framework_TestCase Mustache_Tokenizer::END => 27, Mustache_Tokenizer::NODES => array( array( - Mustache_Tokenizer::TYPE => Mustache_Tokenizer::T_PARENT_ARG, + Mustache_Tokenizer::TYPE => Mustache_Tokenizer::T_BLOCK_ARG, Mustache_Tokenizer::NAME => 'bar', Mustache_Tokenizer::OTAG => '{{', Mustache_Tokenizer::CTAG => '}}', @@ -191,7 +191,7 @@ class Mustache_Test_ParserTest extends PHPUnit_Framework_TestCase array( array( array( - Mustache_Tokenizer::TYPE => Mustache_Tokenizer::T_PARENT_VAR, + Mustache_Tokenizer::TYPE => Mustache_Tokenizer::T_BLOCK_VAR, Mustache_Tokenizer::NAME => 'foo', Mustache_Tokenizer::OTAG => '{{', Mustache_Tokenizer::CTAG => '}}', @@ -213,7 +213,7 @@ class Mustache_Test_ParserTest extends PHPUnit_Framework_TestCase ), array( array( - Mustache_Tokenizer::TYPE => Mustache_Tokenizer::T_PARENT_VAR, + Mustache_Tokenizer::TYPE => Mustache_Tokenizer::T_BLOCK_VAR, Mustache_Tokenizer::NAME => 'foo', Mustache_Tokenizer::OTAG => '{{', Mustache_Tokenizer::CTAG => '}}', diff --git a/test/Mustache/Test/TokenizerTest.php b/test/Mustache/Test/TokenizerTest.php index dab379d..629e543 100644 --- a/test/Mustache/Test/TokenizerTest.php +++ b/test/Mustache/Test/TokenizerTest.php @@ -195,7 +195,7 @@ class Mustache_Test_TokenizerTest extends PHPUnit_Framework_TestCase null, array( array( - Mustache_Tokenizer::TYPE => Mustache_Tokenizer::T_PARENT_VAR, + Mustache_Tokenizer::TYPE => Mustache_Tokenizer::T_BLOCK_VAR, Mustache_Tokenizer::NAME => 'arg', Mustache_Tokenizer::OTAG => '{{', Mustache_Tokenizer::CTAG => '}}',