diff --git a/src/Mustache/Compiler.php b/src/Mustache/Compiler.php index bf5f808..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 = ' $blockFunction = $context->findInBlock(%s); if (is_callable($blockFunction)) { - $boundFunction = $blockFunction->bindTo($this, $this); - $boundFunction($context, $buffer); + $buffer .= call_user_func($blockFunction, $context); } else { %s } @@ -259,15 +262,12 @@ class Mustache_Compiler { $id = var_export($id, true); - return sprintf($this->prepare(self::BLOCK_VAR, $level), $id, $this->walk($nodes)); + return sprintf($this->prepare(self::BLOCK_VAR, $level), $id, $this->walk($nodes, $level)); } const BLOCK_ARG = ' // %s block_arg - $blockFunction = function(& $context, & $buffer, $indent=\'\') { - %s - }; - $newContext[%s] = $blockFunction; + $newContext[%s] = array($this, \'block%s\'); '; /** @@ -285,10 +285,38 @@ class Mustache_Compiler */ private function blockArg($nodes, $id, $start, $end, $otag, $ctag, $level) { + $key = $this->block($nodes); + $keystr = var_export($key, true); $id = var_export($id, true); + + 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); - return sprintf($this->prepare(self::BLOCK_ARG, 1), $id, $code, $id); + $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 b2e178d..598e798 100644 --- a/test/Mustache/Test/Functional/InheritanceTest.php +++ b/test/Mustache/Test/Functional/InheritanceTest.php @@ -467,6 +467,23 @@ class Mustache_Test_Functional_InheritanceTest extends PHPUnit_Framework_TestCas $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