diff --git a/src/Mustache/Compiler.php b/src/Mustache/Compiler.php index e25bd8c..334fd55 100644 --- a/src/Mustache/Compiler.php +++ b/src/Mustache/Compiler.php @@ -19,6 +19,7 @@ class Mustache_Compiler private $pragmas; private $defaultPragmas = array(); private $sections; + private $blocks; private $source; private $indentNextLine; private $customEscape; @@ -43,6 +44,7 @@ class Mustache_Compiler { $this->pragmas = $this->defaultPragmas; $this->sections = array(); + $this->blocks = array(); $this->source = $source; $this->indentNextLine = true; $this->customEscape = $customEscape; @@ -195,6 +197,7 @@ class Mustache_Compiler return $buffer; } %s + %s }'; const KLASS_NO_LAMBDAS = 'walk($tree); $sections = implode("\n", $this->sections); - $klass = empty($this->sections) ? self::KLASS_NO_LAMBDAS : self::KLASS; + $blocks = implode("\n", $this->blocks); + $klass = empty($this->sections) && empty($this->blocks) ? self::KLASS_NO_LAMBDAS : self::KLASS; $callable = $this->strictCallables ? $this->prepare(self::STRICT_CALLABLE) : ''; - return sprintf($this->prepare($klass, 0, false, true), $name, $callable, $code, $sections); + return sprintf($this->prepare($klass, 0, false, true), $name, $callable, $code, $sections, $blocks); } const BLOCK_VAR = ' - $value = $this->resolveValue($context->findInBlock(%s), $context, $indent); - if ($value && !is_array($value) && !is_object($value)) { - $buffer .= $value; + $blockFunction = $context->findInBlock(%s); + if (is_callable($blockFunction)) { + $buffer .= call_user_func($blockFunction, $context); } else { %s } @@ -258,13 +262,12 @@ class Mustache_Compiler { $id = var_export($id, true); - return sprintf($this->prepare(self::BLOCK_VAR, $level), $id, $this->walk($nodes, 2)); + return sprintf($this->prepare(self::BLOCK_VAR, $level), $id, $this->walk($nodes, $level)); } const BLOCK_ARG = ' // %s block_arg - $value = $this->section%s($context, \'\', true); - $newContext[%s] = %s$value; + $newContext[%s] = array($this, \'block%s\'); '; /** @@ -282,10 +285,38 @@ class Mustache_Compiler */ private function blockArg($nodes, $id, $start, $end, $otag, $ctag, $level) { - $key = $this->section($nodes, $id, array(), $start, $end, $otag, $ctag, $level, true); - $id = var_export($id, true); + $key = $this->block($nodes); + $keystr = var_export($key, true); + $id = var_export($id, true); - return sprintf($this->prepare(self::BLOCK_ARG, $level), $id, $key, $id, $this->flushIndent()); + return sprintf($this->prepare(self::BLOCK_ARG, 1), $keystr, $id, $key); + } + + const BLOCK_FUNCTION = 'public function block%s($context) { + $indent = $buffer = \'\'; + + %s + + return $buffer; + }'; + + /** + * Generate Mustache Template inheritance block function PHP source. + * + * @param array $nodes Array of child tokens + * + * @return string key of new block function + */ + private function block($nodes) + { + $code = $this->walk($nodes, 1); + + $key = ucfirst(md5($code)); + + if (!isset($this->blocks[$key])) { + $this->blocks[$key] = sprintf($this->prepare(self::BLOCK_FUNCTION, 1), $key, $code); + } + return $key; } const SECTION_CALL = ' diff --git a/test/Mustache/Test/Functional/InheritanceTest.php b/test/Mustache/Test/Functional/InheritanceTest.php index 122ed31..598e798 100644 --- a/test/Mustache/Test/Functional/InheritanceTest.php +++ b/test/Mustache/Test/Functional/InheritanceTest.php @@ -433,6 +433,57 @@ class Mustache_Test_Functional_InheritanceTest extends PHPUnit_Framework_TestCas $this->assertEquals('default content', $tpl->render($data)); } + public function testInheritanceWithLazyEvaluation() + { + $partials = array( + 'parent' => '{{#items}}{{$value}}ignored{{/value}}{{/items}}', + ); + + $this->mustache->setPartials($partials); + + $tpl = $this->mustache->loadTemplate( + '{{{{/value}}{{/parent}}' + ); + + $data = array('items' => array(1, 2, 3)); + + $this->assertEquals('<1><2><3>', $tpl->render($data)); + } + + public function testInheritanceWithLazyEvaluationWhitespaceIgnored() + { + $partials = array( + 'parent' => '{{#items}}{{$value}}\n\nignored\n\n{{/value}}{{/items}}', + ); + + $this->mustache->setPartials($partials); + + $tpl = $this->mustache->loadTemplate( + '{{{{/value}}\n\n{{/parent}}' + ); + + $data = array('items' => array(1, 2, 3)); + + $this->assertEquals('<1><2><3>', $tpl->render($data)); + } + + public function testInheritanceWithLazyEvaluationAndSections() + { + $partials = array( + 'parent' => '{{#items}}{{$value}}\n\nignored {{.}} {{#more}} there is more {{/more}}\n\n{{/value}}{{/items}}', + ); + + $this->mustache->setPartials($partials); + + $tpl = $this->mustache->loadTemplate( + '{{{{#more}} there is less {{/more}}{{/value}}\n\n{{/parent}}' + ); + + $data = array('items' => array(1, 2, 3), 'more' => 'stuff'); + + $this->assertEquals('<1> there is less <2> there is less <3> there is less ', $tpl->render($data)); + } + /** * @dataProvider getIllegalInheritanceExamples * @expectedException Mustache_Exception_SyntaxException