Separate stacks are necessary for blocks to be resolved independently of variables. Renamed parent_* to block_*.

This commit is contained in:
Dan Miller
2014-04-08 21:02:23 +00:00
parent 63b835bd09
commit af8a62c5e2
8 changed files with 82 additions and 31 deletions
+17 -18
View File
@@ -110,8 +110,8 @@ class Mustache_Compiler
); );
break; break;
case Mustache_Tokenizer::T_PARENT_ARG: case Mustache_Tokenizer::T_BLOCK_ARG:
$code .= $this->parentArg( $code .= $this->blockArg(
$node[Mustache_Tokenizer::NODES], $node[Mustache_Tokenizer::NODES],
$node[Mustache_Tokenizer::NAME], $node[Mustache_Tokenizer::NAME],
$node[Mustache_Tokenizer::INDEX], $node[Mustache_Tokenizer::INDEX],
@@ -122,8 +122,8 @@ class Mustache_Compiler
); );
break; break;
case Mustache_Tokenizer::T_PARENT_VAR: case Mustache_Tokenizer::T_BLOCK_VAR:
$code .= $this->parentVar( $code .= $this->blockVar(
$node[Mustache_Tokenizer::NODES], $node[Mustache_Tokenizer::NODES],
$node[Mustache_Tokenizer::NAME], $node[Mustache_Tokenizer::NAME],
$node[Mustache_Tokenizer::INDEX], $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); throw new Mustache_Exception_SyntaxException(sprintf('Unknown token type: %s', $node[Mustache_Tokenizer::TYPE]), $node);
} }
} }
return $code; return $code;
} }
@@ -211,7 +210,7 @@ class Mustache_Compiler
return sprintf($this->prepare($klass, 0, false, true), $name, $callable, $code, $sections); 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); $value = $this->resolveValue($context->%s(%s), $context, $indent);
if($value && !is_array($value) && !is_object($value)) { if($value && !is_array($value) && !is_object($value)) {
$buffer .= %s; $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); $id_str = var_export($id, true);
$value = $this->getEscape(); $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 = ' const BLOCK_ARG = '
// %s parent_arg // %s block_arg
$value = $this->section%s($context, $indent, true); $value = $this->section%s($context, $indent, true);
$newContext[%s] = %s$value; $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); $key = $this->section($nodes, $id, $start, $end, $otag, $ctag, $level, true);
$filters = ''; $filters = '';
@@ -247,7 +246,7 @@ class Mustache_Compiler
$method = $this->getFindMethod($id); $method = $this->getFindMethod($id);
$id = var_export($id, true); $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 = ' const SECTION_CALL = '
@@ -384,9 +383,9 @@ class Mustache_Compiler
const PARENT = ' const PARENT = '
if ($parent = $this->mustache->LoadPartial(%s)) { if ($parent = $this->mustache->LoadPartial(%s)) {
$context->push($newContext); $context->pushBlockContext($newContext);
$buffer .= $parent->renderInternal($context, $indent); $buffer .= $parent->renderInternal($context, $indent);
$context->pop(); $context->popBlockContext();
} }
'; ';
@@ -394,7 +393,7 @@ class Mustache_Compiler
{ {
$block = ''; $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); $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 = ' const VARIABLE = '
+13 -2
View File
@@ -15,6 +15,7 @@
class Mustache_Context class Mustache_Context
{ {
private $stack = array(); private $stack = array();
private $block_stack = array();
/** /**
* Mustache rendering Context constructor. * Mustache rendering Context constructor.
@@ -38,6 +39,11 @@ class Mustache_Context
array_push($this->stack, $value); array_push($this->stack, $value);
} }
public function pushBlockContext($value)
{
array_push($this->block_stack, $value);
}
/** /**
* Pop the last Context frame from the stack. * Pop the last Context frame from the stack.
* *
@@ -48,6 +54,11 @@ class Mustache_Context
return array_pop($this->stack); return array_pop($this->stack);
} }
public function popBlockContext()
{
return array_pop($this->block_stack);
}
/** /**
* Get the last Context frame. * Get the last Context frame.
* *
@@ -120,9 +131,9 @@ class Mustache_Context
return $value; 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)) { if (is_array($context) && array_key_exists($id, $context)) {
return $context[$id]; return $context[$id];
} }
+2 -2
View File
@@ -108,9 +108,9 @@ class Mustache_Parser
$nodes[] = $this->buildTree($tokens, $token); $nodes[] = $this->buildTree($tokens, $token);
break; break;
case Mustache_Tokenizer::T_PARENT_VAR: case Mustache_Tokenizer::T_BLOCK_VAR:
if ($parent[Mustache_Tokenizer::TYPE] == Mustache_Tokenizer::T_PARENT) { 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); $this->clearStandaloneLines($nodes, $tokens);
$nodes[] = $this->buildTree($tokens, $token); $nodes[] = $this->buildTree($tokens, $token);
+4 -1
View File
@@ -61,10 +61,13 @@ abstract class Mustache_Template
* @param mixed $context Array or object rendering context (default: array()) * @param mixed $context Array or object rendering context (default: array())
* *
* @return string Rendered template * @return string Rendered template
* TODO construct a parent context stack here and pass it in
*/ */
public function render($context = array()) public function render($context = array())
{ {
return $this->renderInternal($this->prepareContextStack($context)); return $this->renderInternal(
$this->prepareContextStack($context)
);
} }
/** /**
+3 -3
View File
@@ -35,8 +35,8 @@ class Mustache_Tokenizer
const T_UNESCAPED_2 = '&'; const T_UNESCAPED_2 = '&';
const T_TEXT = '_t'; const T_TEXT = '_t';
const T_PRAGMA = '%'; const T_PRAGMA = '%';
const T_PARENT_VAR = '$'; const T_BLOCK_VAR = '$';
const T_PARENT_ARG = '$arg'; const T_BLOCK_ARG = '$arg';
// Valid token types // Valid token types
private static $tagTypes = array( private static $tagTypes = array(
@@ -51,7 +51,7 @@ class Mustache_Tokenizer
self::T_UNESCAPED => true, self::T_UNESCAPED => true,
self::T_UNESCAPED_2 => true, self::T_UNESCAPED_2 => true,
self::T_PRAGMA => true, self::T_PRAGMA => true,
self::T_PARENT_VAR => true, self::T_BLOCK_VAR => true,
); );
// Interpolated tags // Interpolated tags
@@ -137,6 +137,44 @@ class Mustache_Test_Functional_InheritanceTest extends PHPUnit_Framework_TestCas
$this->assertEquals('test |override1 default| |override2 default|', $tpl->render($data)); $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(
'{{<include}}{{$var}}var in template{{/var}}{{/include}}'
);
$data = array(
'var' => '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(
'{{<include}}{{/include}}'
);
$data = array(
'var' => 'var in data'
);
$this->assertEquals('var in include', $tpl->render($data));
}
public function testOverridePartialWithNewlines() public function testOverridePartialWithNewlines()
{ {
$partials = array( $partials = array(
+4 -4
View File
@@ -128,7 +128,7 @@ class Mustache_Test_ParserTest extends PHPUnit_Framework_TestCase
Mustache_Tokenizer::INDEX => 8 Mustache_Tokenizer::INDEX => 8
), ),
array( array(
Mustache_Tokenizer::TYPE => Mustache_Tokenizer::T_PARENT_VAR, Mustache_Tokenizer::TYPE => Mustache_Tokenizer::T_BLOCK_VAR,
Mustache_Tokenizer::NAME => 'bar', Mustache_Tokenizer::NAME => 'bar',
Mustache_Tokenizer::OTAG => '{{', Mustache_Tokenizer::OTAG => '{{',
Mustache_Tokenizer::CTAG => '}}', Mustache_Tokenizer::CTAG => '}}',
@@ -168,7 +168,7 @@ class Mustache_Test_ParserTest extends PHPUnit_Framework_TestCase
Mustache_Tokenizer::END => 27, Mustache_Tokenizer::END => 27,
Mustache_Tokenizer::NODES => array( Mustache_Tokenizer::NODES => array(
array( array(
Mustache_Tokenizer::TYPE => Mustache_Tokenizer::T_PARENT_ARG, Mustache_Tokenizer::TYPE => Mustache_Tokenizer::T_BLOCK_ARG,
Mustache_Tokenizer::NAME => 'bar', Mustache_Tokenizer::NAME => 'bar',
Mustache_Tokenizer::OTAG => '{{', Mustache_Tokenizer::OTAG => '{{',
Mustache_Tokenizer::CTAG => '}}', Mustache_Tokenizer::CTAG => '}}',
@@ -191,7 +191,7 @@ class Mustache_Test_ParserTest extends PHPUnit_Framework_TestCase
array( array(
array( 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::NAME => 'foo',
Mustache_Tokenizer::OTAG => '{{', Mustache_Tokenizer::OTAG => '{{',
Mustache_Tokenizer::CTAG => '}}', Mustache_Tokenizer::CTAG => '}}',
@@ -213,7 +213,7 @@ class Mustache_Test_ParserTest extends PHPUnit_Framework_TestCase
), ),
array( 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::NAME => 'foo',
Mustache_Tokenizer::OTAG => '{{', Mustache_Tokenizer::OTAG => '{{',
Mustache_Tokenizer::CTAG => '}}', Mustache_Tokenizer::CTAG => '}}',
+1 -1
View File
@@ -195,7 +195,7 @@ class Mustache_Test_TokenizerTest extends PHPUnit_Framework_TestCase
null, null,
array( array(
array( array(
Mustache_Tokenizer::TYPE => Mustache_Tokenizer::T_PARENT_VAR, Mustache_Tokenizer::TYPE => Mustache_Tokenizer::T_BLOCK_VAR,
Mustache_Tokenizer::NAME => 'arg', Mustache_Tokenizer::NAME => 'arg',
Mustache_Tokenizer::OTAG => '{{', Mustache_Tokenizer::OTAG => '{{',
Mustache_Tokenizer::CTAG => '}}', Mustache_Tokenizer::CTAG => '}}',