diff --git a/README.md b/README.md index 14d2f9f..07e5995 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ Fenom - Template Engine for PHP =============================== -> Composer package: `{"fenom/fenom": "1.*"}`. See on [Packagist.org](https://packagist.org/packages/fenom/fenom) +> Composer package: `{"fenom/fenom": "2.*"}`. See on [Packagist.org](https://packagist.org/packages/fenom/fenom) [![Latest Stable Version](https://poser.pugx.org/fenom/fenom/v/stable.png)](https://packagist.org/packages/fenom/fenom) [![Build Status](https://travis-ci.org/bzick/fenom.png?branch=master)](https://travis-ci.org/bzick/fenom) @@ -12,7 +12,6 @@ Fenom - Template Engine for PHP * Simple [syntax](./docs/syntax.md) * [Fast](./docs/benchmark.md) * [Secure](./docs/settings.md) -* Simple * [Flexible](./docs/ext/extensions.md) * [Lightweight](./docs/benchmark.md#stats) * [Powerful](./docs/readme.md) diff --git a/docs/dev/schema.md b/docs/dev/schema.md new file mode 100644 index 0000000..dfb0cc6 --- /dev/null +++ b/docs/dev/schema.md @@ -0,0 +1,120 @@ +How Fenom works +=============== + +``` + +use Fenom; +use Fenom\Render; +use Fenom\Template; +use Fenom\Tokenizer; + +______________________________ +| | +| Fenom::display($tpl, $var) | +|____________________________| + | + | search the template +______________|___________________________ +| Template loaded into Fenom::$_storage? | +| Fenom::getTemplate($tpl) | +|________________________________________| + | | + | yes | no +______________|__________ | +| Render the template | | +| Render::display($tpl) | | +|_______________________| | + | | + | (hot start) | + | ______________________________|__________________ + | | Template already compiled and stored in cache | + | | Fenom::getTemplate($template) | + | |_______________________________________________| + | | | + | | yes | no + | ____________|_______________ | + | | Load template from cache | not found | + | | Fenom::_load(...) |-------------->| + | |__________________________| | + | | | + | | found | + | ____________|___________ | + | | Validate template | invalid | + | | Render::isValid(...) |------------------>| + | |______________________| | + | | | + | | valid | + | ____________|____________ | + | | Render the template | | + |<----| Render::display(...) | | + | |_______________________| | + | | + | _____________________________ ________|___________________ + | | Initialize compiler | | Compile the template | + | | Template::load($tpl) |<-----| Fenom::compile($tpl) | + | |___________________________| |__________________________| + | | + | ____________|________________ + | | Load template source | + | | Provider::getSource($tpl) | + | |___________________________| + | | + | ____________|______________ + | | Start compilation | + | | Template::compile($tpl) | + | |_________________________| + | | + | ____________|______________ + | | Search template tag | + | | Template::compile($tpl) |<------------------------------------------------------| + | |_________________________| | + | | | | + | | not found | found | + | | _____________|_______________ _______________________________ | + | | | Tokenize the tag's code | | Parse the tag | | + | | | new Tokenizer($tag) |--->| Template::parseTag($tokens) | | + | | |___________________________| |_____________________________| | + | | | | | + | | is tag | | is expression | + | | _______________________________ | _______________|________________ | + | | | Detect tag name | | | Detect expression | | + | | | Template::parseAct($tokens) |<--- | Template::parseAct($tokens) | | + | | | Get callback by tag name | | Parse expression | | + | | | Fenom::getTag($tag_name) | | Template::parseExpr($tokens) | | + | | |_____________________________| |______________________________| | + | | | | | + | | | found | | + | | _______________|_______________ | | + | | | Invoke callback | | | + | | | Template::parseAct($tokens) | | | + | | |_____________________________| | | + | | | | | + | | _______________|________________ | | + | | | Append code to template | | | + | | | Template::_appendCode($code) |<----------------------- | + | | |______________________________| | + | | | | + | | _______________|___________ | + | | | Finalize the tag | starts search next tag | + | | | Template::compile($tpl) |>------------------------------------------------ + | | |_________________________| + | | + | __|___________________________________ + | | Store template to cache | + | | Fenom::compile($tpl) | + | | Store template to Fenom::$_storage | + | | Fenom::getTemplate($tpl) | + | |____________________________________| + | | + | ____________|_____________ + | | Render the template | + | | Template::display(...) | + | |________________________| + | | + | | (cold start) +__|_________|________ +| | +| DONE | +|___________________| + +``` \ No newline at end of file diff --git a/docs/syntax.md b/docs/syntax.md index eb5f5f2..27bf4cd 100644 --- a/docs/syntax.md +++ b/docs/syntax.md @@ -298,17 +298,18 @@ Outputs {/foreach} ``` -### Tag's compile options +### Tag options | name | code | type | description | | ------- | ---- | ----- | ------------ | | strip | s | block | | -| atrim | a | any | | -| raw | r | any | | -| btrim | b | any | | -| ignore | i | block | | +| ltrim | l | any | | +| rtrim | r | any | | | trim | t | any | | +| raw | a | any | | +| escape | e | any | | +| ignore | i | block | | ```smarty -{script:s:a:r:b:i:t} ... {/script} +{script:ignore} ... {/script} ``` \ No newline at end of file diff --git a/src/Fenom.php b/src/Fenom.php index 0d69369..cb6bd13 100644 --- a/src/Fenom.php +++ b/src/Fenom.php @@ -37,7 +37,8 @@ class Fenom const DISABLE_CACHE = 0x400; const FORCE_VERIFY = 0x800; const AUTO_TRIM = 0x1000; // reserved - const DENY_STATICS = 0x2000; // reserved + const DENY_STATICS = 0x2000; + const AUTO_STRIP = 0x4000; // reserved /* @deprecated */ const DENY_INLINE_FUNCS = 0x20; @@ -556,6 +557,7 @@ class Fenom } /** + * Modifier autoloader * @param string $modifier * @param Fenom\Template $template * @return bool @@ -582,7 +584,8 @@ class Fenom } /** - * @param $tag + * Tags autoloader + * @param string $tag * @param Fenom\Template $template * @return bool */ diff --git a/src/Fenom/Compiler.php b/src/Fenom/Compiler.php index 08fce9e..7699e7e 100644 --- a/src/Fenom/Compiler.php +++ b/src/Fenom/Compiler.php @@ -256,7 +256,7 @@ class Compiler $condition = "$var >= {$p['to']}"; if ($p["last"]) $c = "($var + {$p['step']}) < {$p['to']}"; } else { - throw new InvalidUsageException("Invalid step value if {for}"); + throw new InvalidUsageException("Invalid step value"); } } else { $condition = "({$p['step']} > 0 && $var <= {$p['to']} || {$p['step']} < 0 && $var >= {$p['to']})"; @@ -533,7 +533,7 @@ class Compiler if ($name) { $tpl->importBlocks($name); } else { - throw new InvalidUsageException('template name must be given explicitly yet'); + throw new InvalidUsageException('Invalid template name for tag {use}'); } } @@ -551,7 +551,7 @@ class Compiler } $scope["cname"] = $scope->tpl->parsePlainArg($tokens, $name); if (!$name) { - throw new \RuntimeException("Only static names for blocks allowed"); + throw new \RuntimeException("Invalid block name"); } $scope["name"] = $name; $scope["use_parent"] = false; @@ -837,7 +837,7 @@ class Compiler $tpl->parsePlainArg($tokens, $name); if (!$name) { - throw new InvalidUsageException("Invalid usage tag {import}"); + throw new InvalidUsageException("Invalid template name"); } if ($tokens->is(T_AS)) { $alias = $tokens->next()->get(Tokenizer::MACRO_STRING); diff --git a/src/Fenom/Template.php b/src/Fenom/Template.php index 2444481..53d3747 100644 --- a/src/Fenom/Template.php +++ b/src/Fenom/Template.php @@ -274,7 +274,7 @@ class Template extends Render $_tag = substr($tag, 1, -1); // strip delimiters '{' and '}' if ($this->_ignore) { // check ignore - if ($_tag === '/ignore') { // turn off ignore + if ($_tag === '/' . $this->_ignore) { // turn off ignore $this->_ignore = false; } else { // still ignore $this->_appendText($tag); @@ -559,7 +559,7 @@ class Template extends Render try { if ($tokens->is(Tokenizer::MACRO_STRING)) { if ($tokens->current() === "ignore") { - $this->_ignore = true; + $this->_ignore = "ignore"; $tokens->next(); return ''; } else { @@ -596,7 +596,7 @@ class Template extends Render /** @var Scope $scope */ $scope = array_pop($this->_stack); if ($scope->name !== $name) { - throw new TokenizeException("Unexpected closing of the tag '$name' (expecting closing of the tag {$scope->name}, opened on line {$scope->line})"); + throw new TokenizeException("Unexpected closing of the tag '$name' (expecting closing of the tag {$scope->name}, opened in line {$scope->line})"); } if ($scope->is_compiler) { return $scope->close($tokens); @@ -644,12 +644,20 @@ class Template extends Render } else { return $this->out(Compiler::smartFuncParser($static, $tokens, $this)); } + } elseif($tokens->is(':')) { // parse tag options + do { + $tokens->options[ $tokens->next()->need(T_STRING)->getAndNext() ] = true; + } while($tokens->is(':')); } - if ($tag = $this->_fenom->getTag($action, $this)) { // call some function + if ($tag = $this->_fenom->getTag($action, $this)) { + if(isset($tokens->options['ignore']) && ($tag["type"] & Fenom::BLOCK_COMPILER)) { + $this->_ignore = $action; + } switch ($tag["type"]) { case Fenom::BLOCK_COMPILER: $scope = new Scope($action, $this, $this->_line, $tag, count($this->_stack), $this->_body); + $scope->options = &$tokens->options; $code = $scope->open($tokens); if (!$scope->is_closed) { array_push($this->_stack, $scope); @@ -661,6 +669,7 @@ class Template extends Render return $this->out(call_user_func($tag["parser"], $tag["function"], $tokens, $this)); case Fenom::BLOCK_FUNCTION: $scope = new Scope($action, $this, $this->_line, $tag, count($this->_stack), $this->_body); + $scope->options = &$tokens->options; $scope->setFuncName($tag["function"]); array_push($this->_stack, $scope); $scope->escape = $this->escape; diff --git a/tests/cases/Fenom/AutoEscapeTest.php b/tests/cases/Fenom/AutoEscapeTest.php index f8ecc17..de9ef7b 100644 --- a/tests/cases/Fenom/AutoEscapeTest.php +++ b/tests/cases/Fenom/AutoEscapeTest.php @@ -32,26 +32,30 @@ class AutoEscapeTest extends TestCase // inline function array('{test_function text=$html}, {$html}', "$html, $html", $vars, 0), array('{test_function text=$html}, {$html}', "$escaped, $escaped", $vars, \Fenom::AUTO_ESCAPE), - array('{raw:test_function text=$html}, {$html}', "$html, $escaped", $vars, \Fenom::AUTO_ESCAPE), - array('{raw:test_function text="{$html|up}"}, {$html}', strtoupper($html) . ", $escaped", $vars, \Fenom::AUTO_ESCAPE), + array('{test_function:raw text=$html}, {$html}', "$html, $escaped", $vars, \Fenom::AUTO_ESCAPE), + array('{test_function:raw text="{$html|up}"}, {$html}', strtoupper($html) . ", $escaped", $vars, \Fenom::AUTO_ESCAPE), array('{autoescape true}{test_function text=$html}{/autoescape}, {test_function text=$html}', "$escaped, $html", $vars, 0), array('{autoescape false}{test_function text=$html}{/autoescape}, {test_function text=$html}', "$html, $escaped", $vars, \Fenom::AUTO_ESCAPE), array('{autoescape true}{test_function text=$html}{/autoescape}, {test_function text=$html}', "$escaped, $escaped", $vars, \Fenom::AUTO_ESCAPE), array('{autoescape false}{test_function text=$html}{/autoescape}, {test_function text=$html}', "$html, $html", $vars, 0), - array('{autoescape true}{raw:test_function text=$html}{/autoescape}, {test_function text=$html}', "$html, $html", $vars, 0), - array('{autoescape false}{raw:test_function text=$html}{/autoescape}, {test_function text=$html}', "$html, $escaped", $vars, \Fenom::AUTO_ESCAPE), - array('{autoescape true}{raw:test_function text=$html}{/autoescape}, {test_function text=$html}', "$html, $escaped", $vars, \Fenom::AUTO_ESCAPE), - array('{autoescape false}{raw:test_function text=$html}{/autoescape}, {test_function text=$html}', "$html, $html", $vars, 0), + array('{autoescape true}{test_function:raw text=$html}{/autoescape}, {test_function text=$html}', "$html, $html", $vars, 0), + array('{autoescape false}{test_function:raw text=$html}{/autoescape}, {test_function text=$html}', "$html, $escaped", $vars, \Fenom::AUTO_ESCAPE), + array('{autoescape true}{test_function:raw text=$html}{/autoescape}, {test_function text=$html}', "$html, $escaped", $vars, \Fenom::AUTO_ESCAPE), + array('{autoescape false}{test_function:raw text=$html}{/autoescape}, {test_function text=$html}', "$html, $html", $vars, 0), - // block function. Have bugs -// array('{test_block_function}{$html}{/test_block_function}', $html, $vars, 0), -// array('{test_block_function}{$html}{/test_block_function}', $escaped, $vars, \Fenom::AUTO_ESCAPE), -// array('{raw:test_block_function}{$html}{/test_block_function}', $html, $vars, \Fenom::AUTO_ESCAPE), -// array('{raw:test_block_function}{"{$html|up}"}{/test_block_function}', strtoupper($html), $vars, \Fenom::AUTO_ESCAPE), -// array('{autoescape true}{test_block_function}{$html}{/test_block_function}{/autoescape}, {test_block_function}{$html}{/test_block_function}', "$escaped, $html", $vars, 0), -// array('{autoescape false}{test_block_function}{$html}{/test_block_function}{/autoescape}, {test_block_function}{$html}{/test_block_function}', "$html, $escaped", $vars, \Fenom::AUTO_ESCAPE), -// array('{autoescape true}{test_block_function}{$html}{/test_block_function}{/autoescape}, {test_block_function}{$html}{/test_block_function}', "$escaped, $escaped", $vars, \Fenom::AUTO_ESCAPE), -// array('{autoescape false}{test_block_function}{$html}{/test_block_function}{/autoescape}, {test_block_function}{$html}{/test_block_function}', "$html, $html", $vars, 0), + // block function + array('{test_block_function}{$html}{/test_block_function}', $html, $vars, 0), + array('{test_block_function}{$html}{/test_block_function}', $escaped, $vars, \Fenom::AUTO_ESCAPE), + array('{test_block_function:raw}{$html}{/test_block_function}', $html, $vars, \Fenom::AUTO_ESCAPE), + array('{test_block_function:raw}{"{$html|up}"}{/test_block_function}', strtoupper($html), $vars, \Fenom::AUTO_ESCAPE), + array('{autoescape true}{test_block_function}{$html}{/test_block_function}{/autoescape}, {test_block_function}{$html}{/test_block_function}', "$escaped, $html", $vars, 0), + array('{autoescape false}{test_block_function}{$html}{/test_block_function}{/autoescape}, {test_block_function}{$html}{/test_block_function}', "$html, $escaped", $vars, \Fenom::AUTO_ESCAPE), + array('{autoescape true}{test_block_function}{$html}{/test_block_function}{/autoescape}, {test_block_function}{$html}{/test_block_function}', "$escaped, $escaped", $vars, \Fenom::AUTO_ESCAPE), + array('{autoescape false}{test_block_function}{$html}{/test_block_function}{/autoescape}, {test_block_function}{$html}{/test_block_function}', "$html, $html", $vars, 0), + array('{autoescape true}{test_block_function:raw}{$html}{/test_block_function}{/autoescape}, {test_block_function}{$html}{/test_block_function}', "$escaped, $html", $vars, 0), + array('{autoescape false}{test_block_function:raw}{$html}{/test_block_function}{/autoescape}, {test_block_function}{$html}{/test_block_function}', "$html, $escaped", $vars, \Fenom::AUTO_ESCAPE), + array('{autoescape true}{test_block_function:raw}{$html}{/test_block_function}{/autoescape}, {test_block_function}{$html}{/test_block_function}', "$escaped, $escaped", $vars, \Fenom::AUTO_ESCAPE), + array('{autoescape false}{test_block_function:raw}{$html}{/test_block_function}{/autoescape}, {test_block_function}{$html}{/test_block_function}', "$html, $html", $vars, 0), ); } diff --git a/tests/cases/Fenom/ExtendsTest.php b/tests/cases/Fenom/ExtendsTest.php index 1b1a950..006783f 100644 --- a/tests/cases/Fenom/ExtendsTest.php +++ b/tests/cases/Fenom/ExtendsTest.php @@ -18,6 +18,17 @@ class ExtendsTest extends TestCase exit; } + public static function providerExtendsInvalid() + { + return array( + array('{extends "extends/dynamic/child.3.tpl"} {extends "extends/dynamic/child.3.tpl"}', 'Fenom\Error\CompileException', "Only one {extends} allowed"), + array('{if true}{extends "extends/dynamic/child.3.tpl"}{/if}', 'Fenom\Error\CompileException', "Tag {extends} can not be nested"), + array('{if true}{use "extends/dynamic/use.tpl"}{/if}', 'Fenom\Error\CompileException', "Tag {use} can not be nested"), + array('{use $use_this}', 'Fenom\Error\CompileException', "Invalid template name for tag {use}"), + array('{block $use_this}{/block}', 'Fenom\Error\CompileException', "Invalid block name"), + ); + } + public function testAutoExtendsManual() { $child = $this->fenom->getRawTemplate()->load('extends/auto/child.1.tpl', false); @@ -132,5 +143,14 @@ Before footer Footer from use"; $this->assertSame($result, $this->fenom->fetch('extends/dynamic/child.4.tpl', array())); } + + /** + * @group static-invalid + * @dataProvider providerExtendsInvalid + */ + public function testExtendsInvalid($code, $exception, $message, $options = 0) + { + $this->execError($code, $exception, $message, $options); + } } diff --git a/tests/cases/Fenom/TemplateTest.php b/tests/cases/Fenom/TemplateTest.php index 44afc89..439b955 100644 --- a/tests/cases/Fenom/TemplateTest.php +++ b/tests/cases/Fenom/TemplateTest.php @@ -651,6 +651,7 @@ class TemplateTest extends TestCase array('For: {for first=$i $a=3 to=6} block1 {/for} end', 'Fenom\Error\CompileException', "Unexpected token 'first'"), array('For: {for last=$i $a=3 to=6} block1 {/for} end', 'Fenom\Error\CompileException', "Unexpected token 'last'"), array('For: {for $a=4 to=6 unk=4} block1 {/for} end', 'Fenom\Error\CompileException', "Unknown parameter 'unk'"), + array('For: {for $a=4 to=6 step=0} block1 {/for} end', 'Fenom\Error\CompileException', "Invalid step value"), array('For: {for $a=4 to=6} $a: {$a}, {forelse} {break} {/for} end', 'Fenom\Error\CompileException', "Improper usage of the tag {break}"), array('For: {for $a=4 to=6} $a: {$a}, {forelse} {continue} {/for} end', 'Fenom\Error\CompileException', "Improper usage of the tag {continue}"), ); @@ -847,7 +848,7 @@ class TemplateTest extends TestCase public function _testSandbox() { try { - var_dump($this->fenom->compileCode('{Fenom\TemplateTest::multi(3,4)}')->getBody()); + var_dump($this->fenom->compileCode('{var:ignore $a} value {/var}')->getBody()); } catch (\Exception $e) { print_r($e->getMessage() . "\n" . $e->getTraceAsString()); while ($e->getPrevious()) {