Add block functions to the template, so they can be evaluated lazily.
Fixes #264
This commit is contained in:
+38
-10
@@ -19,6 +19,7 @@ class Mustache_Compiler
|
|||||||
private $pragmas;
|
private $pragmas;
|
||||||
private $defaultPragmas = array();
|
private $defaultPragmas = array();
|
||||||
private $sections;
|
private $sections;
|
||||||
|
private $blocks;
|
||||||
private $source;
|
private $source;
|
||||||
private $indentNextLine;
|
private $indentNextLine;
|
||||||
private $customEscape;
|
private $customEscape;
|
||||||
@@ -43,6 +44,7 @@ class Mustache_Compiler
|
|||||||
{
|
{
|
||||||
$this->pragmas = $this->defaultPragmas;
|
$this->pragmas = $this->defaultPragmas;
|
||||||
$this->sections = array();
|
$this->sections = array();
|
||||||
|
$this->blocks = array();
|
||||||
$this->source = $source;
|
$this->source = $source;
|
||||||
$this->indentNextLine = true;
|
$this->indentNextLine = true;
|
||||||
$this->customEscape = $customEscape;
|
$this->customEscape = $customEscape;
|
||||||
@@ -195,6 +197,7 @@ class Mustache_Compiler
|
|||||||
return $buffer;
|
return $buffer;
|
||||||
}
|
}
|
||||||
%s
|
%s
|
||||||
|
%s
|
||||||
}';
|
}';
|
||||||
|
|
||||||
const KLASS_NO_LAMBDAS = '<?php
|
const KLASS_NO_LAMBDAS = '<?php
|
||||||
@@ -225,18 +228,18 @@ class Mustache_Compiler
|
|||||||
{
|
{
|
||||||
$code = $this->walk($tree);
|
$code = $this->walk($tree);
|
||||||
$sections = implode("\n", $this->sections);
|
$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) : '';
|
$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 = '
|
const BLOCK_VAR = '
|
||||||
$blockFunction = $context->findInBlock(%s);
|
$blockFunction = $context->findInBlock(%s);
|
||||||
if (is_callable($blockFunction)) {
|
if (is_callable($blockFunction)) {
|
||||||
$boundFunction = $blockFunction->bindTo($this, $this);
|
$buffer .= call_user_func($blockFunction, $context);
|
||||||
$boundFunction($context, $buffer);
|
|
||||||
} else {
|
} else {
|
||||||
%s
|
%s
|
||||||
}
|
}
|
||||||
@@ -259,15 +262,12 @@ class Mustache_Compiler
|
|||||||
{
|
{
|
||||||
$id = var_export($id, true);
|
$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 = '
|
const BLOCK_ARG = '
|
||||||
// %s block_arg
|
// %s block_arg
|
||||||
$blockFunction = function(& $context, & $buffer, $indent=\'\') {
|
$newContext[%s] = array($this, \'block%s\');
|
||||||
%s
|
|
||||||
};
|
|
||||||
$newContext[%s] = $blockFunction;
|
|
||||||
';
|
';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -285,10 +285,38 @@ class Mustache_Compiler
|
|||||||
*/
|
*/
|
||||||
private function blockArg($nodes, $id, $start, $end, $otag, $ctag, $level)
|
private function blockArg($nodes, $id, $start, $end, $otag, $ctag, $level)
|
||||||
{
|
{
|
||||||
|
$key = $this->block($nodes);
|
||||||
|
$keystr = var_export($key, true);
|
||||||
$id = var_export($id, 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);
|
$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 = '
|
const SECTION_CALL = '
|
||||||
|
|||||||
@@ -467,6 +467,23 @@ class Mustache_Test_Functional_InheritanceTest extends PHPUnit_Framework_TestCas
|
|||||||
$this->assertEquals('<1><2><3>', $tpl->render($data));
|
$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(
|
||||||
|
'{{<parent}}\n\n\n{{$value}}<{{ . }}>{{#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
|
* @dataProvider getIllegalInheritanceExamples
|
||||||
* @expectedException Mustache_Exception_SyntaxException
|
* @expectedException Mustache_Exception_SyntaxException
|
||||||
|
|||||||
Reference in New Issue
Block a user