Dev flags

This commit is contained in:
Ivan Shalganov 2014-04-09 18:03:49 +04:00
parent 61816e76bc
commit 777e315dce
9 changed files with 191 additions and 34 deletions

View File

@ -1,7 +1,7 @@
Fenom - Template Engine for PHP 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) [![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) [![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) * Simple [syntax](./docs/syntax.md)
* [Fast](./docs/benchmark.md) * [Fast](./docs/benchmark.md)
* [Secure](./docs/settings.md) * [Secure](./docs/settings.md)
* Simple
* [Flexible](./docs/ext/extensions.md) * [Flexible](./docs/ext/extensions.md)
* [Lightweight](./docs/benchmark.md#stats) * [Lightweight](./docs/benchmark.md#stats)
* [Powerful](./docs/readme.md) * [Powerful](./docs/readme.md)

120
docs/dev/schema.md Normal file
View File

@ -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 |
|___________________|
```

View File

@ -298,17 +298,18 @@ Outputs
{/foreach} {/foreach}
``` ```
### Tag's compile options ### Tag options
| name | code | type | description | | name | code | type | description |
| ------- | ---- | ----- | ------------ | | ------- | ---- | ----- | ------------ |
| strip | s | block | | | strip | s | block | |
| atrim | a | any | | | ltrim | l | any | |
| raw | r | any | | | rtrim | r | any | |
| btrim | b | any | |
| ignore | i | block | |
| trim | t | any | | | trim | t | any | |
| raw | a | any | |
| escape | e | any | |
| ignore | i | block | |
```smarty ```smarty
{script:s:a:r:b:i:t} ... {/script} {script:ignore} ... {/script}
``` ```

View File

@ -37,7 +37,8 @@ class Fenom
const DISABLE_CACHE = 0x400; const DISABLE_CACHE = 0x400;
const FORCE_VERIFY = 0x800; const FORCE_VERIFY = 0x800;
const AUTO_TRIM = 0x1000; // reserved const AUTO_TRIM = 0x1000; // reserved
const DENY_STATICS = 0x2000; // reserved const DENY_STATICS = 0x2000;
const AUTO_STRIP = 0x4000; // reserved
/* @deprecated */ /* @deprecated */
const DENY_INLINE_FUNCS = 0x20; const DENY_INLINE_FUNCS = 0x20;
@ -556,6 +557,7 @@ class Fenom
} }
/** /**
* Modifier autoloader
* @param string $modifier * @param string $modifier
* @param Fenom\Template $template * @param Fenom\Template $template
* @return bool * @return bool
@ -582,7 +584,8 @@ class Fenom
} }
/** /**
* @param $tag * Tags autoloader
* @param string $tag
* @param Fenom\Template $template * @param Fenom\Template $template
* @return bool * @return bool
*/ */

View File

@ -256,7 +256,7 @@ class Compiler
$condition = "$var >= {$p['to']}"; $condition = "$var >= {$p['to']}";
if ($p["last"]) $c = "($var + {$p['step']}) < {$p['to']}"; if ($p["last"]) $c = "($var + {$p['step']}) < {$p['to']}";
} else { } else {
throw new InvalidUsageException("Invalid step value if {for}"); throw new InvalidUsageException("Invalid step value");
} }
} else { } else {
$condition = "({$p['step']} > 0 && $var <= {$p['to']} || {$p['step']} < 0 && $var >= {$p['to']})"; $condition = "({$p['step']} > 0 && $var <= {$p['to']} || {$p['step']} < 0 && $var >= {$p['to']})";
@ -533,7 +533,7 @@ class Compiler
if ($name) { if ($name) {
$tpl->importBlocks($name); $tpl->importBlocks($name);
} else { } 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); $scope["cname"] = $scope->tpl->parsePlainArg($tokens, $name);
if (!$name) { if (!$name) {
throw new \RuntimeException("Only static names for blocks allowed"); throw new \RuntimeException("Invalid block name");
} }
$scope["name"] = $name; $scope["name"] = $name;
$scope["use_parent"] = false; $scope["use_parent"] = false;
@ -837,7 +837,7 @@ class Compiler
$tpl->parsePlainArg($tokens, $name); $tpl->parsePlainArg($tokens, $name);
if (!$name) { if (!$name) {
throw new InvalidUsageException("Invalid usage tag {import}"); throw new InvalidUsageException("Invalid template name");
} }
if ($tokens->is(T_AS)) { if ($tokens->is(T_AS)) {
$alias = $tokens->next()->get(Tokenizer::MACRO_STRING); $alias = $tokens->next()->get(Tokenizer::MACRO_STRING);

View File

@ -274,7 +274,7 @@ class Template extends Render
$_tag = substr($tag, 1, -1); // strip delimiters '{' and '}' $_tag = substr($tag, 1, -1); // strip delimiters '{' and '}'
if ($this->_ignore) { // check ignore if ($this->_ignore) { // check ignore
if ($_tag === '/ignore') { // turn off ignore if ($_tag === '/' . $this->_ignore) { // turn off ignore
$this->_ignore = false; $this->_ignore = false;
} else { // still ignore } else { // still ignore
$this->_appendText($tag); $this->_appendText($tag);
@ -559,7 +559,7 @@ class Template extends Render
try { try {
if ($tokens->is(Tokenizer::MACRO_STRING)) { if ($tokens->is(Tokenizer::MACRO_STRING)) {
if ($tokens->current() === "ignore") { if ($tokens->current() === "ignore") {
$this->_ignore = true; $this->_ignore = "ignore";
$tokens->next(); $tokens->next();
return ''; return '';
} else { } else {
@ -596,7 +596,7 @@ class Template extends Render
/** @var Scope $scope */ /** @var Scope $scope */
$scope = array_pop($this->_stack); $scope = array_pop($this->_stack);
if ($scope->name !== $name) { 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) { if ($scope->is_compiler) {
return $scope->close($tokens); return $scope->close($tokens);
@ -644,12 +644,20 @@ class Template extends Render
} else { } else {
return $this->out(Compiler::smartFuncParser($static, $tokens, $this)); 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"]) { switch ($tag["type"]) {
case Fenom::BLOCK_COMPILER: case Fenom::BLOCK_COMPILER:
$scope = new Scope($action, $this, $this->_line, $tag, count($this->_stack), $this->_body); $scope = new Scope($action, $this, $this->_line, $tag, count($this->_stack), $this->_body);
$scope->options = &$tokens->options;
$code = $scope->open($tokens); $code = $scope->open($tokens);
if (!$scope->is_closed) { if (!$scope->is_closed) {
array_push($this->_stack, $scope); 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)); return $this->out(call_user_func($tag["parser"], $tag["function"], $tokens, $this));
case Fenom::BLOCK_FUNCTION: case Fenom::BLOCK_FUNCTION:
$scope = new Scope($action, $this, $this->_line, $tag, count($this->_stack), $this->_body); $scope = new Scope($action, $this, $this->_line, $tag, count($this->_stack), $this->_body);
$scope->options = &$tokens->options;
$scope->setFuncName($tag["function"]); $scope->setFuncName($tag["function"]);
array_push($this->_stack, $scope); array_push($this->_stack, $scope);
$scope->escape = $this->escape; $scope->escape = $this->escape;

View File

@ -32,26 +32,30 @@ class AutoEscapeTest extends TestCase
// inline function // inline function
array('{test_function text=$html}, {$html}', "$html, $html", $vars, 0), array('{test_function text=$html}, {$html}', "$html, $html", $vars, 0),
array('{test_function text=$html}, {$html}', "$escaped, $escaped", $vars, \Fenom::AUTO_ESCAPE), 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('{test_function:raw 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|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 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 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 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 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 true}{test_function:raw 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 false}{test_function:raw 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 true}{test_function:raw 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 false}{test_function:raw text=$html}{/autoescape}, {test_function text=$html}', "$html, $html", $vars, 0),
// block function. Have bugs // block function
// array('{test_block_function}{$html}{/test_block_function}', $html, $vars, 0), 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}{$html}{/test_block_function}', $escaped, $vars, \Fenom::AUTO_ESCAPE),
// array('{raw:test_block_function}{$html}{/test_block_function}', $html, $vars, \Fenom::AUTO_ESCAPE), array('{test_block_function:raw}{$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('{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 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 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 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 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),
); );
} }

View File

@ -18,6 +18,17 @@ class ExtendsTest extends TestCase
exit; 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() public function testAutoExtendsManual()
{ {
$child = $this->fenom->getRawTemplate()->load('extends/auto/child.1.tpl', false); $child = $this->fenom->getRawTemplate()->load('extends/auto/child.1.tpl', false);
@ -132,5 +143,14 @@ Before footer
Footer from use"; Footer from use";
$this->assertSame($result, $this->fenom->fetch('extends/dynamic/child.4.tpl', array())); $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);
}
} }

View File

@ -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 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 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 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} {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}"), 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() public function _testSandbox()
{ {
try { 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) { } catch (\Exception $e) {
print_r($e->getMessage() . "\n" . $e->getTraceAsString()); print_r($e->getMessage() . "\n" . $e->getTraceAsString());
while ($e->getPrevious()) { while ($e->getPrevious()) {