mirror of
https://github.com/fenom-template/fenom.git
synced 2023-08-10 21:13:07 +03:00
Done #66, dev dynamic extends
This commit is contained in:
parent
3adafb6fcc
commit
a523d4a4aa
@ -253,10 +253,13 @@ Tags starts with name and may have attributes
|
||||
e.innerHTML = text;
|
||||
document.body.appendChild(e);
|
||||
})('test');
|
||||
</ignore>
|
||||
</script>
|
||||
{if:ignore $js_enabled}
|
||||
|
||||
{/if}
|
||||
```
|
||||
|
||||
Выведет
|
||||
Outputs
|
||||
|
||||
```html
|
||||
<style>
|
||||
@ -277,9 +280,11 @@ Tags starts with name and may have attributes
|
||||
|
||||
```smarty
|
||||
{include 'control.tpl'
|
||||
options = $list
|
||||
name = $cp.name
|
||||
type = 'select'
|
||||
$options = $list
|
||||
$name = $cp.name
|
||||
$type = 'select'
|
||||
isolate = true
|
||||
disable_static = true
|
||||
}
|
||||
|
||||
{foreach [
|
||||
@ -291,4 +296,19 @@ Tags starts with name and may have attributes
|
||||
{$key}: {$val}
|
||||
|
||||
{/foreach}
|
||||
```
|
||||
|
||||
### Tag's compile options
|
||||
|
||||
| name | code | type | description |
|
||||
| ------- | ---- | ----- | ------------ |
|
||||
| strip | s | block | |
|
||||
| atrim | a | any | |
|
||||
| raw | r | any | |
|
||||
| btrim | b | any | |
|
||||
| ignore | i | block | |
|
||||
| trim | t | any | |
|
||||
|
||||
```smarty
|
||||
{script:s:a:r:b:i:t} ... {/script}
|
||||
```
|
@ -466,75 +466,46 @@ class Compiler
|
||||
* Dispatch {extends} tag
|
||||
* @param Tokenizer $tokens
|
||||
* @param Template $tpl
|
||||
* @throws InvalidUsageException
|
||||
* @throws \RuntimeException
|
||||
* @throws Error\InvalidUsageException
|
||||
* @return string
|
||||
*/
|
||||
public static function tagExtends(Tokenizer $tokens, Template $tpl)
|
||||
{
|
||||
if (!empty($tpl->_extends)) {
|
||||
if ($tpl->extends) {
|
||||
throw new InvalidUsageException("Only one {extends} allowed");
|
||||
} elseif ($tpl->getStackSize()) {
|
||||
throw new InvalidUsageException("Tags {extends} can not be nested");
|
||||
}
|
||||
$tpl_name = $tpl->parsePlainArg($tokens, $name);
|
||||
if (empty($tpl->_extended)) {
|
||||
$cname = $tpl->parsePlainArg($tokens, $name);
|
||||
if($name) {
|
||||
$tpl->extends = $name;
|
||||
} else {
|
||||
$tpl->dynamic_extends = $cname;
|
||||
}
|
||||
if(!$tpl->extended) {
|
||||
$tpl->addPostCompile(__CLASS__ . "::extendBody");
|
||||
}
|
||||
if ($tpl->getOptions() & Template::DYNAMIC_EXTEND) {
|
||||
$tpl->_compatible = true;
|
||||
}
|
||||
if ($name) { // static extends
|
||||
$tpl->_extends = $tpl->getStorage()->getRawTemplate()->load($name, false);
|
||||
if (!isset($tpl->_compatible)) {
|
||||
$tpl->_compatible = & $tpl->_extends->_compatible;
|
||||
}
|
||||
$tpl->addDepend($tpl->_extends);
|
||||
return "";
|
||||
} else { // dynamic extends
|
||||
if (!isset($tpl->_compatible)) {
|
||||
$tpl->_compatible = true;
|
||||
}
|
||||
$tpl->_extends = $tpl_name;
|
||||
return '$parent = $tpl->getStorage()->getTemplate(' . $tpl_name . ', \Fenom\Template::EXTENDED);';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Post compile action for {extends ...} tag
|
||||
* @param string $body
|
||||
* @param Template $tpl
|
||||
* @param string $body
|
||||
*/
|
||||
public static function extendBody(&$body, $tpl)
|
||||
public static function extendBody($tpl, &$body)
|
||||
{
|
||||
$t = $tpl;
|
||||
if ($tpl->uses) {
|
||||
$tpl->blocks += $tpl->uses;
|
||||
}
|
||||
while (isset($t->_extends)) {
|
||||
$t = $t->_extends;
|
||||
if (is_object($t)) {
|
||||
/* @var \Fenom\Template $t */
|
||||
$t->_extended = true;
|
||||
$tpl->addDepend($t);
|
||||
$t->_compatible = & $tpl->_compatible;
|
||||
$t->blocks = & $tpl->blocks;
|
||||
$t->compile();
|
||||
if ($t->uses) {
|
||||
$tpl->blocks += $t->uses;
|
||||
}
|
||||
if (!isset($t->_extends)) { // last item => parent
|
||||
if (empty($tpl->_compatible)) {
|
||||
$body = $t->getBody();
|
||||
} else {
|
||||
$body = '<?php ob_start(); ?>' . $body . '<?php ob_end_clean(); ?>' . $t->getBody();
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
$body .= $t->getBody();
|
||||
}
|
||||
} else {
|
||||
$body = '<?php ob_start(); ?>' . $body . '<?php ob_end_clean(); $parent->b = &$tpl->b; $parent->display((array)$tpl); unset($tpl->b, $parent->b); ?>';
|
||||
return;
|
||||
if($tpl->dynamic_extends) {
|
||||
$body = "";
|
||||
foreach($tpl->blocks as $name => $block) {
|
||||
$body .= '<?php $tpl->blocks["'.$name.'"] = function ($var, $tpl) { ?>'.PHP_EOL.$block['block'].'<?php } ?>'.PHP_EOL.PHP_EOL;
|
||||
}
|
||||
$body .= '<?php $tpl->getStorage()->getTemplate('.$tpl->dynamic_extends.', \Fenom\Template::DYNAMIC_EXTEND)->display($var); ?>';
|
||||
} else {
|
||||
$child = $tpl;
|
||||
while($child && $child->extends) {
|
||||
$parent = $tpl->extend($child->extends);
|
||||
$child = $parent->extends ? $parent : false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -587,6 +558,7 @@ class Compiler
|
||||
{
|
||||
$tpl = $scope->tpl;
|
||||
$name = $scope["name"];
|
||||
|
||||
if(isset($tpl->blocks[$name])) { // block defined
|
||||
$block = &$tpl->blocks[$name];
|
||||
if($block['use_parent']) {
|
||||
@ -602,6 +574,7 @@ class Compiler
|
||||
$scope->replaceContent($block["block"]);
|
||||
}
|
||||
}
|
||||
|
||||
$tpl->blocks[$scope["name"]] = [
|
||||
"from" => $tpl->getName(),
|
||||
"import" => false,
|
||||
|
@ -27,6 +27,7 @@ class Scope extends \ArrayObject
|
||||
public $is_compiler = true;
|
||||
public $is_closed = false;
|
||||
public $escape = false;
|
||||
public $options = [];
|
||||
private $_action;
|
||||
private $_body;
|
||||
private $_offset;
|
||||
|
@ -55,9 +55,9 @@ class Template extends Render
|
||||
*/
|
||||
public $blocks = array();
|
||||
|
||||
public $uses = array();
|
||||
// public $uses = array();
|
||||
|
||||
public $parents = array();
|
||||
// public $parents = array();
|
||||
|
||||
/**
|
||||
* Escape outputs value
|
||||
@ -65,9 +65,16 @@ class Template extends Render
|
||||
*/
|
||||
public $escape = false;
|
||||
|
||||
public $_extends;
|
||||
public $_extended = false;
|
||||
public $_compatible;
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
public $extends;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
public $extended;
|
||||
// public $_compatible;
|
||||
|
||||
/**
|
||||
* Template PHP code
|
||||
@ -221,7 +228,7 @@ class Template extends Render
|
||||
$end = $pos = 0;
|
||||
$this->escape = $this->_options & Fenom::AUTO_ESCAPE;
|
||||
foreach ($this->_fenom->getPreFilters() as $filter) {
|
||||
$this->_src = call_user_func($filter, $this->_src, $this);
|
||||
$this->_src = call_user_func($filter, $this, $this->_src);
|
||||
}
|
||||
|
||||
while (($start = strpos($this->_src, '{', $pos)) !== false) { // search open-symbol of tags
|
||||
@ -303,12 +310,12 @@ class Template extends Render
|
||||
$this->_src = ""; // cleanup
|
||||
if ($this->_post) {
|
||||
foreach ($this->_post as $cb) {
|
||||
call_user_func_array($cb, array(&$this->_body, $this));
|
||||
call_user_func_array($cb, array($this, &$this->_body));
|
||||
}
|
||||
}
|
||||
$this->addDepend($this); // for 'verify' performance
|
||||
foreach ($this->_fenom->getPostFilters() as $filter) {
|
||||
$this->_body = call_user_func($filter, $this->_body, $this);
|
||||
$this->_body = call_user_func($filter, $this, $this->_body);
|
||||
}
|
||||
}
|
||||
|
||||
@ -342,7 +349,7 @@ class Template extends Render
|
||||
if ($this->_filters) {
|
||||
if (strpos($text, "<?") === false) {
|
||||
foreach ($this->_filters as $filter) {
|
||||
$text = call_user_func($filter, $text, $this);
|
||||
$text = call_user_func($filter, $this, $text);
|
||||
}
|
||||
$this->_body .= $text;
|
||||
} else {
|
||||
@ -350,7 +357,7 @@ class Template extends Render
|
||||
foreach ($fragments as &$fragment) {
|
||||
if ($fragment) {
|
||||
foreach ($this->_filters as $filter) {
|
||||
$fragment = call_user_func($filter, $fragment, $this);
|
||||
$fragment = call_user_func($filter, $this, $fragment);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -508,16 +515,21 @@ class Template extends Render
|
||||
/**
|
||||
* Extends the template
|
||||
* @param string $tpl
|
||||
* @return \Fenom\Template parent
|
||||
*/
|
||||
public function extend($tpl) {
|
||||
$this->compile();
|
||||
if(!$this->_body) {
|
||||
$this->compile();
|
||||
}
|
||||
$parent = $this->_fenom->getRawTemplate()->load($tpl, false);
|
||||
$parent->blocks = &$this->blocks;
|
||||
$parent->extended = $this->getName();
|
||||
$parent->_options = $this->_options;
|
||||
$parent->compile();
|
||||
$this->_body = $parent->_body;
|
||||
$this->_src = $parent->_src;
|
||||
$this->addDepend($parent);
|
||||
return $parent;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -84,6 +84,7 @@ class Tokenizer
|
||||
public $tokens;
|
||||
public $p = 0;
|
||||
public $quotes = 0;
|
||||
public $options;
|
||||
private $_max = 0;
|
||||
private $_last_no = 0;
|
||||
|
||||
|
@ -26,4 +26,14 @@ function dump()
|
||||
fwrite(STDERR, "DUMP: " . call_user_func("print_r", $arg, true) . "\n");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function dumpt()
|
||||
{
|
||||
foreach (func_get_args() as $arg) {
|
||||
fwrite(STDERR, "DUMP: " . call_user_func("print_r", $arg, true) . "\n");
|
||||
|
||||
}
|
||||
$e = new Exception();
|
||||
echo "-------\nDump trace: \n" . $e->getTraceAsString() . "\n";
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
namespace Fenom;
|
||||
use Fenom, Fenom\TestCase;
|
||||
|
||||
class ExtendsTemplateTest extends TestCase
|
||||
class ExtendsTemplateTest_ extends TestCase
|
||||
{
|
||||
|
||||
public function _testSandbox()
|
||||
|
@ -10,12 +10,8 @@ class ExtendsTest extends TestCase
|
||||
public function _testSandbox()
|
||||
{
|
||||
try {
|
||||
var_dump($this->fenom->getTemplate([
|
||||
'autoextends/child.3.tpl',
|
||||
'autoextends/child.2.tpl',
|
||||
'autoextends/child.1.tpl',
|
||||
'autoextends/parent.tpl',
|
||||
])->getBody());
|
||||
var_dump($this->fenom->getTemplate("extends/static/nested/child.1.tpl")->fetch([]));
|
||||
// var_dump($this->fenom->getTemplate("extends/dynamic/child.2.tpl")->getBody());
|
||||
// var_dump($this->fenom->compileCode('{block "main"}a {parent} b{/block}')->getBody());
|
||||
// $child = $this->fenom->getRawTemplate()->load('autoextends/child.1.tpl', false);
|
||||
// $child->extend('autoextends/parent.tpl');
|
||||
@ -27,10 +23,10 @@ class ExtendsTest extends TestCase
|
||||
exit;
|
||||
}
|
||||
|
||||
public function testManualExtends()
|
||||
public function testAutoExtendsManual()
|
||||
{
|
||||
$child = $this->fenom->getRawTemplate()->load('autoextends/child.1.tpl', false);
|
||||
$child->extend('autoextends/parent.tpl');
|
||||
$child = $this->fenom->getRawTemplate()->load('extends/auto/child.1.tpl', false);
|
||||
$child->extend('extends/auto/parent.tpl');
|
||||
$child->compile();
|
||||
$result = "Before header
|
||||
Content of the header
|
||||
@ -52,12 +48,53 @@ Child 3 content
|
||||
Before footer
|
||||
Footer from use";
|
||||
$this->assertSame($result, $this->fenom->fetch([
|
||||
'autoextends/child.3.tpl',
|
||||
'autoextends/child.2.tpl',
|
||||
'autoextends/child.1.tpl',
|
||||
'autoextends/parent.tpl',
|
||||
'extends/auto/child.3.tpl',
|
||||
'extends/auto/child.2.tpl',
|
||||
'extends/auto/child.1.tpl',
|
||||
'extends/auto/parent.tpl',
|
||||
], array()));
|
||||
}
|
||||
|
||||
public function testStaticExtendLevel1() {
|
||||
$result = "Before header
|
||||
Content of the header
|
||||
Before body
|
||||
Child 1 Body
|
||||
Before footer
|
||||
Content of the footer";
|
||||
$this->assertSame($result, $this->fenom->fetch('extends/static/child.1.tpl', array()));
|
||||
}
|
||||
|
||||
public function testStaticExtendLevel3() {
|
||||
$result = "Before header
|
||||
Child 2 header
|
||||
Before body
|
||||
Child 3 content
|
||||
Before footer
|
||||
Footer from use";
|
||||
$this->assertSame($result, $this->fenom->fetch('extends/static/child.3.tpl', array()));
|
||||
}
|
||||
|
||||
public function testStaticExtendNested() {
|
||||
$result = "Before body
|
||||
|
||||
Before header
|
||||
Child 1: Content of the header
|
||||
Before footer
|
||||
Content of the footer
|
||||
";
|
||||
$this->assertSame($result, $this->fenom->fetch('extends/static/nested/child.1.tpl', array()));
|
||||
}
|
||||
|
||||
public function _testDynamicExtendLevel2() {
|
||||
$result = "Before header
|
||||
Content of the header
|
||||
Before body
|
||||
Child 1 Body
|
||||
Before footer
|
||||
Content of the footer";
|
||||
$this->assertSame($result, $this->fenom->fetch('extends/dynamic/child.2.tpl', array()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -144,17 +144,17 @@ class FenomTest extends \Fenom\TestCase
|
||||
public function testFilter()
|
||||
{
|
||||
$punit = $this;
|
||||
$this->fenom->addPreFilter(function ($src, $tpl) use ($punit) {
|
||||
$this->fenom->addPreFilter(function ($tpl, $src) use ($punit) {
|
||||
$punit->assertInstanceOf('Fenom\Template', $tpl);
|
||||
return "== $src ==";
|
||||
});
|
||||
|
||||
$this->fenom->addPostFilter(function ($code, $tpl) use ($punit) {
|
||||
$this->fenom->addPostFilter(function ($tpl, $code) use ($punit) {
|
||||
$punit->assertInstanceOf('Fenom\Template', $tpl);
|
||||
return "+++ $code +++";
|
||||
});
|
||||
|
||||
$this->fenom->addFilter(function ($text, $tpl) use ($punit) {
|
||||
$this->fenom->addFilter(function ($tpl, $text) use ($punit) {
|
||||
$punit->assertInstanceOf('Fenom\Template', $tpl);
|
||||
return "|--- $text ---|";
|
||||
});
|
||||
|
@ -1,3 +1,3 @@
|
||||
|
||||
{use 'autoextends/use.tpl'}
|
||||
{use 'extends/auto/use.tpl'}
|
||||
{block 'header'}Child 2 header{/block}
|
2
tests/resources/provider/extends/dynamic/child.1.tpl
Normal file
2
tests/resources/provider/extends/dynamic/child.1.tpl
Normal file
@ -0,0 +1,2 @@
|
||||
{extends 'extends/dynamic/parent.tpl'}
|
||||
{block 'body'}Child 1 {parent}{/block}
|
4
tests/resources/provider/extends/dynamic/child.2.tpl
Normal file
4
tests/resources/provider/extends/dynamic/child.2.tpl
Normal file
@ -0,0 +1,4 @@
|
||||
{var $no = 1}
|
||||
{extends "extends/dynamic/child.{$no}.tpl"}
|
||||
{use 'extends/dynamic/use.tpl'}
|
||||
{block 'header'}Child 2 header{/block}
|
2
tests/resources/provider/extends/dynamic/child.3.tpl
Normal file
2
tests/resources/provider/extends/dynamic/child.3.tpl
Normal file
@ -0,0 +1,2 @@
|
||||
{extends 'extends/dynamic/child.2.tpl'}
|
||||
{block 'body'}Child 3 content{/block}
|
2
tests/resources/provider/extends/dynamic/use.tpl
Normal file
2
tests/resources/provider/extends/dynamic/use.tpl
Normal file
@ -0,0 +1,2 @@
|
||||
{block 'footer'}Footer from use{/block}
|
||||
{block 'header'}Header from use{/block}
|
2
tests/resources/provider/extends/static/child.1.tpl
Normal file
2
tests/resources/provider/extends/static/child.1.tpl
Normal file
@ -0,0 +1,2 @@
|
||||
{extends 'extends/static/parent.tpl'}
|
||||
{block 'body'}Child 1 {parent}{/block}
|
3
tests/resources/provider/extends/static/child.2.tpl
Normal file
3
tests/resources/provider/extends/static/child.2.tpl
Normal file
@ -0,0 +1,3 @@
|
||||
{extends 'extends/static/child.1.tpl'}
|
||||
{use 'extends/static/use.tpl'}
|
||||
{block 'header'}Child 2 header{/block}
|
2
tests/resources/provider/extends/static/child.3.tpl
Normal file
2
tests/resources/provider/extends/static/child.3.tpl
Normal file
@ -0,0 +1,2 @@
|
||||
{extends 'extends/static/child.2.tpl'}
|
||||
{block 'body'}Child 3 content{/block}
|
@ -0,0 +1,2 @@
|
||||
{extends 'extends/static/nested/parent.tpl'}
|
||||
{block 'header'}Child 1: {parent}{/block}
|
@ -0,0 +1,7 @@
|
||||
Before body
|
||||
{block 'body'}
|
||||
Before header
|
||||
{block 'header'}Content of the header{/block}
|
||||
Before footer
|
||||
{block 'footer'}Content of the footer{/block}
|
||||
{/block}
|
6
tests/resources/provider/extends/static/parent.tpl
Normal file
6
tests/resources/provider/extends/static/parent.tpl
Normal file
@ -0,0 +1,6 @@
|
||||
Before header
|
||||
{block 'header'}Content of the header{/block}
|
||||
Before body
|
||||
{block 'body'}Body{/block}
|
||||
Before footer
|
||||
{block 'footer'}Content of the footer{/block}
|
2
tests/resources/provider/extends/static/use.tpl
Normal file
2
tests/resources/provider/extends/static/use.tpl
Normal file
@ -0,0 +1,2 @@
|
||||
{block 'footer'}Footer from use{/block}
|
||||
{block 'header'}Header from use{/block}
|
@ -1,2 +0,0 @@
|
||||
|
||||
{block 'body'}Child 1 content{/block}
|
@ -1,2 +0,0 @@
|
||||
|
||||
{block 'header'}Child 2 header{/block}
|
@ -1,2 +0,0 @@
|
||||
|
||||
{block 'body'}Child 3 content{/block}
|
Loading…
Reference in New Issue
Block a user