From 8030c1292392562af9c9a8ce660635aeb965cc8b Mon Sep 17 00:00:00 2001 From: bzick Date: Wed, 24 Jul 2013 16:16:20 +0400 Subject: [PATCH 01/18] =?UTF-8?q?Add=20{unset=20=E2=80=A6}=20(#29)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Fenom.php | 4 ++++ src/Fenom/Compiler.php | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/src/Fenom.php b/src/Fenom.php index 4334b33..c0eb7bf 100644 --- a/src/Fenom.php +++ b/src/Fenom.php @@ -228,6 +228,10 @@ class Fenom { 'type' => self::BLOCK_COMPILER, 'open' => 'Fenom\Compiler::autoescapeOpen', 'close' => 'Fenom\Compiler::autoescapeClose' + ), + 'unset' => array( + 'type' => self::INLINE_COMPILER, + 'parser' => 'Fenom\Compiler::tagUnset' ) ); diff --git a/src/Fenom/Compiler.php b/src/Fenom/Compiler.php index d125856..30e390a 100644 --- a/src/Fenom/Compiler.php +++ b/src/Fenom/Compiler.php @@ -914,4 +914,23 @@ class Compiler { public static function autoescapeClose(Tokenizer $tokens, Scope $scope) { $scope->tpl->escape = $scope["escape"]; } + + /** + * Unset present variables + * + * @param Tokenizer $tokens + * @param Template $tpl + * @return string + * @throws InvalidUsageException + */ + public static function tagUnset(Tokenizer $tokens, Template $tpl) { + $vars = array(); + while($tokens->valid()) { + $vars[] = $tpl->parseVar($tokens); + } + if(!$vars) { + throw new InvalidUsageException("Unset must accept variable(s)"); + } + return 'unset('.implode(', ', $vars).')'; + } } From d5dcfbe410ec67c5a82d9745643c52f9006d6745 Mon Sep 17 00:00:00 2001 From: bzick Date: Wed, 24 Jul 2013 19:37:07 +0400 Subject: [PATCH 02/18] Add hook for loads modifiers and tags --- CHANGELOG.md | 5 +++ src/Fenom.php | 60 +++++++++++++++++++++++------- src/Fenom/Compiler.php | 2 +- src/Fenom/Template.php | 29 +++++++++------ tests/autoload.php | 2 + tests/cases/Fenom/TemplateTest.php | 2 +- 6 files changed, 72 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d3a9d83..5ad40e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +## 1.2.0 + +- Feature #29: add {unset} tag +- Add hook for loading modifiers and tags + ## 1.1.0 - Bug #19: Bug with "if" expressions starting with "(" diff --git a/src/Fenom.php b/src/Fenom.php index 5696403..a1c4309 100644 --- a/src/Fenom.php +++ b/src/Fenom.php @@ -16,7 +16,7 @@ use Fenom\Template, * @author Ivan Shalganov */ class Fenom { - const VERSION = '1.1'; + const VERSION = '1.2'; /* Actions */ const INLINE_COMPILER = 1; @@ -27,7 +27,7 @@ class Fenom { /* Options */ const DENY_METHODS = 0x10; - const DENY_INLINE_FUNCS = 0x20; + const DENY_NATIVE_FUNCS = 0x20; const FORCE_INCLUDE = 0x40; const AUTO_RELOAD = 0x80; const FORCE_COMPILE = 0x100; @@ -37,6 +37,9 @@ class Fenom { const AUTO_TRIM = 0x1000; // reserved const DENY_STATICS = 0x2000; // reserved + /* @deprecated */ + const DENY_INLINE_FUNCS = 0x20; + /* Default parsers */ const DEFAULT_CLOSE_COMPILER = 'Fenom\Compiler::stdClose'; const DEFAULT_FUNC_PARSER = 'Fenom\Compiler::stdFuncParser'; @@ -50,7 +53,7 @@ class Fenom { */ private static $_options_list = array( "disable_methods" => self::DENY_METHODS, - "disable_native_funcs" => self::DENY_INLINE_FUNCS, + "disable_native_funcs" => self::DENY_NATIVE_FUNCS, "disable_cache" => self::DISABLE_CACHE, "force_compile" => self::FORCE_COMPILE, "auto_reload" => self::AUTO_RELOAD, @@ -461,40 +464,69 @@ class Fenom { /** * Return modifier function * - * @param $modifier + * @param string $modifier + * @param Fenom\Template $template * @return mixed - * @throws \Exception */ - public function getModifier($modifier) { + public function getModifier($modifier, Template $template = null) { if(isset($this->_modifiers[$modifier])) { return $this->_modifiers[$modifier]; } elseif($this->isAllowedFunction($modifier)) { return $modifier; } else { - throw new \Exception("Modifier $modifier not found"); + return $this->_loadModifier($modifier, $template); } } /** - * Return function - * + * @param string $modifier + * @param Fenom\Template $template + * @return bool + */ + protected function _loadModifier($modifier, $template) { + return false; + } + + /** * @param string $function + * @param Fenom\Template $template + * @return bool|string + * @deprecated + */ + public function getFunction($function, Template $template = null) { + return $this->getTag($function, $template); + } + + /** + * Returns tag info + * + * @param string $tag + * @param Fenom\Template $template * @return string|bool */ - public function getFunction($function) { - if(isset($this->_actions[$function])) { - return $this->_actions[$function]; + public function getTag($tag, Template $template = null) { + if(isset($this->_actions[$tag])) { + return $this->_actions[$tag]; } else { - return false; + return $this->_loadTag($tag, $template); } } + /** + * @param $tag + * @param Fenom\Template $template + * @return bool + */ + protected function _loadTag($tag, Template $template) { + return false; + } + /** * @param string $function * @return bool */ public function isAllowedFunction($function) { - if($this->_options & self::DENY_INLINE_FUNCS) { + if($this->_options & self::DENY_NATIVE_FUNCS) { return isset($this->_allowed_funcs[$function]); } else { return is_callable($function); diff --git a/src/Fenom/Compiler.php b/src/Fenom/Compiler.php index 30e390a..1ef5a58 100644 --- a/src/Fenom/Compiler.php +++ b/src/Fenom/Compiler.php @@ -879,7 +879,7 @@ class Compiler { $tpl->escape = false; if($tokens->is(':')) { $func = $tokens->getNext(Tokenizer::MACRO_STRING); - $tag = $tpl->getStorage()->getFunction($func); + $tag = $tpl->getStorage()->getTag($func, $tpl); if($tag["type"] == \Fenom::INLINE_FUNCTION) { $code = $tpl->parseAct($tokens); } elseif ($tag["type"] == \Fenom::BLOCK_FUNCTION) { diff --git a/src/Fenom/Template.php b/src/Fenom/Template.php index 50399fd..e5c9ce5 100644 --- a/src/Fenom/Template.php +++ b/src/Fenom/Template.php @@ -520,22 +520,22 @@ class Template extends Render { return $this->parseMacro($tokens, $name); } - if($act = $this->_fenom->getFunction($action)) { // call some function - switch($act["type"]) { + if($tag = $this->_fenom->getTag($action, $this)) { // call some function + switch($tag["type"]) { case Fenom::BLOCK_COMPILER: - $scope = new Scope($action, $this, $this->_line, $act, count($this->_stack), $this->_body); + $scope = new Scope($action, $this, $this->_line, $tag, count($this->_stack), $this->_body); $code = $scope->open($tokens); if(!$scope->is_closed) { array_push($this->_stack, $scope); } return $code; case Fenom::INLINE_COMPILER: - return call_user_func($act["parser"], $tokens, $this); + return call_user_func($tag["parser"], $tokens, $this); case Fenom::INLINE_FUNCTION: - return $this->out(call_user_func($act["parser"], $act["function"], $tokens, $this)); + return $this->out(call_user_func($tag["parser"], $tag["function"], $tokens, $this)); case Fenom::BLOCK_FUNCTION: - $scope = new Scope($action, $this, $this->_line, $act, count($this->_stack), $this->_body); - $scope->setFuncName($act["function"]); + $scope = new Scope($action, $this, $this->_line, $tag, count($this->_stack), $this->_body); + $scope->setFuncName($tag["function"]); array_push($this->_stack, $scope); $scope->escape = $this->escape; $this->escape = false; @@ -562,10 +562,10 @@ class Template extends Render { * * @static * @param Tokenizer $tokens - * @param bool $required - * @throws \LogicException - * @throws UnexpectedTokenException + * @param bool $required * @throws TokenizeException + * @throws UnexpectedTokenException + * @throws \Exception * @return string */ public function parseExp(Tokenizer $tokens, $required = false) { @@ -625,6 +625,9 @@ class Template extends Render { $_exp[] = $tokens->getAndNext(); } elseif($tokens->isNext("(") && !$tokens->getWhitespace()) { $func = $this->_fenom->getModifier($tokens->current()); + if(!$func) { + throw new \Exception("Function ".$tokens->getAndNext()." not found"); + } $tokens->next(); $func = $func.$this->parseArgs($tokens); if($tokens->is('|')) { @@ -1063,8 +1066,10 @@ class Template extends Render { */ public function parseModifier(Tokenizer $tokens, $value) { while($tokens->is("|")) { - $mods = $this->_fenom->getModifier( $modifier_name = $tokens->getNext(Tokenizer::MACRO_STRING) ); - + $mods = $this->_fenom->getModifier($tokens->getNext(Tokenizer::MACRO_STRING) ); + if(!$mods) { + throw new \Exception("Modifier ".$tokens->current()." not found"); + } $tokens->next(); $args = array(); diff --git a/tests/autoload.php b/tests/autoload.php index a3691fd..68e6c9a 100644 --- a/tests/autoload.php +++ b/tests/autoload.php @@ -9,6 +9,8 @@ define('FENOM_RESOURCES', __DIR__."/resources"); require_once FENOM_RESOURCES."/actions.php"; require_once __DIR__."/TestCase.php"; +ini_set('date.timezone', 'Europe/Moscow'); + function drop() { call_user_func_array("var_dump", func_get_args()); $e = new Exception(); diff --git a/tests/cases/Fenom/TemplateTest.php b/tests/cases/Fenom/TemplateTest.php index 7ae60e0..738e888 100644 --- a/tests/cases/Fenom/TemplateTest.php +++ b/tests/cases/Fenom/TemplateTest.php @@ -319,7 +319,7 @@ class TemplateTest extends TestCase { array('Create: {var $v = --$a++} Result: {$v} end', 'Fenom\CompileException', "Can not use two increments and/or decrements for one variable"), array('Create: {var $v = $a|upper++} Result: {$v} end', 'Fenom\CompileException', "Unexpected token '++'"), array('Create: {var $v = max($a,2)++} Result: {$v} end', 'Fenom\CompileException', "Unexpected token '++'"), - array('Create: {var $v = max($a,2)} Result: {$v} end', 'Fenom\CompileException', "Modifier max not found", Fenom::DENY_INLINE_FUNCS), + array('Create: {var $v = max($a,2)} Result: {$v} end', 'Fenom\CompileException', "Function max not found", Fenom::DENY_NATIVE_FUNCS), array('Create: {var $v = 4*} Result: {$v} end', 'Fenom\CompileException', "Unexpected token '*'"), array('Create: {var $v = ""$a} Result: {$v} end', 'Fenom\CompileException', "Unexpected token '\$a'"), array('Create: {var $v = [1,2} Result: {$v} end', 'Fenom\CompileException', "Unexpected end of expression"), From d977c86116cf902a9feb98acde542728a3484b8d Mon Sep 17 00:00:00 2001 From: bzick Date: Wed, 24 Jul 2013 20:09:14 +0400 Subject: [PATCH 03/18] Fix loader hint --- src/Fenom.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Fenom.php b/src/Fenom.php index a1c4309..92ddf44 100644 --- a/src/Fenom.php +++ b/src/Fenom.php @@ -517,7 +517,7 @@ class Fenom { * @param Fenom\Template $template * @return bool */ - protected function _loadTag($tag, Template $template) { + protected function _loadTag($tag, $template) { return false; } From bd69bbd86a463e6d594ede46ba750b8c87d31e84 Mon Sep 17 00:00:00 2001 From: bzick Date: Wed, 24 Jul 2013 20:56:28 +0400 Subject: [PATCH 04/18] Fix getModifier arguments in parsers --- src/Fenom/Template.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Fenom/Template.php b/src/Fenom/Template.php index e5c9ce5..46d2ed6 100644 --- a/src/Fenom/Template.php +++ b/src/Fenom/Template.php @@ -624,7 +624,7 @@ class Template extends Render { if($tokens->isSpecialVal()) { $_exp[] = $tokens->getAndNext(); } elseif($tokens->isNext("(") && !$tokens->getWhitespace()) { - $func = $this->_fenom->getModifier($tokens->current()); + $func = $this->_fenom->getModifier($tokens->current(), $this); if(!$func) { throw new \Exception("Function ".$tokens->getAndNext()." not found"); } @@ -1066,7 +1066,7 @@ class Template extends Render { */ public function parseModifier(Tokenizer $tokens, $value) { while($tokens->is("|")) { - $mods = $this->_fenom->getModifier($tokens->getNext(Tokenizer::MACRO_STRING) ); + $mods = $this->_fenom->getModifier($tokens->getNext(Tokenizer::MACRO_STRING), $this); if(!$mods) { throw new \Exception("Modifier ".$tokens->current()." not found"); } From 2a64bdb1fc98e62f233259b15b66f9e21cd96bdf Mon Sep 17 00:00:00 2001 From: bzick Date: Thu, 25 Jul 2013 02:05:44 +0400 Subject: [PATCH 05/18] Add replacing of parsers --- README.md | 2 +- src/Fenom.php | 14 ++++---------- src/Fenom/Template.php | 17 +++-------------- 3 files changed, 8 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index a03957f..7c8f8a9 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Fenom - Template Engine for PHP > Composer package: `{"fenom/fenom": "dev-master"}`. See on [Packagist.org](https://packagist.org/packages/bzick/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/fenom/fenom) ## [Usage](./docs/usage.md) :: [Documentation](./docs/readme.md) :: [Benchmark](./docs/benchmark.md) :: [Articles](./docs/articles.md) * Simple [syntax](./docs/syntax.md) diff --git a/src/Fenom.php b/src/Fenom.php index 92ddf44..a8649b7 100644 --- a/src/Fenom.php +++ b/src/Fenom.php @@ -107,7 +107,6 @@ class Fenom { "unescape" => 'Fenom\Modifier::unescape', "strip" => 'Fenom\Modifier::strip', "length" => 'Fenom\Modifier::length', - "default" => 'Fenom\Modifier::defaultValue', "iterable" => 'Fenom\Modifier::isIterable' ); @@ -558,12 +557,7 @@ class Fenom { } /** - * Set options. May be bitwise mask of constants DENY_METHODS, DENY_INLINE_FUNCS, DENY_SET_VARS, INCLUDE_SOURCES, - * FORCE_COMPILE, CHECK_MTIME, or associative array with boolean values: - * disable_methods - disable all calls method in template - * disable_native_funcs - disable all native PHP functions in template - * force_compile - recompile template every time (very slow!) - * compile_check - check template modifications (slow!) + * Set options * @param int|array $options */ public function setOptions($options) { @@ -605,7 +599,7 @@ class Fenom { * @return Fenom\Template */ public function getRawTemplate() { - return new \Fenom\Template($this, $this->_options); + return new Template($this, $this->_options); } /** @@ -726,7 +720,7 @@ class Fenom { */ public function compile($tpl, $store = true, $options = 0) { $options = $this->_options | $options; - $template = Template::factory($this, $options)->load($tpl); + $template = $this->getRawTemplate()->load($tpl); if($store) { $cache = $this->_getCacheName($tpl, $options); $tpl_tmp = tempnam($this->_compile_dir, $cache); @@ -766,7 +760,7 @@ class Fenom { * @return Fenom\Template */ public function compileCode($code, $name = 'Runtime compile') { - return Template::factory($this, $this->_options)->source($name, $code); + return $this->getRawTemplate()->source($name, $code); } diff --git a/src/Fenom/Template.php b/src/Fenom/Template.php index 46d2ed6..e696095 100644 --- a/src/Fenom/Template.php +++ b/src/Fenom/Template.php @@ -118,17 +118,6 @@ class Template extends Render { 'third' => '!(%s %% 3)' ); - /** - * Just factory - * - * @param \Fenom $fenom - * @param $options - * @return Template - */ - public static function factory(Fenom $fenom, $options) { - return new static($fenom, $options); - } - /** * @param Fenom $fenom Template storage * @param int $options @@ -215,7 +204,7 @@ class Template extends Render { $this->_appendText(substr($this->_src, $pos, $start - $pos)); $end = $start + 1; do { - $need_next_close_symbol = false; + $need_more = false; $end = strpos($this->_src, '}', $end + 1); // search close-symbol of the tag if($end === false) { // if unexpected end of template throw new CompileException("Unclosed tag in line {$this->_line}", 0, 1, $this->_name, $this->_line); @@ -233,7 +222,7 @@ class Template extends Render { } else { $tokens = new Tokenizer($_tag); // tokenize the tag if($tokens->isIncomplete()) { // all strings finished? - $need_next_close_symbol = true; + $need_more = true; } else { $this->_appendCode( $this->parseTag($tokens) , $tag); // start the tag lexer if($tokens->key()) { // if tokenizer have tokens - throws exceptions @@ -241,7 +230,7 @@ class Template extends Render { } } } - } while ($need_next_close_symbol); + } while ($need_more); unset($_tag, $tag); // cleanup break; } From 97e891895ae0e63e9f3360a0f644678eb07c035f Mon Sep 17 00:00:00 2001 From: bzick Date: Fri, 26 Jul 2013 10:40:07 +0400 Subject: [PATCH 06/18] Add ::before() --- src/Fenom/Template.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/Fenom/Template.php b/src/Fenom/Template.php index e696095..975e867 100644 --- a/src/Fenom/Template.php +++ b/src/Fenom/Template.php @@ -88,6 +88,8 @@ class Template extends Render { */ private $_ignore = false; + private $_before; + private $_filter = array(); private static $_checkers = array( @@ -259,6 +261,14 @@ class Template extends Render { $this->addDepend($this); // for 'verify' performance } + /** + * Execute some code in loading cache + * @param $code + */ + public function before($code) { + $this->_before .= $code; + } + /** * Generate temporary internal template variable * @return string @@ -354,6 +364,7 @@ class Template extends Render { public function getTemplateCode() { return "_name."' compiled at ".date('Y-m-d H:i:s')." */\n". + ($this->_before ? $this->_before."\n" : ""). "return new Fenom\\Render(\$fenom, ".$this->_getClosureSource().", ".var_export(array( "options" => $this->_options, "provider" => $this->_scm, From 4edd0e170887862ccc2ca1241f031192e4b8f4b5 Mon Sep 17 00:00:00 2001 From: bzick Date: Mon, 29 Jul 2013 14:53:21 +0400 Subject: [PATCH 07/18] Add recursive macros support (#28) --- README.md | 4 +- composer.json | 5 +-- docs/ext/extensions.md | 8 +++- src/Fenom.php | 2 + src/Fenom/Compiler.php | 47 ++++++++++++++-------- src/Fenom/Render.php | 25 ++++++++++-- src/Fenom/Template.php | 67 +++++++++++++++++++++----------- tests/cases/Fenom/MacrosTest.php | 26 +++++++++++++ 8 files changed, 135 insertions(+), 49 deletions(-) diff --git a/README.md b/README.md index 7c8f8a9..2993bfc 100644 --- a/README.md +++ b/README.md @@ -9,8 +9,8 @@ Fenom - Template Engine for PHP * Simple [syntax](./docs/syntax.md) * [Fast](./docs/benchmark.md) * [Secure](./docs/settings.md) -* [Simple](./ideology.md) -* [Flexible](./docs/main.md#extends) +* Simple +* [Flexible](./docs/ext/extensions.md) * [Lightweight](./docs/benchmark.md#stats) * [Powerful](./docs/main.md) * Easy to use: diff --git a/composer.json b/composer.json index 5cc9d88..bec82b2 100644 --- a/composer.json +++ b/composer.json @@ -1,9 +1,8 @@ { "name": "fenom/fenom", "type": "library", - "description": "Fenom - fast template engine for PHP", - "homepage": "http://bzick.github.io/fenom/", - "keywords": ["fenom", "template", "templating", "cytro"], + "description": "Fenom - excellent template engine for PHP", + "keywords": ["fenom", "template", "templating", "templater"], "license": "BSD-3", "authors": [ { diff --git a/docs/ext/extensions.md b/docs/ext/extensions.md index 9374920..e82ab24 100644 --- a/docs/ext/extensions.md +++ b/docs/ext/extensions.md @@ -1,5 +1,9 @@ Extensions ========== -* [Extra pack](https://github.com/bzick/fenom-extra) basic add-ons for web-base project. -* *Smarty pack* (planned) Smarty3 adapter \ No newline at end of file +* [Extra pack](https://github.com/bzick/fenom-extra) of add-ons for Fenom template engine. + * Tools for static files (css, js). + * Global variables + * Allow more hooks for extending + * Add variable container + * You can only use the necessary add-ons \ No newline at end of file diff --git a/src/Fenom.php b/src/Fenom.php index a8649b7..3e41406 100644 --- a/src/Fenom.php +++ b/src/Fenom.php @@ -47,6 +47,8 @@ class Fenom { const DEFAULT_FUNC_CLOSE = 'Fenom\Compiler::stdFuncClose'; const SMART_FUNC_PARSER = 'Fenom\Compiler::smartFuncParser'; + const MAX_MACRO_RECURSIVE = 32; + /** * @var int[] of possible options, as associative array * @see setOptions diff --git a/src/Fenom/Compiler.php b/src/Fenom/Compiler.php index 1ef5a58..c5b511e 100644 --- a/src/Fenom/Compiler.php +++ b/src/Fenom/Compiler.php @@ -34,7 +34,7 @@ class Compiler { if($name && ($tpl->getStorage()->getOptions() & \Fenom::FORCE_INCLUDE)) { // if FORCE_INCLUDE enabled and template name known $inc = $tpl->getStorage()->compile($name, false); $tpl->addDepend($inc); - return '$_tpl = (array)$tpl; $tpl->exchangeArray('.self::toArray($p).'+$_tpl); ?>'.$inc->_body.'exchangeArray($_tpl); unset($_tpl);'; + return '$_tpl = (array)$tpl; $tpl->exchangeArray('.self::toArray($p).'+$_tpl); ?>'.$inc->getBody().'exchangeArray($_tpl); unset($_tpl);'; } else { return '$tpl->getStorage()->getTemplate('.$cname.')->display('.self::toArray($p).'+(array)$tpl);'; } @@ -42,7 +42,7 @@ class Compiler { if($name && ($tpl->getStorage()->getOptions() & \Fenom::FORCE_INCLUDE)) { // if FORCE_INCLUDE enabled and template name known $inc = $tpl->getStorage()->compile($name, false); $tpl->addDepend($inc); - return '$_tpl = (array)$tpl; ?>'.$inc->_body.'exchangeArray($_tpl); unset($_tpl);'; + return '$_tpl = (array)$tpl; ?>'.$inc->getBody().'exchangeArray($_tpl); unset($_tpl);'; } else { return '$tpl->getStorage()->getTemplate('.$cname.')->display((array)$tpl);'; } @@ -493,7 +493,6 @@ class Compiler { */ public static function tagBlockOpen(Tokenizer $tokens, Scope $scope) { if($scope->level > 0) { - var_dump("".$scope->tpl); $scope->tpl->_compatible = true; } $scope["cname"] = $scope->tpl->parsePlainArg($tokens, $name); @@ -809,7 +808,6 @@ class Compiler { if($alias) { $name = $alias.'.'.$name; } - $tpl->macros[$name] = $macro; } $tpl->addDepend($donor); @@ -827,8 +825,9 @@ class Compiler { */ public static function macroOpen(Tokenizer $tokens, Scope $scope) { $scope["name"] = $tokens->get(Tokenizer::MACRO_STRING); - $scope["args"] = array(); - $scope["defaults"] = array(); + $scope["recursive"] = array(); + $args = array(); + $defaults = array(); if(!$tokens->valid()) { return; } @@ -836,12 +835,12 @@ class Compiler { if($tokens->is(')')) { return; } - while($tokens->is(Tokenizer::MACRO_STRING)) { - $scope["args"][] = $param = $tokens->getAndNext(); + while($tokens->is(Tokenizer::MACRO_STRING, T_VARIABLE)) { + $args[] = $param = $tokens->getAndNext(); if($tokens->is('=')) { $tokens->next(); if($tokens->is(T_CONSTANT_ENCAPSED_STRING, T_LNUMBER, T_DNUMBER) || $tokens->isSpecialVal()) { - $scope["defaults"][ $param ] = $tokens->getAndNext(); + $defaults[ $param ] = $tokens->getAndNext(); } else { throw new InvalidUsageException("Macro parameters may have only scalar defaults"); } @@ -849,7 +848,12 @@ class Compiler { $tokens->skipIf(','); } $tokens->skipIf(')'); - + $scope["macro"] = array( + "id" => $scope->tpl->i++, + "args" => $args, + "defaults" => $defaults, + "body" => "" + ); return; } @@ -858,12 +862,23 @@ class Compiler { * @param Scope $scope */ public static function macroClose(Tokenizer $tokens, Scope $scope) { - $scope->tpl->macros[ $scope["name"] ] = array( - "body" => $content = $scope->getContent(), - "args" => $scope["args"], - "defaults" => $scope["defaults"] - ); - $scope->tpl->_body = substr($scope->tpl->_body, 0, strlen($scope->tpl->_body) - strlen($content)); + if($scope["recursive"]) { + $switch = "switch(\$call['mark']) {\n"; + foreach($scope["recursive"] as $mark) { + $switch .= "case $mark: goto macro_$mark;\n"; + } + $switch .= "}"; + $stack = '$stack_'.$scope["macro"]['id']; + $scope["macro"]["body"] = ''.$scope->cutContent().''; + } else { + $scope["macro"]["body"] = $scope->cutContent(); + } + $scope->tpl->macros[ $scope["name"] ] = $scope["macro"]; } /** diff --git a/src/Fenom/Render.php b/src/Fenom/Render.php index e7d7361..2f08fed 100644 --- a/src/Fenom/Render.php +++ b/src/Fenom/Render.php @@ -72,11 +72,10 @@ class Render extends \ArrayObject { * @param callable $code template body * @param array $props */ - public function __construct(Fenom $fenom, \Closure $code, $props = array()) { + public function __construct(Fenom $fenom, \Closure $code, array $props = array()) { $this->_fenom = $fenom; $props += self::$_props; $this->_name = $props["name"]; -// $this->_provider = $this->_fenom->getProvider($props["scm"]); $this->_scm = $props["scm"]; $this->_time = $props["time"]; $this->_depends = $props["depends"]; @@ -85,28 +84,48 @@ class Render extends \ArrayObject { /** * Get template storage - * @return Fenom + * @return \Fenom */ public function getStorage() { return $this->_fenom; } + /** + * Get depends list + * @return array + */ public function getDepends() { return $this->_depends; } + /** + * Get schema name + * @return string + */ public function getScm() { return $this->_scm; } + /** + * Get provider of template source + * @return ProviderInterface + */ public function getProvider() { return $this->_fenom->getProvider($this->_scm); } + /** + * Get name without schema + * @return string + */ public function getBaseName() { return $this->_base_name; } + /** + * Get parse options + * @return int + */ public function getOptions() { return $this->_options; } diff --git a/src/Fenom/Template.php b/src/Fenom/Template.php index 975e867..3d51ede 100644 --- a/src/Fenom/Template.php +++ b/src/Fenom/Template.php @@ -38,12 +38,6 @@ class Template extends Render { * @var int shared counter */ public $i = 1; - /** - * Template PHP code - * @var string - */ - public $_body; - /** * @var array of macros */ @@ -63,10 +57,17 @@ class Template extends Render { * @var bool */ public $escape = false; + public $_extends; public $_extended = false; public $_compatible; + /** + * Template PHP code + * @var string + */ + private $_body; + /** * Call stack * @var Scope[] @@ -362,9 +363,10 @@ class Template extends Render { * @return string */ public function getTemplateCode() { + $before = $this->_before ? $this->_before."\n" : ""; return "_name."' compiled at ".date('Y-m-d H:i:s')." */\n". - ($this->_before ? $this->_before."\n" : ""). + $before. // some code 'before' template "return new Fenom\\Render(\$fenom, ".$this->_getClosureSource().", ".var_export(array( "options" => $this->_options, "provider" => $this->_scm, @@ -517,7 +519,7 @@ class Template extends Render { if($action !== "macro") { $name = $action.".".$name; } - return $this->parseMacro($tokens, $name); + return $this->parseMacroCall($tokens, $name); } if($tag = $this->_fenom->getTag($action, $this)) { // call some function @@ -839,7 +841,7 @@ class Template extends Render { } /** - * Parse 'is' and 'is not' operator + * Parse 'is' and 'is not' operators * @see $_checkers * @param Tokenizer $tokens * @param string $value @@ -1095,7 +1097,7 @@ class Template extends Render { } if(!is_string($mods)) { // dynamic modifier - $mods = 'call_user_func($tpl->getStorage()->getModifier("'.$modifier_name.'"), '; + $mods = 'call_user_func($tpl->getStorage()->getModifier("'.$mods.'"), '; } else { $mods .= "("; } @@ -1188,24 +1190,42 @@ class Template extends Render { * @return string * @throws InvalidUsageException */ - public function parseMacro(Tokenizer $tokens, $name) { + public function parseMacroCall(Tokenizer $tokens, $name) { + $recursive = false; + $macro = false; if(isset($this->macros[ $name ])) { $macro = $this->macros[ $name ]; - $p = $this->parseParams($tokens); - $args = array(); - foreach($macro["args"] as $arg) { - if(isset($p[ $arg ])) { - $args[ $arg ] = $p[ $arg ]; - } elseif(isset($macro["defaults"][ $arg ])) { - $args[ $arg ] = $macro["defaults"][ $arg ]; - } else { - throw new InvalidUsageException("Macro '$name' require '$arg' argument"); + } else { + foreach($this->_stack as $scope) { + if($scope->name == 'macro' && $scope['name'] == $name) { // invoke recursive + $recursive = $scope; + $macro = $scope['macro']; + break; } } - $args = $args ? '$tpl = '.Compiler::toArray($args).';' : ''; - return '$_tpl = $tpl; '.$args.' ?>'.$macro["body"].'next(); + $p = $this->parseParams($tokens); + $args = array(); + foreach($macro['args'] as $arg) { + if(isset($p[ $arg ])) { + $args[ $arg ] = $p[ $arg ]; + } elseif(isset($macro['defaults'][ $arg ])) { + $args[ $arg ] = $macro['defaults'][ $arg ]; + } else { + throw new InvalidUsageException("Macro '$name' require '$arg' argument"); + } + } + $args = $args ? '$tpl = '.Compiler::toArray($args).';' : ''; + if($recursive) { + $n = $this->i++; + $recursive['recursive'][] = $n; + return '$stack_'.$macro['id'].'[] = array("tpl" => $tpl, "mark" => '.$n.'); '.$args.' goto macro_'.$macro['id'].'; macro_'.$n.':'; } else { - throw new InvalidUsageException("Undefined macro '$name'"); + return '$_tpl = $tpl; '.$args.' ?>'.$macro["body"].'tpl("macro_recursive.tpl", '{macro factorial(num)} + {if $num} + {$num} {macro.factorial num=$num-1} + {/if} + {/macro} + + {macro.factorial num=10}'); + } + + public function _testSandbox() { + try { + $this->fenom->compile("macro_recursive.tpl"); + $this->fenom->flush(); + var_dump($this->fenom->fetch("macro_recursive.tpl", [])); + } catch(\Exception $e) { + var_dump($e->getMessage().": ".$e->getTraceAsString()); + } + exit; } public function testMacros() { @@ -72,4 +91,11 @@ class MacrosTest extends TestCase { $this->assertSame('a: x + y = 3 , x - y - z = 3 , new minus macros .', Modifier::strip($tpl->fetch(array()), true)); } + + public function testRecursive() { + $this->fenom->compile('macro_recursive.tpl'); + $this->fenom->flush(); + $tpl = $this->fenom->getTemplate('macro_recursive.tpl'); + $this->assertSame("10 9 8 7 6 5 4 3 2 1", Modifier::strip($tpl->fetch(array()), true)); + } } From 3674de41cb6c6d8ae00b4114d2a530169e016079 Mon Sep 17 00:00:00 2001 From: bzick Date: Mon, 29 Jul 2013 14:58:14 +0400 Subject: [PATCH 08/18] To PSR-x format --- src/Fenom.php | 310 +++++---- src/Fenom/Compiler.php | 534 +++++++++------- src/Fenom/Modifier.php | 72 ++- src/Fenom/Provider.php | 63 +- src/Fenom/ProviderInterface.php | 3 +- src/Fenom/Render.php | 55 +- src/Fenom/Scope.php | 47 +- src/Fenom/Template.php | 744 ++++++++++++---------- src/Fenom/Tokenizer.php | 277 ++++---- src/Fenom/UnexpectedTokenException.php | 29 + tests/TestCase.php | 240 +++---- tests/autoload.php | 23 +- tests/cases/Fenom/AutoEscapeTest.php | 13 +- tests/cases/Fenom/CommentTest.php | 26 +- tests/cases/Fenom/CustomProviderTest.php | 11 +- tests/cases/Fenom/ExtendsTemplateTest.php | 127 ++-- tests/cases/Fenom/FunctionsTest.php | 106 +-- tests/cases/Fenom/MacrosTest.php | 34 +- tests/cases/Fenom/ModifiersTest.php | 25 +- tests/cases/Fenom/ProviderTest.php | 52 +- tests/cases/Fenom/RenderTest.php | 20 +- tests/cases/Fenom/ScopeTest.php | 14 +- tests/cases/Fenom/TagsTest.php | 72 ++- tests/cases/Fenom/TemplateTest.php | 690 +++++++++++--------- tests/cases/Fenom/TokenizerTest.php | 75 +-- tests/cases/FenomTest.php | 52 +- tests/resources/actions.php | 31 +- 27 files changed, 2071 insertions(+), 1674 deletions(-) create mode 100644 src/Fenom/UnexpectedTokenException.php diff --git a/src/Fenom.php b/src/Fenom.php index 3e41406..bdaae07 100644 --- a/src/Fenom.php +++ b/src/Fenom.php @@ -15,55 +15,56 @@ use Fenom\Template, * * @author Ivan Shalganov */ -class Fenom { +class Fenom +{ const VERSION = '1.2'; /* Actions */ - const INLINE_COMPILER = 1; - const BLOCK_COMPILER = 2; - const INLINE_FUNCTION = 3; - const BLOCK_FUNCTION = 4; - const MODIFIER = 5; + const INLINE_COMPILER = 1; + const BLOCK_COMPILER = 2; + const INLINE_FUNCTION = 3; + const BLOCK_FUNCTION = 4; + const MODIFIER = 5; /* Options */ - const DENY_METHODS = 0x10; - const DENY_NATIVE_FUNCS = 0x20; - const FORCE_INCLUDE = 0x40; - const AUTO_RELOAD = 0x80; - const FORCE_COMPILE = 0x100; - const AUTO_ESCAPE = 0x200; - const DISABLE_CACHE = 0x400; - const FORCE_VERIFY = 0x800; // reserved - const AUTO_TRIM = 0x1000; // reserved - const DENY_STATICS = 0x2000; // reserved + const DENY_METHODS = 0x10; + const DENY_NATIVE_FUNCS = 0x20; + const FORCE_INCLUDE = 0x40; + const AUTO_RELOAD = 0x80; + const FORCE_COMPILE = 0x100; + const AUTO_ESCAPE = 0x200; + const DISABLE_CACHE = 0x400; + const FORCE_VERIFY = 0x800; // reserved + const AUTO_TRIM = 0x1000; // reserved + const DENY_STATICS = 0x2000; // reserved /* @deprecated */ - const DENY_INLINE_FUNCS = 0x20; + const DENY_INLINE_FUNCS = 0x20; /* Default parsers */ const DEFAULT_CLOSE_COMPILER = 'Fenom\Compiler::stdClose'; - const DEFAULT_FUNC_PARSER = 'Fenom\Compiler::stdFuncParser'; - const DEFAULT_FUNC_OPEN = 'Fenom\Compiler::stdFuncOpen'; - const DEFAULT_FUNC_CLOSE = 'Fenom\Compiler::stdFuncClose'; - const SMART_FUNC_PARSER = 'Fenom\Compiler::smartFuncParser'; + const DEFAULT_FUNC_PARSER = 'Fenom\Compiler::stdFuncParser'; + const DEFAULT_FUNC_OPEN = 'Fenom\Compiler::stdFuncOpen'; + const DEFAULT_FUNC_CLOSE = 'Fenom\Compiler::stdFuncClose'; + const SMART_FUNC_PARSER = 'Fenom\Compiler::smartFuncParser'; - const MAX_MACRO_RECURSIVE = 32; + const MAX_MACRO_RECURSIVE = 32; /** * @var int[] of possible options, as associative array * @see setOptions */ private static $_options_list = array( - "disable_methods" => self::DENY_METHODS, + "disable_methods" => self::DENY_METHODS, "disable_native_funcs" => self::DENY_NATIVE_FUNCS, - "disable_cache" => self::DISABLE_CACHE, - "force_compile" => self::FORCE_COMPILE, - "auto_reload" => self::AUTO_RELOAD, - "force_include" => self::FORCE_INCLUDE, - "auto_escape" => self::AUTO_ESCAPE, - "force_verify" => self::FORCE_VERIFY, - "auto_trim" => self::AUTO_TRIM, - "disable_statics" => self::DENY_STATICS, + "disable_cache" => self::DISABLE_CACHE, + "force_compile" => self::FORCE_COMPILE, + "auto_reload" => self::AUTO_RELOAD, + "force_include" => self::FORCE_INCLUDE, + "auto_escape" => self::AUTO_ESCAPE, + "force_verify" => self::FORCE_VERIFY, + "auto_trim" => self::AUTO_TRIM, + "disable_statics" => self::DENY_STATICS, ); /** @@ -97,19 +98,19 @@ class Fenom { * @var string[] list of modifiers [modifier_name => callable] */ protected $_modifiers = array( - "upper" => 'strtoupper', - "up" => 'strtoupper', - "lower" => 'strtolower', - "low" => 'strtolower', + "upper" => 'strtoupper', + "up" => 'strtoupper', + "lower" => 'strtolower', + "low" => 'strtolower', "date_format" => 'Fenom\Modifier::dateFormat', - "date" => 'Fenom\Modifier::date', - "truncate" => 'Fenom\Modifier::truncate', - "escape" => 'Fenom\Modifier::escape', - "e" => 'Fenom\Modifier::escape', // alias of escape - "unescape" => 'Fenom\Modifier::unescape', - "strip" => 'Fenom\Modifier::strip', - "length" => 'Fenom\Modifier::length', - "iterable" => 'Fenom\Modifier::isIterable' + "date" => 'Fenom\Modifier::date', + "truncate" => 'Fenom\Modifier::truncate', + "escape" => 'Fenom\Modifier::escape', + "e" => 'Fenom\Modifier::escape', // alias of escape + "unescape" => 'Fenom\Modifier::unescape', + "strip" => 'Fenom\Modifier::strip', + "length" => 'Fenom\Modifier::length', + "iterable" => 'Fenom\Modifier::isIterable' ); /** @@ -136,7 +137,7 @@ class Fenom { ), 'float_tags' => array('break' => 1, 'continue' => 1) ), - 'if' => array( // {if ...} {elseif ...} {else} {/if} + 'if' => array( // {if ...} {elseif ...} {else} {/if} 'type' => self::BLOCK_COMPILER, 'open' => 'Fenom\Compiler::ifOpen', 'close' => 'Fenom\Compiler::stdClose', @@ -145,7 +146,7 @@ class Fenom { 'else' => 'Fenom\Compiler::tagElse', ) ), - 'switch' => array( // {switch ...} {case ...} {break} {default} {/switch} + 'switch' => array( // {switch ...} {case ...} {break} {default} {/switch} 'type' => self::BLOCK_COMPILER, 'open' => 'Fenom\Compiler::switchOpen', 'close' => 'Fenom\Compiler::stdClose', @@ -156,7 +157,7 @@ class Fenom { ), 'float_tags' => array('break' => 1) ), - 'for' => array( // {for ...} {break} {continue} {/for} + 'for' => array( // {for ...} {break} {continue} {/for} 'type' => self::BLOCK_COMPILER, 'open' => 'Fenom\Compiler::forOpen', 'close' => 'Fenom\Compiler::forClose', @@ -167,7 +168,7 @@ class Fenom { ), 'float_tags' => array('break' => 1, 'continue' => 1) ), - 'while' => array( // {while ...} {break} {continue} {/while} + 'while' => array( // {while ...} {break} {continue} {/while} 'type' => self::BLOCK_COMPILER, 'open' => 'Fenom\Compiler::whileOpen', 'close' => 'Fenom\Compiler::stdClose', @@ -181,12 +182,12 @@ class Fenom { 'type' => self::INLINE_COMPILER, 'parser' => 'Fenom\Compiler::tagInclude' ), - 'var' => array( // {var ...} + 'var' => array( // {var ...} 'type' => self::BLOCK_COMPILER, 'open' => 'Fenom\Compiler::varOpen', 'close' => 'Fenom\Compiler::varClose' ), - 'block' => array( // {block ...} {parent} {/block} + 'block' => array( // {block ...} {parent} {/block} 'type' => self::BLOCK_COMPILER, 'open' => 'Fenom\Compiler::tagBlockOpen', 'close' => 'Fenom\Compiler::tagBlockClose', @@ -226,7 +227,7 @@ class Fenom { 'type' => self::INLINE_COMPILER, 'parser' => 'Fenom\Compiler::tagCycle' ), - 'raw' => array( + 'raw' => array( 'type' => self::INLINE_COMPILER, 'parser' => 'Fenom\Compiler::tagRaw' ), @@ -250,10 +251,11 @@ class Fenom { * @throws InvalidArgumentException * @return Fenom */ - public static function factory($source, $compile_dir = '/tmp', $options = 0) { - if(is_string($source)) { + public static function factory($source, $compile_dir = '/tmp', $options = 0) + { + if (is_string($source)) { $provider = new Fenom\Provider($source); - } elseif($source instanceof ProviderInterface) { + } elseif ($source instanceof ProviderInterface) { $provider = $source; } else { throw new InvalidArgumentException("Source must be a valid path or provider object"); @@ -261,7 +263,7 @@ class Fenom { $fenom = new static($provider); /* @var Fenom $fenom */ $fenom->setCompileDir($compile_dir); - if($options) { + if ($options) { $fenom->setOptions($options); } return $fenom; @@ -270,7 +272,8 @@ class Fenom { /** * @param Fenom\ProviderInterface $provider */ - public function __construct(Fenom\ProviderInterface $provider) { + public function __construct(Fenom\ProviderInterface $provider) + { $this->_provider = $provider; } @@ -280,7 +283,8 @@ class Fenom { * @param string $dir directory to store compiled templates in * @return Fenom */ - public function setCompileDir($dir) { + public function setCompileDir($dir) + { $this->_compile_dir = $dir; return $this; } @@ -289,7 +293,8 @@ class Fenom { * * @param callable $cb */ - public function addPreCompileFilter($cb) { + public function addPreCompileFilter($cb) + { $this->_on_pre_cmp[] = $cb; } @@ -297,14 +302,16 @@ class Fenom { * * @param callable $cb */ - public function addPostCompileFilter($cb) { + public function addPostCompileFilter($cb) + { $this->_on_post_cmp[] = $cb; } /** * @param callable $cb */ - public function addCompileFilter($cb) { + public function addCompileFilter($cb) + { $this->_on_cmp[] = $cb; } @@ -315,7 +322,8 @@ class Fenom { * @param string $callback the modifier callback * @return Fenom */ - public function addModifier($modifier, $callback) { + public function addModifier($modifier, $callback) + { $this->_modifiers[$modifier] = $callback; return $this; } @@ -327,7 +335,8 @@ class Fenom { * @param callable $parser * @return Fenom */ - public function addCompiler($compiler, $parser) { + public function addCompiler($compiler, $parser) + { $this->_actions[$compiler] = array( 'type' => self::INLINE_COMPILER, 'parser' => $parser @@ -340,11 +349,12 @@ class Fenom { * @param string|object $storage * @return $this */ - public function addCompilerSmart($compiler, $storage) { - if(method_exists($storage, "tag".$compiler)) { + public function addCompilerSmart($compiler, $storage) + { + if (method_exists($storage, "tag" . $compiler)) { $this->_actions[$compiler] = array( 'type' => self::INLINE_COMPILER, - 'parser' => array($storage, "tag".$compiler) + 'parser' => array($storage, "tag" . $compiler) ); } return $this; @@ -359,11 +369,12 @@ class Fenom { * @param array $tags * @return Fenom */ - public function addBlockCompiler($compiler, $open_parser, $close_parser = self::DEFAULT_CLOSE_COMPILER, array $tags = array()) { + public function addBlockCompiler($compiler, $open_parser, $close_parser = self::DEFAULT_CLOSE_COMPILER, array $tags = array()) + { $this->_actions[$compiler] = array( 'type' => self::BLOCK_COMPILER, 'open' => $open_parser, - 'close' => $close_parser ?: self::DEFAULT_CLOSE_COMPILER, + 'close' => $close_parser ? : self::DEFAULT_CLOSE_COMPILER, 'tags' => $tags, ); return $this; @@ -377,27 +388,28 @@ class Fenom { * @throws LogicException * @return Fenom */ - public function addBlockCompilerSmart($compiler, $storage, array $tags, array $floats = array()) { + public function addBlockCompilerSmart($compiler, $storage, array $tags, array $floats = array()) + { $c = array( 'type' => self::BLOCK_COMPILER, "tags" => array(), "float_tags" => array() ); - if(method_exists($storage, $compiler."Open")) { - $c["open"] = $compiler."Open"; + if (method_exists($storage, $compiler . "Open")) { + $c["open"] = $compiler . "Open"; } else { throw new \LogicException("Open compiler {$compiler}Open not found"); } - if(method_exists($storage, $compiler."Close")) { - $c["close"] = $compiler."Close"; + if (method_exists($storage, $compiler . "Close")) { + $c["close"] = $compiler . "Close"; } else { throw new \LogicException("Close compiler {$compiler}Close not found"); } - foreach($tags as $tag) { - if(method_exists($storage, "tag".$tag)) { - $c["tags"][ $tag ] = "tag".$tag; - if($floats && in_array($tag, $floats)) { - $c['float_tags'][ $tag ] = 1; + foreach ($tags as $tag) { + if (method_exists($storage, "tag" . $tag)) { + $c["tags"][$tag] = "tag" . $tag; + if ($floats && in_array($tag, $floats)) { + $c['float_tags'][$tag] = 1; } } else { throw new \LogicException("Tag compiler $tag (tag{$compiler}) not found"); @@ -413,7 +425,8 @@ class Fenom { * @param callable|string $parser * @return Fenom */ - public function addFunction($function, $callback, $parser = self::DEFAULT_FUNC_PARSER) { + public function addFunction($function, $callback, $parser = self::DEFAULT_FUNC_PARSER) + { $this->_actions[$function] = array( 'type' => self::INLINE_FUNCTION, 'parser' => $parser, @@ -427,7 +440,8 @@ class Fenom { * @param callable $callback * @return Fenom */ - public function addFunctionSmart($function, $callback) { + public function addFunctionSmart($function, $callback) + { $this->_actions[$function] = array( 'type' => self::INLINE_FUNCTION, 'parser' => self::SMART_FUNC_PARSER, @@ -443,12 +457,13 @@ class Fenom { * @param callable|string $parser_close * @return Fenom */ - public function addBlockFunction($function, $callback, $parser_open = self::DEFAULT_FUNC_OPEN, $parser_close = self::DEFAULT_FUNC_CLOSE) { + public function addBlockFunction($function, $callback, $parser_open = self::DEFAULT_FUNC_OPEN, $parser_close = self::DEFAULT_FUNC_CLOSE) + { $this->_actions[$function] = array( - 'type' => self::BLOCK_FUNCTION, - 'open' => $parser_open, - 'close' => $parser_close, - 'function' => $callback, + 'type' => self::BLOCK_FUNCTION, + 'open' => $parser_open, + 'close' => $parser_close, + 'function' => $callback, ); return $this; } @@ -457,7 +472,8 @@ class Fenom { * @param array $funcs * @return Fenom */ - public function addAllowedFunctions(array $funcs) { + public function addAllowedFunctions(array $funcs) + { $this->_allowed_funcs = $this->_allowed_funcs + array_flip($funcs); return $this; } @@ -469,10 +485,11 @@ class Fenom { * @param Fenom\Template $template * @return mixed */ - public function getModifier($modifier, Template $template = null) { - if(isset($this->_modifiers[$modifier])) { + public function getModifier($modifier, Template $template = null) + { + if (isset($this->_modifiers[$modifier])) { return $this->_modifiers[$modifier]; - } elseif($this->isAllowedFunction($modifier)) { + } elseif ($this->isAllowedFunction($modifier)) { return $modifier; } else { return $this->_loadModifier($modifier, $template); @@ -484,7 +501,8 @@ class Fenom { * @param Fenom\Template $template * @return bool */ - protected function _loadModifier($modifier, $template) { + protected function _loadModifier($modifier, $template) + { return false; } @@ -494,7 +512,8 @@ class Fenom { * @return bool|string * @deprecated */ - public function getFunction($function, Template $template = null) { + public function getFunction($function, Template $template = null) + { return $this->getTag($function, $template); } @@ -505,8 +524,9 @@ class Fenom { * @param Fenom\Template $template * @return string|bool */ - public function getTag($tag, Template $template = null) { - if(isset($this->_actions[$tag])) { + public function getTag($tag, Template $template = null) + { + if (isset($this->_actions[$tag])) { return $this->_actions[$tag]; } else { return $this->_loadTag($tag, $template); @@ -518,7 +538,8 @@ class Fenom { * @param Fenom\Template $template * @return bool */ - protected function _loadTag($tag, $template) { + protected function _loadTag($tag, $template) + { return false; } @@ -526,8 +547,9 @@ class Fenom { * @param string $function * @return bool */ - public function isAllowedFunction($function) { - if($this->_options & self::DENY_NATIVE_FUNCS) { + public function isAllowedFunction($function) + { + if ($this->_options & self::DENY_NATIVE_FUNCS) { return isset($this->_allowed_funcs[$function]); } else { return is_callable($function); @@ -538,10 +560,11 @@ class Fenom { * @param string $tag * @return array */ - public function getTagOwners($tag) { + public function getTagOwners($tag) + { $tags = array(); - foreach($this->_actions as $owner => $params) { - if(isset($params["tags"][$tag])) { + foreach ($this->_actions as $owner => $params) { + if (isset($params["tags"][$tag])) { $tags[] = $owner; } } @@ -554,7 +577,8 @@ class Fenom { * @param string $scm scheme name * @param Fenom\ProviderInterface $provider provider object */ - public function addProvider($scm, \Fenom\ProviderInterface $provider) { + public function addProvider($scm, \Fenom\ProviderInterface $provider) + { $this->_providers[$scm] = $provider; } @@ -562,8 +586,9 @@ class Fenom { * Set options * @param int|array $options */ - public function setOptions($options) { - if(is_array($options)) { + public function setOptions($options) + { + if (is_array($options)) { $options = self::_makeMask($options, self::$_options_list, $this->_options); } $this->_storage = array(); @@ -574,7 +599,8 @@ class Fenom { * Get options as bits * @return int */ - public function getOptions() { + public function getOptions() + { return $this->_options; } @@ -583,9 +609,10 @@ class Fenom { * @return Fenom\ProviderInterface * @throws InvalidArgumentException */ - public function getProvider($scm = false) { - if($scm) { - if(isset($this->_providers[$scm])) { + public function getProvider($scm = false) + { + if ($scm) { + if (isset($this->_providers[$scm])) { return $this->_providers[$scm]; } else { throw new InvalidArgumentException("Provider for '$scm' not found"); @@ -600,7 +627,8 @@ class Fenom { * * @return Fenom\Template */ - public function getRawTemplate() { + public function getRawTemplate() + { return new Template($this, $this->_options); } @@ -611,7 +639,8 @@ class Fenom { * @param array $vars array of data for template * @return Fenom\Render */ - public function display($template, array $vars = array()) { + public function display($template, array $vars = array()) + { return $this->getTemplate($template)->display($vars); } @@ -621,7 +650,8 @@ class Fenom { * @param array $vars array of data for template * @return mixed */ - public function fetch($template, array $vars = array()) { + public function fetch($template, array $vars = array()) + { return $this->getTemplate($template)->fetch($vars); } @@ -634,7 +664,8 @@ class Fenom { * @param float $chunk * @return array */ - public function pipe($template, $callback, array $vars = array(), $chunk = 1e6) { + public function pipe($template, $callback, array $vars = array(), $chunk = 1e6) + { ob_start($callback, $chunk, true); $data = $this->getTemplate($template)->display($vars); ob_end_flush(); @@ -648,21 +679,22 @@ class Fenom { * @param int $options additional options and flags * @return Fenom\Template */ - public function getTemplate($template, $options = 0) { + public function getTemplate($template, $options = 0) + { $options |= $this->_options; - $key = dechex($options)."@".$template; - if(isset($this->_storage[ $key ])) { - /** @var Fenom\Template $tpl */ - $tpl = $this->_storage[ $key ]; - if(($this->_options & self::AUTO_RELOAD) && !$tpl->isValid()) { - return $this->_storage[ $key ] = $this->compile($template, true, $options); + $key = dechex($options) . "@" . $template; + if (isset($this->_storage[$key])) { + /** @var Fenom\Template $tpl */ + $tpl = $this->_storage[$key]; + if (($this->_options & self::AUTO_RELOAD) && !$tpl->isValid()) { + return $this->_storage[$key] = $this->compile($template, true, $options); } else { return $tpl; } - } elseif($this->_options & self::FORCE_COMPILE) { + } elseif ($this->_options & self::FORCE_COMPILE) { return $this->compile($template, $this->_options & self::DISABLE_CACHE & ~self::FORCE_COMPILE, $options); } else { - return $this->_storage[ $key ] = $this->_load($template, $options); + return $this->_storage[$key] = $this->_load($template, $options); } } @@ -671,9 +703,10 @@ class Fenom { * @param string $template * @return bool */ - public function templateExists($template) { - if($provider = strstr($template, ":", true)) { - if(isset($this->_providers[$provider])) { + public function templateExists($template) + { + if ($provider = strstr($template, ":", true)) { + if (isset($this->_providers[$provider])) { return $this->_providers[$provider]->templateExists(substr($template, strlen($provider) + 1)); } } else { @@ -689,13 +722,14 @@ class Fenom { * @param int $opts * @return Fenom\Render */ - protected function _load($tpl, $opts) { + protected function _load($tpl, $opts) + { $file_name = $this->_getCacheName($tpl, $opts); - if(!is_file($this->_compile_dir."/".$file_name)) { + if (!is_file($this->_compile_dir . "/" . $file_name)) { return $this->compile($tpl, true, $opts); } else { $fenom = $this; - return include($this->_compile_dir."/".$file_name); + return include($this->_compile_dir . "/" . $file_name); } } @@ -706,8 +740,9 @@ class Fenom { * @param int $options * @return string */ - private function _getCacheName($tpl, $options) { - $hash = $tpl.":".$options; + private function _getCacheName($tpl, $options) + { + $hash = $tpl . ":" . $options; return sprintf("%s.%x.%x.php", str_replace(":", "_", basename($tpl)), crc32($hash), strlen($hash)); } @@ -720,20 +755,21 @@ class Fenom { * @throws RuntimeException * @return \Fenom\Template */ - public function compile($tpl, $store = true, $options = 0) { + public function compile($tpl, $store = true, $options = 0) + { $options = $this->_options | $options; $template = $this->getRawTemplate()->load($tpl); - if($store) { + if ($store) { $cache = $this->_getCacheName($tpl, $options); $tpl_tmp = tempnam($this->_compile_dir, $cache); $tpl_fp = fopen($tpl_tmp, "w"); - if(!$tpl_fp) { - throw new \RuntimeException("Can't to open temporary file $tpl_tmp. Directory ".$this->_compile_dir." is writable?"); + if (!$tpl_fp) { + throw new \RuntimeException("Can't to open temporary file $tpl_tmp. Directory " . $this->_compile_dir . " is writable?"); } fwrite($tpl_fp, $template->getTemplateCode()); fclose($tpl_fp); - $file_name = $this->_compile_dir."/".$cache; - if(!rename($tpl_tmp, $file_name)) { + $file_name = $this->_compile_dir . "/" . $cache; + if (!rename($tpl_tmp, $file_name)) { throw new \RuntimeException("Can't to move $tpl_tmp to $tpl"); } } @@ -743,14 +779,16 @@ class Fenom { /** * Flush internal memory template cache */ - public function flush() { + public function flush() + { $this->_storage = array(); } /** * Remove all compiled templates */ - public function clearAllCompiles() { + public function clearAllCompiles() + { \Fenom\Provider::clean($this->_compile_dir); } @@ -761,7 +799,8 @@ class Fenom { * @param string $name * @return Fenom\Template */ - public function compileCode($code, $name = 'Runtime compile') { + public function compileCode($code, $name = 'Runtime compile') + { return $this->getRawTemplate()->source($name, $code); } @@ -775,7 +814,8 @@ class Fenom { * @return int result, ( $mask | a ) & ~b * @throws \RuntimeException if key from custom assoc doesn't exists into possible values */ - private static function _makeMask(array $values, array $options, $mask = 0) { + private static function _makeMask(array $values, array $options, $mask = 0) + { foreach ($values as $key => $value) { if (isset($options[$key])) { if ($options[$key]) { diff --git a/src/Fenom/Compiler.php b/src/Fenom/Compiler.php index c5b511e..3ab07f4 100644 --- a/src/Fenom/Compiler.php +++ b/src/Fenom/Compiler.php @@ -17,34 +17,36 @@ use Fenom\Scope; * @package Fenom * @author Ivan Shalganov */ -class Compiler { +class Compiler +{ /** * Tag {include ...} * * @static * @param Tokenizer $tokens - * @param Template $tpl + * @param Template $tpl * @throws InvalidUsageException * @return string */ - public static function tagInclude(Tokenizer $tokens, Template $tpl) { + public static function tagInclude(Tokenizer $tokens, Template $tpl) + { $cname = $tpl->parsePlainArg($tokens, $name); $p = $tpl->parseParams($tokens); - if($p) { // if we have additionally variables - if($name && ($tpl->getStorage()->getOptions() & \Fenom::FORCE_INCLUDE)) { // if FORCE_INCLUDE enabled and template name known + if ($p) { // if we have additionally variables + if ($name && ($tpl->getStorage()->getOptions() & \Fenom::FORCE_INCLUDE)) { // if FORCE_INCLUDE enabled and template name known $inc = $tpl->getStorage()->compile($name, false); $tpl->addDepend($inc); - return '$_tpl = (array)$tpl; $tpl->exchangeArray('.self::toArray($p).'+$_tpl); ?>'.$inc->getBody().'exchangeArray($_tpl); unset($_tpl);'; + return '$_tpl = (array)$tpl; $tpl->exchangeArray(' . self::toArray($p) . '+$_tpl); ?>' . $inc->getBody() . 'exchangeArray($_tpl); unset($_tpl);'; } else { - return '$tpl->getStorage()->getTemplate('.$cname.')->display('.self::toArray($p).'+(array)$tpl);'; + return '$tpl->getStorage()->getTemplate(' . $cname . ')->display(' . self::toArray($p) . '+(array)$tpl);'; } } else { - if($name && ($tpl->getStorage()->getOptions() & \Fenom::FORCE_INCLUDE)) { // if FORCE_INCLUDE enabled and template name known + if ($name && ($tpl->getStorage()->getOptions() & \Fenom::FORCE_INCLUDE)) { // if FORCE_INCLUDE enabled and template name known $inc = $tpl->getStorage()->compile($name, false); $tpl->addDepend($inc); - return '$_tpl = (array)$tpl; ?>'.$inc->getBody().'exchangeArray($_tpl); unset($_tpl);'; + return '$_tpl = (array)$tpl; ?>' . $inc->getBody() . 'exchangeArray($_tpl); unset($_tpl);'; } else { - return '$tpl->getStorage()->getTemplate('.$cname.')->display((array)$tpl);'; + return '$tpl->getStorage()->getTemplate(' . $cname . ')->display((array)$tpl);'; } } } @@ -55,12 +57,13 @@ class Compiler { * * @static * @param Tokenizer $tokens - * @param Scope $scope + * @param Scope $scope * @return string */ - public static function ifOpen(Tokenizer $tokens, Scope $scope) { + public static function ifOpen(Tokenizer $tokens, Scope $scope) + { $scope["else"] = false; - return 'if('.$scope->tpl->parseExp($tokens, true).') {'; + return 'if(' . $scope->tpl->parseExp($tokens, true) . ') {'; } /** @@ -68,27 +71,29 @@ class Compiler { * * @static * @param Tokenizer $tokens - * @param Scope $scope + * @param Scope $scope * @throws InvalidUsageException * @return string */ - public static function tagElseIf(Tokenizer $tokens, Scope $scope) { - if($scope["else"]) { + public static function tagElseIf(Tokenizer $tokens, Scope $scope) + { + if ($scope["else"]) { throw new InvalidUsageException('Incorrect use of the tag {elseif}'); } - return '} elseif('.$scope->tpl->parseExp($tokens, true).') {'; + return '} elseif(' . $scope->tpl->parseExp($tokens, true) . ') {'; } /** * Tag {else} * * @param Tokenizer $tokens - * @param Scope $scope + * @param Scope $scope * @internal param $ - * @param Scope $scope + * @param Scope $scope * @return string */ - public static function tagElse(Tokenizer $tokens, Scope $scope) { + public static function tagElse(Tokenizer $tokens, Scope $scope) + { $scope["else"] = true; return '} else {'; } @@ -98,22 +103,23 @@ class Compiler { * * @static * @param Tokenizer $tokens - * @param Scope $scope + * @param Scope $scope * @throws UnexpectedTokenException * @throws InvalidUsageException * @return string */ - public static function foreachOpen(Tokenizer $tokens, Scope $scope) { + public static function foreachOpen(Tokenizer $tokens, Scope $scope) + { $p = array("index" => false, "first" => false, "last" => false); $key = null; $before = $body = array(); - if($tokens->is(T_VARIABLE)) { + if ($tokens->is(T_VARIABLE)) { $from = $scope->tpl->parseVariable($tokens, Template::DENY_MODS); $prepend = ""; - } elseif($tokens->is('[')) { + } elseif ($tokens->is('[')) { $from = $scope->tpl->parseArray($tokens); - $uid = '$v'.$scope->tpl->i++; - $prepend = $uid.' = '.$from.';'; + $uid = '$v' . $scope->tpl->i++; + $prepend = $uid . ' = ' . $from . ';'; $from = $uid; } else { throw new UnexpectedTokenException($tokens, null, "tag {foreach}"); @@ -121,7 +127,7 @@ class Compiler { $tokens->get(T_AS); $tokens->next(); $value = $scope->tpl->parseVariable($tokens, Template::DENY_MODS | Template::DENY_ARRAY); - if($tokens->is(T_DOUBLE_ARROW)) { + if ($tokens->is(T_DOUBLE_ARROW)) { $tokens->next(); $key = $value; $value = $scope->tpl->parseVariable($tokens, Template::DENY_MODS | Template::DENY_ARRAY); @@ -130,35 +136,35 @@ class Compiler { $scope["after"] = array(); $scope["else"] = false; - while($token = $tokens->key()) { + while ($token = $tokens->key()) { $param = $tokens->get(T_STRING); - if(!isset($p[ $param ])) { + if (!isset($p[$param])) { throw new InvalidUsageException("Unknown parameter '$param' in {foreach}"); } $tokens->getNext("="); $tokens->next(); - $p[ $param ] = $scope->tpl->parseVariable($tokens, Template::DENY_MODS | Template::DENY_ARRAY); + $p[$param] = $scope->tpl->parseVariable($tokens, Template::DENY_MODS | Template::DENY_ARRAY); } - if($p["index"]) { - $before[] = $p["index"].' = 0'; - $scope["after"][] = $p["index"].'++'; + if ($p["index"]) { + $before[] = $p["index"] . ' = 0'; + $scope["after"][] = $p["index"] . '++'; } - if($p["first"]) { - $before[] = $p["first"].' = true'; - $scope["after"][] = $p["first"] .' && ('. $p["first"].' = false )'; + if ($p["first"]) { + $before[] = $p["first"] . ' = true'; + $scope["after"][] = $p["first"] . ' && (' . $p["first"] . ' = false )'; } - if($p["last"]) { - $before[] = $p["last"].' = false'; - $scope["uid"] = "v".$scope->tpl->i++; - $before[] = '$'.$scope["uid"]." = count($from)"; - $body[] = 'if(!--$'.$scope["uid"].') '.$p["last"].' = true'; + if ($p["last"]) { + $before[] = $p["last"] . ' = false'; + $scope["uid"] = "v" . $scope->tpl->i++; + $before[] = '$' . $scope["uid"] . " = count($from)"; + $body[] = 'if(!--$' . $scope["uid"] . ') ' . $p["last"] . ' = true'; } - $before = $before ? implode("; ", $before).";" : ""; - $body = $body ? implode("; ", $body).";" : ""; - $scope["after"] = $scope["after"] ? implode("; ", $scope["after"]).";" : ""; - if($key) { + $before = $before ? implode("; ", $before) . ";" : ""; + $body = $body ? implode("; ", $body) . ";" : ""; + $scope["after"] = $scope["after"] ? implode("; ", $scope["after"]) . ";" : ""; + if ($key) { return "$prepend if($from) { $before foreach($from as $key => $value) { $body"; } else { return "$prepend if($from) { $before foreach($from as $value) { $body"; @@ -169,10 +175,11 @@ class Compiler { * Tag {foreachelse} * * @param Tokenizer $tokens - * @param Scope $scope + * @param Scope $scope * @return string */ - public static function foreachElse($tokens, Scope $scope) { + public static function foreachElse($tokens, Scope $scope) + { $scope["no-break"] = $scope["no-continue"] = $scope["else"] = true; return " {$scope['after']} } } else {"; } @@ -182,11 +189,12 @@ class Compiler { * * @static * @param Tokenizer $tokens - * @param Scope $scope + * @param Scope $scope * @return string */ - public static function foreachClose($tokens, Scope $scope) { - if($scope["else"]) { + public static function foreachClose($tokens, Scope $scope) + { + if ($scope["else"]) { return '}'; } else { return " {$scope['after']} } }"; @@ -196,11 +204,12 @@ class Compiler { /** * @static * @param Tokenizer $tokens - * @param Scope $scope + * @param Scope $scope * @return string * @throws InvalidUsageException */ - public static function forOpen(Tokenizer $tokens, Scope $scope) { + public static function forOpen(Tokenizer $tokens, Scope $scope) + { $p = array("index" => false, "first" => false, "last" => false, "step" => 1, "to" => false, "max" => false, "min" => false); $scope["after"] = $before = $body = array(); $i = array('', ''); @@ -211,40 +220,40 @@ class Compiler { $val = $scope->tpl->parseExp($tokens, true); $p = $scope->tpl->parseParams($tokens, $p); - if(is_numeric($p["step"])) { - if($p["step"] > 0) { + if (is_numeric($p["step"])) { + if ($p["step"] > 0) { $condition = "$var <= {$p['to']}"; - if($p["last"]) $c = "($var + {$p['step']}) > {$p['to']}"; - } elseif($p["step"] < 0) { + if ($p["last"]) $c = "($var + {$p['step']}) > {$p['to']}"; + } elseif ($p["step"] < 0) { $condition = "$var >= {$p['to']}"; - if($p["last"]) $c = "($var + {$p['step']}) < {$p['to']}"; + if ($p["last"]) $c = "($var + {$p['step']}) < {$p['to']}"; } else { throw new InvalidUsageException("Invalid step value if {for}"); } } else { $condition = "({$p['step']} > 0 && $var <= {$p['to']} || {$p['step']} < 0 && $var >= {$p['to']})"; - if($p["last"]) $c = "({$p['step']} > 0 && ($var + {$p['step']}) <= {$p['to']} || {$p['step']} < 0 && ($var + {$p['step']}) >= {$p['to']})"; + if ($p["last"]) $c = "({$p['step']} > 0 && ($var + {$p['step']}) <= {$p['to']} || {$p['step']} < 0 && ($var + {$p['step']}) >= {$p['to']})"; } - if($p["first"]) { - $before[] = $p["first"].' = true'; - $scope["after"][] = $p["first"] .' && ('. $p["first"].' = false )'; + if ($p["first"]) { + $before[] = $p["first"] . ' = true'; + $scope["after"][] = $p["first"] . ' && (' . $p["first"] . ' = false )'; } - if($p["last"]) { - $before[] = $p["last"].' = false'; + if ($p["last"]) { + $before[] = $p["last"] . ' = false'; $body[] = "if($c) {$p['last']} = true"; } - if($p["index"]) { - $i[0] .= $p["index"].' = 0,'; - $i[1] .= $p["index"].'++,'; + if ($p["index"]) { + $i[0] .= $p["index"] . ' = 0,'; + $i[1] .= $p["index"] . '++,'; } $scope["else"] = false; $scope["else_cond"] = "$var==$val"; - $before = $before ? implode("; ", $before).";" : ""; - $body = $body ? implode("; ", $body).";" : ""; - $scope["after"] = $scope["after"] ? implode("; ", $scope["after"]).";" : ""; + $before = $before ? implode("; ", $before) . ";" : ""; + $body = $body ? implode("; ", $body) . ";" : ""; + $scope["after"] = $scope["after"] ? implode("; ", $scope["after"]) . ";" : ""; return "$before for({$i[0]} $var=$val; $condition;{$i[1]} $var+={$p['step']}) { $body"; } @@ -252,10 +261,11 @@ class Compiler { /** * @static * @param Tokenizer $tokens - * @param Scope $scope + * @param Scope $scope * @return string */ - public static function forElse(Tokenizer $tokens, Scope $scope) { + public static function forElse(Tokenizer $tokens, Scope $scope) + { $scope["no-break"] = $scope["no-continue"] = true; $scope["else"] = true; return " } if({$scope['else_cond']}) {"; @@ -264,11 +274,12 @@ class Compiler { /** * @static * @param Tokenizer $tokens - * @param Scope $scope + * @param Scope $scope * @return string */ - public static function forClose($tokens, Scope $scope) { - if($scope["else"]) { + public static function forClose($tokens, Scope $scope) + { + if ($scope["else"]) { return '}'; } else { return " {$scope['after']} }"; @@ -278,11 +289,12 @@ class Compiler { /** * @static * @param Tokenizer $tokens - * @param Scope $scope + * @param Scope $scope * @return string */ - public static function whileOpen(Tokenizer $tokens, Scope $scope) { - return 'while('.$scope->tpl->parseExp($tokens, true).') {'; + public static function whileOpen(Tokenizer $tokens, Scope $scope) + { + return 'while(' . $scope->tpl->parseExp($tokens, true) . ') {'; } /** @@ -290,12 +302,13 @@ class Compiler { * * @static * @param Tokenizer $tokens - * @param Scope $scope + * @param Scope $scope * @return string */ - public static function switchOpen(Tokenizer $tokens, Scope $scope) { + public static function switchOpen(Tokenizer $tokens, Scope $scope) + { $scope["no-break"] = $scope["no-continue"] = true; - $scope["switch"] = 'switch('.$scope->tpl->parseExp($tokens, true).') {'; + $scope["switch"] = 'switch(' . $scope->tpl->parseExp($tokens, true) . ') {'; // lazy init return ''; } @@ -305,14 +318,15 @@ class Compiler { * * @static * @param Tokenizer $tokens - * @param Scope $scope + * @param Scope $scope * @return string */ - public static function tagCase(Tokenizer $tokens, Scope $scope) { - $code = 'case '.$scope->tpl->parseExp($tokens, true).': '; - if($scope["switch"]) { + public static function tagCase(Tokenizer $tokens, Scope $scope) + { + $code = 'case ' . $scope->tpl->parseExp($tokens, true) . ': '; + if ($scope["switch"]) { unset($scope["no-break"], $scope["no-continue"]); - $code = $scope["switch"]."\n".$code; + $code = $scope["switch"] . "\n" . $code; $scope["switch"] = ""; } return $code; @@ -323,12 +337,13 @@ class Compiler { * * @static * @param Tokenizer $tokens - * @param Scope $scope + * @param Scope $scope * @throws InvalidUsageException * @return string */ - public static function tagContinue($tokens, Scope $scope) { - if(empty($scope["no-continue"])) { + public static function tagContinue($tokens, Scope $scope) + { + if (empty($scope["no-continue"])) { return 'continue;'; } else { throw new InvalidUsageException("Improper usage of the tag {continue}"); @@ -343,11 +358,12 @@ class Compiler { * @param Scope $scope * @return string */ - public static function tagDefault($tokens, Scope $scope) { + public static function tagDefault($tokens, Scope $scope) + { $code = 'default: '; - if($scope["switch"]) { + if ($scope["switch"]) { unset($scope["no-break"], $scope["no-continue"]); - $code = $scope["switch"]."\n".$code; + $code = $scope["switch"] . "\n" . $code; $scope["switch"] = ""; } return $code; @@ -358,12 +374,13 @@ class Compiler { * * @static * @param Tokenizer $tokens - * @param Scope $scope + * @param Scope $scope * @throws InvalidUsageException * @return string */ - public static function tagBreak($tokens, Scope $scope) { - if(empty($scope["no-break"])) { + public static function tagBreak($tokens, Scope $scope) + { + if (empty($scope["no-break"])) { return 'break;'; } else { throw new InvalidUsageException("Improper usage of the tag {break}"); @@ -377,32 +394,33 @@ class Compiler { * @throws InvalidUsageException * @return string */ - public static function tagExtends(Tokenizer $tokens, Template $tpl) { - if(!empty($tpl->_extends)) { + public static function tagExtends(Tokenizer $tokens, Template $tpl) + { + if (!empty($tpl->_extends)) { throw new InvalidUsageException("Only one {extends} allowed"); - } elseif($tpl->getStackSize()) { + } elseif ($tpl->getStackSize()) { throw new InvalidUsageException("Tags {extends} can not be nested"); } $tpl_name = $tpl->parsePlainArg($tokens, $name); - if(empty($tpl->_extended)) { - $tpl->addPostCompile(__CLASS__."::extendBody"); + if (empty($tpl->_extended)) { + $tpl->addPostCompile(__CLASS__ . "::extendBody"); } - if($tpl->getOptions() & Template::DYNAMIC_EXTEND) { + if ($tpl->getOptions() & Template::DYNAMIC_EXTEND) { $tpl->_compatible = true; } - if($name) { // static extends + if ($name) { // static extends $tpl->_extends = $tpl->getStorage()->getRawTemplate()->load($name, false); - if(!isset($tpl->_compatible)) { - $tpl->_compatible = &$tpl->_extends->_compatible; + if (!isset($tpl->_compatible)) { + $tpl->_compatible = & $tpl->_extends->_compatible; } $tpl->addDepend($tpl->_extends); return ""; } else { // dynamic extends - if(!isset($tpl->_compatible)) { + if (!isset($tpl->_compatible)) { $tpl->_compatible = true; } $tpl->_extends = $tpl_name; - return '$parent = $tpl->getStorage()->getTemplate('.$tpl_name.', \Fenom\Template::EXTENDED);'; + return '$parent = $tpl->getStorage()->getTemplate(' . $tpl_name . ', \Fenom\Template::EXTENDED);'; } } @@ -411,35 +429,36 @@ class Compiler { * @param string $body * @param Template $tpl */ - public static function extendBody(&$body, $tpl) { + public static function extendBody(&$body, $tpl) + { $t = $tpl; - if($tpl->uses) { + if ($tpl->uses) { $tpl->blocks += $tpl->uses; } - while(isset($t->_extends)) { + while (isset($t->_extends)) { $t = $t->_extends; - if(is_object($t)) { + if (is_object($t)) { /* @var \Fenom\Template $t */ $t->_extended = true; $tpl->addDepend($t); - $t->_compatible = &$tpl->_compatible; - $t->blocks = &$tpl->blocks; + $t->_compatible = & $tpl->_compatible; + $t->blocks = & $tpl->blocks; $t->compile(); - if($t->uses) { + if ($t->uses) { $tpl->blocks += $t->uses; } - if(!isset($t->_extends)) { // last item => parent - if(empty($tpl->_compatible)) { + if (!isset($t->_extends)) { // last item => parent + if (empty($tpl->_compatible)) { $body = $t->getBody(); } else { - $body = ''.$body.''.$t->getBody(); + $body = '' . $body . '' . $t->getBody(); } return; } else { $body .= $t->getBody(); } } else { - $body = ''.$body.'b = &$tpl->b; $parent->display((array)$tpl); unset($tpl->b, $parent->b); ?>'; + $body = '' . $body . 'b = &$tpl->b; $parent->display((array)$tpl); unset($tpl->b, $parent->b); ?>'; return; } } @@ -452,34 +471,35 @@ class Compiler { * @throws InvalidUsageException * @return string */ - public static function tagUse(Tokenizer $tokens, Template $tpl) { - if($tpl->getStackSize()) { + public static function tagUse(Tokenizer $tokens, Template $tpl) + { + if ($tpl->getStackSize()) { throw new InvalidUsageException("Tags {use} can not be nested"); } $cname = $tpl->parsePlainArg($tokens, $name); - if($name) { + if ($name) { $donor = $tpl->getStorage()->getRawTemplate()->load($name, false); $donor->_extended = true; $donor->_extends = $tpl; - $donor->_compatible = &$tpl->_compatible; + $donor->_compatible = & $tpl->_compatible; //$donor->blocks = &$tpl->blocks; $donor->compile(); $blocks = $donor->blocks; - foreach($blocks as $name => $code) { - if(isset($tpl->blocks[$name])) { + foreach ($blocks as $name => $code) { + if (isset($tpl->blocks[$name])) { $tpl->blocks[$name] = $code; unset($blocks[$name]); } } $tpl->uses = $blocks + $tpl->uses; $tpl->addDepend($donor); - return '?>'.$donor->getBody().'' . $donor->getBody() . '_compatible = true; - return '$donor = $tpl->getStorage()->getTemplate('.$cname.', \Fenom\Template::EXTENDED);'.PHP_EOL. - '$donor->fetch((array)$tpl);'.PHP_EOL. + return '$donor = $tpl->getStorage()->getTemplate(' . $cname . ', \Fenom\Template::EXTENDED);' . PHP_EOL . + '$donor->fetch((array)$tpl);' . PHP_EOL . '$tpl->b += (array)$donor->b'; } } @@ -491,12 +511,13 @@ class Compiler { * @return string * @throws InvalidUsageException */ - public static function tagBlockOpen(Tokenizer $tokens, Scope $scope) { - if($scope->level > 0) { + public static function tagBlockOpen(Tokenizer $tokens, Scope $scope) + { + if ($scope->level > 0) { $scope->tpl->_compatible = true; } $scope["cname"] = $scope->tpl->parsePlainArg($tokens, $name); - $scope["name"] = $name; + $scope["name"] = $name; } /** @@ -505,55 +526,56 @@ class Compiler { * @param Scope $scope * @return string */ - public static function tagBlockClose($tokens, Scope $scope) { + public static function tagBlockClose($tokens, Scope $scope) + { $tpl = $scope->tpl; - if(isset($tpl->_extends)) { // is child - if($scope["name"]) { // is scalar name - if($tpl->_compatible) { // is compatible mode + if (isset($tpl->_extends)) { // is child + if ($scope["name"]) { // is scalar name + if ($tpl->_compatible) { // is compatible mode $scope->replaceContent( - 'b['.$scope["cname"].'])) { '. - '$tpl->b['.$scope["cname"].'] = function($tpl) { ?>'.PHP_EOL. - $scope->getContent(). - "".PHP_EOL + 'b[' . $scope["cname"] . '])) { ' . + '$tpl->b[' . $scope["cname"] . '] = function($tpl) { ?>' . PHP_EOL . + $scope->getContent() . + "" . PHP_EOL ); - } elseif(!isset($tpl->blocks[ $scope["name"] ])) { // is block not registered - $tpl->blocks[ $scope["name"] ] = $scope->getContent(); + } elseif (!isset($tpl->blocks[$scope["name"]])) { // is block not registered + $tpl->blocks[$scope["name"]] = $scope->getContent(); $scope->replaceContent( - '_compatible.' */'.PHP_EOL.' $tpl->b['.$scope["cname"].'] = function($tpl) { ?>'.PHP_EOL. - $scope->getContent(). - "".PHP_EOL + '_compatible . ' */' . PHP_EOL . ' $tpl->b[' . $scope["cname"] . '] = function($tpl) { ?>' . PHP_EOL . + $scope->getContent() . + "" . PHP_EOL ); } } else { // dynamic name $tpl->_compatible = true; // enable compatible mode $scope->replaceContent( - 'b['.$scope["cname"].'])) { '. - '$tpl->b['.$scope["cname"].'] = function($tpl) { ?>'.PHP_EOL. - $scope->getContent(). - "".PHP_EOL + 'b[' . $scope["cname"] . '])) { ' . + '$tpl->b[' . $scope["cname"] . '] = function($tpl) { ?>' . PHP_EOL . + $scope->getContent() . + "" . PHP_EOL ); } - } else { // is parent - if(isset($tpl->blocks[ $scope["name"] ])) { // has block - if($tpl->_compatible) { // compatible mode enabled + } else { // is parent + if (isset($tpl->blocks[$scope["name"]])) { // has block + if ($tpl->_compatible) { // compatible mode enabled $scope->replaceContent( - 'b['.$scope["cname"].'])) { echo $tpl->b['.$scope["cname"].']->__invoke($tpl); } else {?>'.PHP_EOL. - $tpl->blocks[ $scope["name"] ]. - ''.PHP_EOL + 'b[' . $scope["cname"] . '])) { echo $tpl->b[' . $scope["cname"] . ']->__invoke($tpl); } else {?>' . PHP_EOL . + $tpl->blocks[$scope["name"]] . + '' . PHP_EOL ); } else { - $scope->replaceContent($tpl->blocks[ $scope["name"] ]); + $scope->replaceContent($tpl->blocks[$scope["name"]]); } // } elseif(isset($tpl->_extended) || !empty($tpl->_compatible)) { - } elseif(isset($tpl->_extended) && $tpl->_compatible || empty($tpl->_extended)) { + } elseif (isset($tpl->_extended) && $tpl->_compatible || empty($tpl->_extended)) { $scope->replaceContent( - 'b['.$scope["cname"].'])) { echo $tpl->b['.$scope["cname"].']->__invoke($tpl); } else {?>'.PHP_EOL. - $scope->getContent(). - ''.PHP_EOL + 'b[' . $scope["cname"] . '])) { echo $tpl->b[' . $scope["cname"] . ']->__invoke($tpl); } else {?>' . PHP_EOL . + $scope->getContent() . + '' . PHP_EOL ); } } @@ -561,8 +583,9 @@ class Compiler { } - public static function tagParent($tokens, Scope $scope) { - if(empty($scope->tpl->_extends)) { + public static function tagParent($tokens, Scope $scope) + { + if (empty($scope->tpl->_extends)) { throw new InvalidUsageException("Tag {parent} may be declared in children"); } } @@ -573,7 +596,8 @@ class Compiler { * @static * @return string */ - public static function stdClose() { + public static function stdClose() + { return '}'; } @@ -581,13 +605,14 @@ class Compiler { * Standard function parser * * @static - * @param mixed $function + * @param mixed $function * @param Tokenizer $tokens - * @param Template $tpl + * @param Template $tpl * @return string */ - public static function stdFuncParser($function, Tokenizer $tokens, Template $tpl) { - return "$function(".self::toArray($tpl->parseParams($tokens)).', $tpl)'; + public static function stdFuncParser($function, Tokenizer $tokens, Template $tpl) + { + return "$function(" . self::toArray($tpl->parseParams($tokens)) . ', $tpl)'; } /** @@ -596,11 +621,12 @@ class Compiler { * @static * @param $function * @param Tokenizer $tokens - * @param Template $tpl + * @param Template $tpl * @return string */ - public static function smartFuncParser($function, Tokenizer $tokens, Template $tpl) { - if(strpos($function, "::")) { + public static function smartFuncParser($function, Tokenizer $tokens, Template $tpl) + { + if (strpos($function, "::")) { list($class, $method) = explode("::", $function, 2); $ref = new \ReflectionMethod($class, $method); } else { @@ -608,16 +634,16 @@ class Compiler { } $args = array(); $params = $tpl->parseParams($tokens); - foreach($ref->getParameters() as $param) { - if(isset($params[ $param->getName() ])) { - $args[] = $params[ $param->getName() ]; - } elseif(isset($params[ $param->getPosition() ])) { - $args[] = $params[ $param->getPosition() ]; - } elseif($param->isOptional()) { + foreach ($ref->getParameters() as $param) { + if (isset($params[$param->getName()])) { + $args[] = $params[$param->getName()]; + } elseif (isset($params[$param->getPosition()])) { + $args[] = $params[$param->getPosition()]; + } elseif ($param->isOptional()) { $args[] = var_export($param->getDefaultValue(), true); } } - return "$function(".implode(", ", $args).')'; + return "$function(" . implode(", ", $args) . ')'; } /** @@ -625,10 +651,11 @@ class Compiler { * * @static * @param Tokenizer $tokens - * @param Scope $scope + * @param Scope $scope * @return string */ - public static function stdFuncOpen(Tokenizer $tokens, Scope $scope) { + public static function stdFuncOpen(Tokenizer $tokens, Scope $scope) + { $scope["params"] = self::toArray($scope->tpl->parseParams($tokens)); return 'ob_start();'; } @@ -638,11 +665,12 @@ class Compiler { * * @static * @param Tokenizer $tokens - * @param Scope $scope + * @param Scope $scope * @return string */ - public static function stdFuncClose($tokens, Scope $scope) { - return $scope["function"].'('.$scope["params"].', ob_get_clean(), $tpl)'; + public static function stdFuncClose($tokens, Scope $scope) + { + return $scope["function"] . '(' . $scope["params"] . ', ob_get_clean(), $tpl)'; } /** @@ -650,13 +678,14 @@ class Compiler { * @param $params * @return string */ - public static function toArray($params) { + public static function toArray($params) + { $_code = array(); - foreach($params as $k => $v) { - $_code[] = '"'.$k.'" => '.$v; + foreach ($params as $k => $v) { + $_code[] = '"' . $k . '" => ' . $v; } - return 'array('.implode(",", $_code).')'; + return 'array(' . implode(",", $_code) . ')'; } /** @@ -664,19 +693,20 @@ class Compiler { * @param Scope $scope * @return string */ - public static function varOpen(Tokenizer $tokens, Scope $scope) { + public static function varOpen(Tokenizer $tokens, Scope $scope) + { $var = $scope->tpl->parseVariable($tokens, Template::DENY_MODS); - if($tokens->is('=')) { // inline tag {var ...} + if ($tokens->is('=')) { // inline tag {var ...} $scope->is_closed = true; $tokens->next(); - if($tokens->is("[")) { - return $var.'='.$scope->tpl->parseArray($tokens); + if ($tokens->is("[")) { + return $var . '=' . $scope->tpl->parseArray($tokens); } else { - return $var.'='.$scope->tpl->parseExp($tokens, true); + return $var . '=' . $scope->tpl->parseExp($tokens, true); } } else { $scope["name"] = $var; - if($tokens->is('|')) { + if ($tokens->is('|')) { $scope["value"] = $scope->tpl->parseModifier($tokens, "ob_get_clean()"); } else { $scope["value"] = "ob_get_clean()"; @@ -690,8 +720,9 @@ class Compiler { * @param Scope $scope * @return string */ - public static function varClose(Tokenizer $tokens, Scope $scope) { - return $scope["name"].'='.$scope["value"].';'; + public static function varClose(Tokenizer $tokens, Scope $scope) + { + return $scope["name"] . '=' . $scope["value"] . ';'; } @@ -700,7 +731,8 @@ class Compiler { * @param Scope $scope * @return string */ - public static function filterOpen(Tokenizer $tokens, Scope $scope) { + public static function filterOpen(Tokenizer $tokens, Scope $scope) + { $scope["filter"] = $scope->tpl->parseModifier($tokens, "ob_get_clean()"); return "ob_start();"; } @@ -710,8 +742,9 @@ class Compiler { * @param Scope $scope * @return string */ - public static function filterClose($tokens, Scope $scope) { - return "echo ".$scope["filter"].";"; + public static function filterClose($tokens, Scope $scope) + { + return "echo " . $scope["filter"] . ";"; } /** @@ -722,22 +755,23 @@ class Compiler { * @return string * @throws InvalidUsageException */ - public static function tagCycle(Tokenizer $tokens, Template $tpl) { - if($tokens->is("[")) { + public static function tagCycle(Tokenizer $tokens, Template $tpl) + { + if ($tokens->is("[")) { $exp = $tpl->parseArray($tokens); } else { $exp = $tpl->parseExp($tokens, true); } - if($tokens->valid()) { + if ($tokens->valid()) { $p = $tpl->parseParams($tokens); - if(empty($p["index"])) { + if (empty($p["index"])) { throw new InvalidUsageException("Cycle may contain only index attribute"); } else { - return 'echo '.__CLASS__.'::cycle('.$exp.', '.$p["index"].')'; + return 'echo ' . __CLASS__ . '::cycle(' . $exp . ', ' . $p["index"] . ')'; } } else { $var = $tpl->tmpVar(); - return 'echo '.__CLASS__.'::cycle('.$exp.", isset($var) ? ++$var : ($var = 0) )"; + return 'echo ' . __CLASS__ . '::cycle(' . $exp . ", isset($var) ? ++$var : ($var = 0) )"; } } @@ -747,7 +781,8 @@ class Compiler { * @param $index * @return mixed */ - public static function cycle($vals, $index) { + public static function cycle($vals, $index) + { return $vals[$index % count($vals)]; } @@ -760,36 +795,37 @@ class Compiler { * @throws InvalidUsageException * @return string */ - public static function tagImport(Tokenizer $tokens, Template $tpl) { + public static function tagImport(Tokenizer $tokens, Template $tpl) + { $import = array(); - if($tokens->is('[')) { + if ($tokens->is('[')) { $tokens->next(); - while($tokens->valid()) { - if($tokens->is(Tokenizer::MACRO_STRING)) { - $import[ $tokens->current() ] = true; + while ($tokens->valid()) { + if ($tokens->is(Tokenizer::MACRO_STRING)) { + $import[$tokens->current()] = true; $tokens->next(); - } elseif($tokens->is(']')) { + } elseif ($tokens->is(']')) { $tokens->next(); break; - } elseif($tokens->is(',')) { + } elseif ($tokens->is(',')) { $tokens->next(); } else { break; } } - if($tokens->current() != "from") { + if ($tokens->current() != "from") { throw new UnexpectedTokenException($tokens); } $tokens->next(); } $tpl->parsePlainArg($tokens, $name); - if(!$name) { + if (!$name) { throw new InvalidUsageException("Invalid usage tag {import}"); } - if($tokens->is(T_AS)) { + if ($tokens->is(T_AS)) { $alias = $tokens->next()->get(Tokenizer::MACRO_STRING); - if($alias === "macro") { + if ($alias === "macro") { $alias = ""; } $tokens->next(); @@ -797,16 +833,16 @@ class Compiler { $alias = ""; } $donor = $tpl->getStorage()->getRawTemplate()->load($name, true); - if($donor->macros) { - foreach($donor->macros as $name => $macro) { - if($p = strpos($name, ".")) { + if ($donor->macros) { + foreach ($donor->macros as $name => $macro) { + if ($p = strpos($name, ".")) { $name = substr($name, $p); } - if($import && !isset($import[$name])) { + if ($import && !isset($import[$name])) { continue; } - if($alias) { - $name = $alias.'.'.$name; + if ($alias) { + $name = $alias . '.' . $name; } $tpl->macros[$name] = $macro; } @@ -823,24 +859,25 @@ class Compiler { * @param Scope $scope * @throws InvalidUsageException */ - public static function macroOpen(Tokenizer $tokens, Scope $scope) { + public static function macroOpen(Tokenizer $tokens, Scope $scope) + { $scope["name"] = $tokens->get(Tokenizer::MACRO_STRING); $scope["recursive"] = array(); $args = array(); $defaults = array(); - if(!$tokens->valid()) { + if (!$tokens->valid()) { return; } $tokens->next()->need('(')->next(); - if($tokens->is(')')) { + if ($tokens->is(')')) { return; } - while($tokens->is(Tokenizer::MACRO_STRING, T_VARIABLE)) { + while ($tokens->is(Tokenizer::MACRO_STRING, T_VARIABLE)) { $args[] = $param = $tokens->getAndNext(); - if($tokens->is('=')) { + if ($tokens->is('=')) { $tokens->next(); - if($tokens->is(T_CONSTANT_ENCAPSED_STRING, T_LNUMBER, T_DNUMBER) || $tokens->isSpecialVal()) { - $defaults[ $param ] = $tokens->getAndNext(); + if ($tokens->is(T_CONSTANT_ENCAPSED_STRING, T_LNUMBER, T_DNUMBER) || $tokens->isSpecialVal()) { + $defaults[$param] = $tokens->getAndNext(); } else { throw new InvalidUsageException("Macro parameters may have only scalar defaults"); } @@ -861,24 +898,25 @@ class Compiler { * @param Tokenizer $tokens * @param Scope $scope */ - public static function macroClose(Tokenizer $tokens, Scope $scope) { - if($scope["recursive"]) { + public static function macroClose(Tokenizer $tokens, Scope $scope) + { + if ($scope["recursive"]) { $switch = "switch(\$call['mark']) {\n"; - foreach($scope["recursive"] as $mark) { + foreach ($scope["recursive"] as $mark) { $switch .= "case $mark: goto macro_$mark;\n"; } $switch .= "}"; - $stack = '$stack_'.$scope["macro"]['id']; - $scope["macro"]["body"] = ''.$scope->cutContent().''; + $stack = '$stack_' . $scope["macro"]['id']; + $scope["macro"]["body"] = '' . $scope->cutContent() . ''; } else { $scope["macro"]["body"] = $scope->cutContent(); } - $scope->tpl->macros[ $scope["name"] ] = $scope["macro"]; + $scope->tpl->macros[$scope["name"]] = $scope["macro"]; } /** @@ -889,13 +927,14 @@ class Compiler { * @throws InvalidUsageException * @return string */ - public static function tagRaw(Tokenizer $tokens, Template $tpl) { + public static function tagRaw(Tokenizer $tokens, Template $tpl) + { $escape = (bool)$tpl->escape; $tpl->escape = false; - if($tokens->is(':')) { + if ($tokens->is(':')) { $func = $tokens->getNext(Tokenizer::MACRO_STRING); $tag = $tpl->getStorage()->getTag($func, $tpl); - if($tag["type"] == \Fenom::INLINE_FUNCTION) { + if ($tag["type"] == \Fenom::INLINE_FUNCTION) { $code = $tpl->parseAct($tokens); } elseif ($tag["type"] == \Fenom::BLOCK_FUNCTION) { $code = $tpl->parseAct($tokens); @@ -915,7 +954,8 @@ class Compiler { * @param Tokenizer $tokens * @param Scope $scope */ - public static function autoescapeOpen(Tokenizer $tokens, Scope $scope) { + public static function autoescapeOpen(Tokenizer $tokens, Scope $scope) + { $boolean = ($tokens->get(T_STRING) == "true" ? true : false); $scope["escape"] = $scope->tpl->escape; $scope->tpl->escape = $boolean; @@ -926,7 +966,8 @@ class Compiler { * @param Tokenizer $tokens * @param Scope $scope */ - public static function autoescapeClose(Tokenizer $tokens, Scope $scope) { + public static function autoescapeClose(Tokenizer $tokens, Scope $scope) + { $scope->tpl->escape = $scope["escape"]; } @@ -938,14 +979,15 @@ class Compiler { * @return string * @throws InvalidUsageException */ - public static function tagUnset(Tokenizer $tokens, Template $tpl) { + public static function tagUnset(Tokenizer $tokens, Template $tpl) + { $vars = array(); - while($tokens->valid()) { + while ($tokens->valid()) { $vars[] = $tpl->parseVar($tokens); } - if(!$vars) { + if (!$vars) { throw new InvalidUsageException("Unset must accept variable(s)"); } - return 'unset('.implode(', ', $vars).')'; + return 'unset(' . implode(', ', $vars) . ')'; } } diff --git a/src/Fenom/Modifier.php b/src/Fenom/Modifier.php index 95b50b7..3da9279 100644 --- a/src/Fenom/Modifier.php +++ b/src/Fenom/Modifier.php @@ -13,7 +13,8 @@ namespace Fenom; * Collection of modifiers * @author Ivan Shalganov */ -class Modifier { +class Modifier +{ /** * Date format @@ -22,10 +23,11 @@ class Modifier { * @param string $format * @return string */ - public static function dateFormat($date, $format = "%b %e, %Y") { - if(is_string($date) && !is_numeric($date)) { + public static function dateFormat($date, $format = "%b %e, %Y") + { + if (is_string($date) && !is_numeric($date)) { $date = strtotime($date); - if(!$date) $date = time(); + if (!$date) $date = time(); } return strftime($format, $date); } @@ -35,10 +37,11 @@ class Modifier { * @param string $format * @return string */ - public static function date($date, $format = "Y m d") { - if(is_string($date) && !is_numeric($date)) { + public static function date($date, $format = "Y m d") + { + if (is_string($date) && !is_numeric($date)) { $date = strtotime($date); - if(!$date) $date = time(); + if (!$date) $date = time(); } return date($format, $date); } @@ -50,8 +53,9 @@ class Modifier { * @param string $type * @return string */ - public static function escape($text, $type = 'html') { - switch(strtolower($type)) { + public static function escape($text, $type = 'html') + { + switch (strtolower($type)) { case "url": return urlencode($text); case "html"; @@ -68,8 +72,9 @@ class Modifier { * @param string $type * @return string */ - public static function unescape($text, $type = 'html') { - switch(strtolower($type)) { + public static function unescape($text, $type = 'html') + { + switch (strtolower($type)) { case "url": return urldecode($text); case "html"; @@ -89,23 +94,24 @@ class Modifier { * @param bool $middle * @return string */ - public static function truncate($string, $length = 80, $etc = '...', $by_words = false, $middle = false) { - if($middle) { - if(preg_match('#^(.{'.$length.'}).*?(.{'.$length.'})?$#usS', $string, $match)) { - if(count($match) == 3) { - if($by_words) { - return preg_replace('#\s.*$#usS', "", $match[1]).$etc.preg_replace('#^.*\s#usS', "", $match[2]); + public static function truncate($string, $length = 80, $etc = '...', $by_words = false, $middle = false) + { + if ($middle) { + if (preg_match('#^(.{' . $length . '}).*?(.{' . $length . '})?$#usS', $string, $match)) { + if (count($match) == 3) { + if ($by_words) { + return preg_replace('#\s.*$#usS', "", $match[1]) . $etc . preg_replace('#^.*\s#usS', "", $match[2]); } else { - return $match[1].$etc.$match[2]; + return $match[1] . $etc . $match[2]; } } } } else { - if(preg_match('#^(.{'.$length.'})#usS', $string, $match)) { - if($by_words) { - return preg_replace('#\s.*$#usS', "", $match[1]).$etc; + if (preg_match('#^(.{' . $length . '})#usS', $string, $match)) { + if ($by_words) { + return preg_replace('#\s.*$#usS', "", $match[1]) . $etc; } else { - return $match[1].$etc; + return $match[1] . $etc; } } } @@ -119,9 +125,10 @@ class Modifier { * @param bool $to_line strip line ends * @return string */ - public static function strip($str, $to_line = false) { + public static function strip($str, $to_line = false) + { $str = trim($str); - if($to_line) { + if ($to_line) { return preg_replace('#[\s]+#ms', ' ', $str); } else { return preg_replace('#[ \t]{2,}#', ' ', $str); @@ -133,12 +140,13 @@ class Modifier { * @param mixed $item * @return int */ - public static function length($item) { - if(is_string($item)) { + public static function length($item) + { + if (is_string($item)) { return strlen(preg_replace('#[\x00-\x7F]|[\x80-\xDF][\x00-\xBF]|[\xE0-\xEF][\x00-\xBF]{2}#s', ' ', $item)); } elseif (is_array($item)) { return count($item); - } elseif($item instanceof \Countable) { + } elseif ($item instanceof \Countable) { return count($item); } else { return 0; @@ -151,10 +159,11 @@ class Modifier { * @param mixed $haystack * @return bool */ - public static function in($value, $haystack) { - if(is_array($haystack)) { + public static function in($value, $haystack) + { + if (is_array($haystack)) { return in_array($value, $haystack) || array_key_exists($value, $haystack); - } elseif(is_string($haystack)) { + } elseif (is_string($haystack)) { return strpos($haystack, $value) !== false; } return false; @@ -164,7 +173,8 @@ class Modifier { * @param $value * @return bool */ - public static function isIterable($value) { + public static function isIterable($value) + { return is_array($value) || ($value instanceof \Iterator); } } diff --git a/src/Fenom/Provider.php b/src/Fenom/Provider.php index dc3812a..0e66bcf 100644 --- a/src/Fenom/Provider.php +++ b/src/Fenom/Provider.php @@ -10,11 +10,13 @@ namespace Fenom; use Fenom\ProviderInterface; + /** * Base template provider * @author Ivan Shalganov */ -class Provider implements ProviderInterface { +class Provider implements ProviderInterface +{ private $_path; /** @@ -22,10 +24,11 @@ class Provider implements ProviderInterface { * * @param string $path */ - public static function clean($path) { - if(is_file($path)) { + public static function clean($path) + { + if (is_file($path)) { unlink($path); - } elseif(is_dir($path)) { + } elseif (is_dir($path)) { $iterator = iterator_to_array( new \RecursiveIteratorIterator( new \RecursiveDirectoryIterator($path, @@ -33,13 +36,13 @@ class Provider implements ProviderInterface { \RecursiveIteratorIterator::CHILD_FIRST ) ); - foreach($iterator as $file) { - /* @var \splFileInfo $file*/ - if($file->isFile()) { - if(strpos($file->getBasename(), ".") !== 0) { + foreach ($iterator as $file) { + /* @var \splFileInfo $file */ + if ($file->isFile()) { + if (strpos($file->getBasename(), ".") !== 0) { unlink($file->getRealPath()); } - } elseif($file->isDir()) { + } elseif ($file->isDir()) { rmdir($file->getRealPath()); } } @@ -51,9 +54,10 @@ class Provider implements ProviderInterface { * * @param string $path */ - public static function rm($path) { + public static function rm($path) + { self::clean($path); - if(is_dir($path)) { + if (is_dir($path)) { rmdir($path); } } @@ -62,8 +66,9 @@ class Provider implements ProviderInterface { * @param string $template_dir directory of templates * @throws \LogicException if directory doesn't exists */ - public function __construct($template_dir) { - if($_dir = realpath($template_dir)) { + public function __construct($template_dir) + { + if ($_dir = realpath($template_dir)) { $this->_path = $_dir; } else { throw new \LogicException("Template directory {$template_dir} doesn't exists"); @@ -76,7 +81,8 @@ class Provider implements ProviderInterface { * @param int $time load last modified time * @return string */ - public function getSource($tpl, &$time) { + public function getSource($tpl, &$time) + { $tpl = $this->_getTemplatePath($tpl); clearstatcache(null, $tpl); $time = filemtime($tpl); @@ -88,7 +94,8 @@ class Provider implements ProviderInterface { * @param string $tpl * @return int */ - public function getLastModified($tpl) { + public function getLastModified($tpl) + { clearstatcache(null, $tpl = $this->_getTemplatePath($tpl)); return filemtime($tpl); } @@ -99,7 +106,8 @@ class Provider implements ProviderInterface { * @param string $extension all templates must have this extension, default .tpl * @return array|\Iterator */ - public function getList($extension = "tpl") { + public function getList($extension = "tpl") + { $list = array(); $iterator = new \RecursiveIteratorIterator( new \RecursiveDirectoryIterator($this->_path, @@ -107,9 +115,9 @@ class Provider implements ProviderInterface { \RecursiveIteratorIterator::CHILD_FIRST ); $path_len = strlen($this->_path); - foreach($iterator as $file) { + foreach ($iterator as $file) { /* @var \SplFileInfo $file */ - if($file->isFile() && $file->getExtension() == $extension) { + if ($file->isFile() && $file->getExtension() == $extension) { $list[] = substr($file->getPathname(), $path_len + 1); } } @@ -122,8 +130,9 @@ class Provider implements ProviderInterface { * @return string * @throws \RuntimeException */ - protected function _getTemplatePath($tpl) { - if(($path = realpath($this->_path."/".$tpl)) && strpos($path, $this->_path) === 0) { + protected function _getTemplatePath($tpl) + { + if (($path = realpath($this->_path . "/" . $tpl)) && strpos($path, $this->_path) === 0) { return $path; } else { throw new \RuntimeException("Template $tpl not found"); @@ -134,8 +143,9 @@ class Provider implements ProviderInterface { * @param string $tpl * @return bool */ - public function templateExists($tpl) { - return file_exists($this->_path."/".$tpl); + public function templateExists($tpl) + { + return file_exists($this->_path . "/" . $tpl); } /** @@ -144,10 +154,11 @@ class Provider implements ProviderInterface { * @param array $templates [template_name => modified, ...] By conversation, you may trust the template's name * @return bool */ - public function verify(array $templates) { - foreach($templates as $template => $mtime) { - clearstatcache(null, $template = $this->_path.'/'.$template); - if(@filemtime($template) !== $mtime) { + public function verify(array $templates) + { + foreach ($templates as $template => $mtime) { + clearstatcache(null, $template = $this->_path . '/' . $template); + if (@filemtime($template) !== $mtime) { return false; } diff --git a/src/Fenom/ProviderInterface.php b/src/Fenom/ProviderInterface.php index b770c62..c0bbca0 100644 --- a/src/Fenom/ProviderInterface.php +++ b/src/Fenom/ProviderInterface.php @@ -14,7 +14,8 @@ namespace Fenom; * @package Fenom * @author Ivan Shalganov */ -interface ProviderInterface { +interface ProviderInterface +{ /** * @param string $tpl * @return bool diff --git a/src/Fenom/Render.php b/src/Fenom/Render.php index 2f08fed..15a8b1d 100644 --- a/src/Fenom/Render.php +++ b/src/Fenom/Render.php @@ -14,7 +14,8 @@ use Fenom; * Primitive template * @author Ivan Shalganov */ -class Render extends \ArrayObject { +class Render extends \ArrayObject +{ private static $_props = array( "name" => "runtime", "base_name" => "", @@ -72,7 +73,8 @@ class Render extends \ArrayObject { * @param callable $code template body * @param array $props */ - public function __construct(Fenom $fenom, \Closure $code, array $props = array()) { + public function __construct(Fenom $fenom, \Closure $code, array $props = array()) + { $this->_fenom = $fenom; $props += self::$_props; $this->_name = $props["name"]; @@ -86,7 +88,8 @@ class Render extends \ArrayObject { * Get template storage * @return \Fenom */ - public function getStorage() { + public function getStorage() + { return $this->_fenom; } @@ -94,7 +97,8 @@ class Render extends \ArrayObject { * Get depends list * @return array */ - public function getDepends() { + public function getDepends() + { return $this->_depends; } @@ -102,7 +106,8 @@ class Render extends \ArrayObject { * Get schema name * @return string */ - public function getScm() { + public function getScm() + { return $this->_scm; } @@ -110,7 +115,8 @@ class Render extends \ArrayObject { * Get provider of template source * @return ProviderInterface */ - public function getProvider() { + public function getProvider() + { return $this->_fenom->getProvider($this->_scm); } @@ -118,7 +124,8 @@ class Render extends \ArrayObject { * Get name without schema * @return string */ - public function getBaseName() { + public function getBaseName() + { return $this->_base_name; } @@ -126,14 +133,16 @@ class Render extends \ArrayObject { * Get parse options * @return int */ - public function getOptions() { + public function getOptions() + { return $this->_options; } /** * @return string */ - public function __toString() { + public function __toString() + { return $this->_name; } @@ -141,11 +150,13 @@ class Render extends \ArrayObject { * Get template name * @return string */ - public function getName() { + public function getName() + { return $this->_name; } - public function getTime() { + public function getTime() + { return $this->_time; } @@ -154,16 +165,17 @@ class Render extends \ArrayObject { * Validate template * @return bool */ - public function isValid() { - if(count($this->_depends) === 1) { // if no external dependencies, only self + public function isValid() + { + if (count($this->_depends) === 1) { // if no external dependencies, only self $provider = $this->_fenom->getProvider($this->_scm); - if($provider->getLastModified($this->_name) !== $this->_time) { + if ($provider->getLastModified($this->_name) !== $this->_time) { return false; } } else { - foreach($this->_depends as $scm => $templates) { + foreach ($this->_depends as $scm => $templates) { $provider = $this->_fenom->getProvider($scm); - if(!$provider->verify($templates)) { + if (!$provider->verify($templates)) { return false; } } @@ -176,7 +188,8 @@ class Render extends \ArrayObject { * @param array $values for template * @return Render */ - public function display(array $values) { + public function display(array $values) + { $this->exchangeArray($values); $this->_code->__invoke($this); return $this->exchangeArray(array()); @@ -188,7 +201,8 @@ class Render extends \ArrayObject { * @return string * @throws \Exception */ - public function fetch(array $values) { + public function fetch(array $values) + { ob_start(); try { $this->display($values); @@ -205,7 +219,8 @@ class Render extends \ArrayObject { * @param $args * @throws \BadMethodCallException */ - public function __call($method, $args) { - throw new \BadMethodCallException("Unknown method ".$method); + public function __call($method, $args) + { + throw new \BadMethodCallException("Unknown method " . $method); } } diff --git a/src/Fenom/Scope.php b/src/Fenom/Scope.php index 3dda98a..39b3288 100644 --- a/src/Fenom/Scope.php +++ b/src/Fenom/Scope.php @@ -14,7 +14,8 @@ namespace Fenom; * * @author Ivan Shalganov */ -class Scope extends \ArrayObject { +class Scope extends \ArrayObject +{ public $line = 0; public $name; @@ -33,20 +34,21 @@ class Scope extends \ArrayObject { /** * Creating cope * - * @param string $name + * @param string $name * @param Template $tpl - * @param int $line - * @param array $action - * @param int $level + * @param int $line + * @param array $action + * @param int $level * @param $body */ - public function __construct($name, $tpl, $line, $action, $level, &$body) { + public function __construct($name, $tpl, $line, $action, $level, &$body) + { $this->line = $line; $this->name = $name; $this->tpl = $tpl; $this->_action = $action; $this->level = $level; - $this->_body = &$body; + $this->_body = & $body; $this->_offset = strlen($body); } @@ -54,7 +56,8 @@ class Scope extends \ArrayObject { * * @param string $function */ - public function setFuncName($function) { + public function setFuncName($function) + { $this["function"] = $function; $this->is_compiler = false; $this->escape = $this->tpl->escape; @@ -66,7 +69,8 @@ class Scope extends \ArrayObject { * @param Tokenizer $tokenizer * @return mixed */ - public function open($tokenizer) { + public function open($tokenizer) + { return call_user_func($this->_action["open"], $tokenizer, $this); } @@ -77,9 +81,10 @@ class Scope extends \ArrayObject { * @param int $level * @return bool */ - public function hasTag($tag, $level) { - if(isset($this->_action["tags"][$tag])) { - if($level) { + public function hasTag($tag, $level) + { + if (isset($this->_action["tags"][$tag])) { + if ($level) { return isset($this->_action["float_tags"][$tag]); } else { return true; @@ -95,7 +100,8 @@ class Scope extends \ArrayObject { * @param Tokenizer $tokenizer * @return string */ - public function tag($tag, $tokenizer) { + public function tag($tag, $tokenizer) + { return call_user_func($this->_action["tags"][$tag], $tokenizer, $this); } @@ -105,7 +111,8 @@ class Scope extends \ArrayObject { * @param Tokenizer $tokenizer * @return string */ - public function close($tokenizer) { + public function close($tokenizer) + { return call_user_func($this->_action["close"], $tokenizer, $this); } @@ -115,7 +122,8 @@ class Scope extends \ArrayObject { * @throws \LogicException * @return string */ - public function getContent() { + public function getContent() + { return substr($this->_body, $this->_offset); } @@ -125,7 +133,8 @@ class Scope extends \ArrayObject { * @return string * @throws \LogicException */ - public function cutContent() { + public function cutContent() + { $content = substr($this->_body, $this->_offset + 1); $this->_body = substr($this->_body, 0, $this->_offset); return $content; @@ -136,12 +145,14 @@ class Scope extends \ArrayObject { * * @param $new_content */ - public function replaceContent($new_content) { + public function replaceContent($new_content) + { $this->cutContent(); $this->_body .= $new_content; } - public function unEscapeContent() { + public function unEscapeContent() + { } } \ No newline at end of file diff --git a/src/Fenom/Template.php b/src/Fenom/Template.php index 3d51ede..36991e2 100644 --- a/src/Fenom/Template.php +++ b/src/Fenom/Template.php @@ -16,7 +16,8 @@ use Fenom; * @package Fenom * @author Ivan Shalganov */ -class Template extends Render { +class Template extends Render +{ /** * Disable array parser. @@ -94,31 +95,31 @@ class Template extends Render { private $_filter = array(); private static $_checkers = array( - 'integer' => 'is_int(%s)', - 'int' => 'is_int(%s)', - 'float' => 'is_float(%s)', - 'double' => 'is_float(%s)', - 'decimal' => 'is_float(%s)', - 'string' => 'is_string(%s)', - 'bool' => 'is_bool(%s)', - 'boolean' => 'is_bool(%s)', - 'number' => 'is_numeric(%s)', - 'numeric' => 'is_numeric(%s)', - 'scalar' => 'is_scalar(%s)', - 'object' => 'is_object(%s)', + 'integer' => 'is_int(%s)', + 'int' => 'is_int(%s)', + 'float' => 'is_float(%s)', + 'double' => 'is_float(%s)', + 'decimal' => 'is_float(%s)', + 'string' => 'is_string(%s)', + 'bool' => 'is_bool(%s)', + 'boolean' => 'is_bool(%s)', + 'number' => 'is_numeric(%s)', + 'numeric' => 'is_numeric(%s)', + 'scalar' => 'is_scalar(%s)', + 'object' => 'is_object(%s)', 'callable' => 'is_callable(%s)', 'callback' => 'is_callable(%s)', - 'array' => 'is_array(%s)', + 'array' => 'is_array(%s)', 'iterable' => '\Fenom\Modifier::isIterable(%s)', - 'const' => 'defined(%s)', + 'const' => 'defined(%s)', 'template' => '$tpl->getStorage()->templateExists(%s)', - 'empty' => 'empty(%s)', - 'set' => 'isset(%s)', - '_empty' => '!%s', // for none variable - '_set' => '(%s !== null)', // for none variable - 'odd' => '(%s & 1)', - 'even' => '!(%s %% 2)', - 'third' => '!(%s %% 3)' + 'empty' => 'empty(%s)', + 'set' => 'isset(%s)', + '_empty' => '!%s', // for none variable + '_set' => '(%s !== null)', // for none variable + 'odd' => '(%s & 1)', + 'even' => '!(%s %% 2)', + 'third' => '!(%s %% 3)' ); /** @@ -126,7 +127,8 @@ class Template extends Render { * @param int $options * @return \Fenom\Template */ - public function __construct(Fenom $fenom, $options) { + public function __construct(Fenom $fenom, $options) + { $this->_fenom = $fenom; $this->_options = $options; } @@ -135,7 +137,8 @@ class Template extends Render { * Get tag stack size * @return int */ - public function getStackSize() { + public function getStackSize() + { return count($this->_stack); } @@ -145,9 +148,10 @@ class Template extends Render { * @param bool $compile * @return $this */ - public function load($name, $compile = true) { + public function load($name, $compile = true) + { $this->_name = $name; - if($provider = strstr($name, ":", true)) { + if ($provider = strstr($name, ":", true)) { $this->_scm = $provider; $this->_base_name = substr($name, strlen($provider) + 1); } else { @@ -155,7 +159,7 @@ class Template extends Render { } $this->_provider = $this->_fenom->getProvider($provider); $this->_src = $this->_provider->getSource($this->_base_name, $this->_time); - if($compile) { + if ($compile) { $this->compile(); } return $this; @@ -168,10 +172,11 @@ class Template extends Render { * @param bool $compile * @return \Fenom\Template */ - public function source($name, $src, $compile = true) { + public function source($name, $src, $compile = true) + { $this->_name = $name; $this->_src = $src; - if($compile) { + if ($compile) { $this->compile(); } return $this; @@ -182,19 +187,24 @@ class Template extends Render { * * @throws CompileException */ - public function compile() { + public function compile() + { $end = $pos = 0; $this->escape = $this->_options & Fenom::AUTO_ESCAPE; - while(($start = strpos($this->_src, '{', $pos)) !== false) { // search open-symbol of tags - switch($this->_src[$start + 1]) { // check next character - case "\n": case "\r": case "\t": case " ": case "}": // ignore the tag + while (($start = strpos($this->_src, '{', $pos)) !== false) { // search open-symbol of tags + switch ($this->_src[$start + 1]) { // check next character + case "\n": + case "\r": + case "\t": + case " ": + case "}": // ignore the tag $this->_appendText(substr($this->_src, $pos, $start - $pos + 2)); $end = $start + 1; break; case "*": // comment block $end = strpos($this->_src, '*}', $start); // find end of the comment block - if($end === false) { + if ($end === false) { throw new CompileException("Unclosed comment block in line {$this->_line}", 0, 1, $this->_name, $this->_line); } $end++; @@ -209,27 +219,27 @@ class Template extends Render { do { $need_more = false; $end = strpos($this->_src, '}', $end + 1); // search close-symbol of the tag - if($end === false) { // if unexpected end of template + if ($end === false) { // if unexpected end of template throw new CompileException("Unclosed tag in line {$this->_line}", 0, 1, $this->_name, $this->_line); } $tag = substr($this->_src, $start, $end - $start + 1); // variable $tag contains fenom tag '{...}' $_tag = substr($tag, 1, -1); // strip delimiters '{' and '}' - if($this->_ignore) { // check ignore - if($_tag === '/ignore') { // turn off ignore + if ($this->_ignore) { // check ignore + if ($_tag === '/ignore') { // turn off ignore $this->_ignore = false; } else { // still ignore $this->_appendText($tag); } } else { $tokens = new Tokenizer($_tag); // tokenize the tag - if($tokens->isIncomplete()) { // all strings finished? + if ($tokens->isIncomplete()) { // all strings finished? $need_more = true; } else { - $this->_appendCode( $this->parseTag($tokens) , $tag); // start the tag lexer - if($tokens->key()) { // if tokenizer have tokens - throws exceptions - throw new CompileException("Unexpected token '".$tokens->current()."' in {$this} line {$this->_line}, near '{".$tokens->getSnippetAsString(0,0)."' <- there", 0, E_ERROR, $this->_name, $this->_line); + $this->_appendCode($this->parseTag($tokens), $tag); // start the tag lexer + if ($tokens->key()) { // if tokenizer have tokens - throws exceptions + throw new CompileException("Unexpected token '" . $tokens->current() . "' in {$this} line {$this->_line}, near '{" . $tokens->getSnippetAsString(0, 0) . "' <- there", 0, E_ERROR, $this->_name, $this->_line); } } } @@ -242,20 +252,20 @@ class Template extends Render { gc_collect_cycles(); $this->_appendText(substr($this->_src, $end ? $end + 1 : 0)); // append tail of the template - if($this->_stack) { + if ($this->_stack) { $_names = array(); $_line = 0; - foreach($this->_stack as $scope) { - if(!$_line) { + foreach ($this->_stack as $scope) { + if (!$_line) { $_line = $scope->line; } - $_names[] = '{'.$scope->name.'} opened on line '.$scope->line; + $_names[] = '{' . $scope->name . '} opened on line ' . $scope->line; } - throw new CompileException("Unclosed tag".(count($_names) == 1 ? "" : "s").": ".implode(", ", $_names), 0, 1, $this->_name, $_line); + throw new CompileException("Unclosed tag" . (count($_names) == 1 ? "" : "s") . ": " . implode(", ", $_names), 0, 1, $this->_name, $_line); } $this->_src = ""; // cleanup - if($this->_post) { - foreach($this->_post as $cb) { + if ($this->_post) { + foreach ($this->_post as $cb) { call_user_func_array($cb, array(&$this->_body, $this)); } } @@ -266,7 +276,8 @@ class Template extends Render { * Execute some code in loading cache * @param $code */ - public function before($code) { + public function before($code) + { $this->_before .= $code; } @@ -274,8 +285,9 @@ class Template extends Render { * Generate temporary internal template variable * @return string */ - public function tmpVar() { - return '$t'.($this->i++); + public function tmpVar() + { + return '$t' . ($this->i++); } /** @@ -283,16 +295,17 @@ class Template extends Render { * * @param string $text */ - private function _appendText($text) { + private function _appendText($text) + { $this->_line += substr_count($text, "\n"); - if($this->_filter) { - if(strpos($text, "_filter) { + if (strpos($text, "_body .= $text; } else { $fragments = explode("_filter as $filter) { + foreach ($fragments as &$fragment) { + if ($fragment) { + foreach ($this->_filter as $filter) { $fragment = call_user_func($filter, $fragment); } } @@ -300,7 +313,7 @@ class Template extends Render { $this->_body .= implode('', $fragments); } } else { - $this->_body .= str_replace("'.PHP_EOL, $text); + $this->_body .= str_replace("' . PHP_EOL, $text); } } @@ -309,13 +322,14 @@ class Template extends Render { * @param int $code * @return string */ - private function _escapeCode($code) { + private function _escapeCode($code) + { $c = ""; - foreach(token_get_all($code) as $token) { - if(is_string($token)) { + foreach (token_get_all($code) as $token) { + if (is_string($token)) { $c .= $token; - } elseif($token[0] == T_CLOSE_TAG) { - $c .= $token[1].PHP_EOL; + } elseif ($token[0] == T_CLOSE_TAG) { + $c .= $token[1] . PHP_EOL; } else { $c .= $token[1]; } @@ -329,22 +343,24 @@ class Template extends Render { * @param string $code * @param $source */ - private function _appendCode($code, $source) { - if(!$code) { + private function _appendCode($code, $source) + { + if (!$code) { return; } else { $this->_line += substr_count($source, "\n"); - if(strpos($code, '?>') !== false) { + if (strpos($code, '?>') !== false) { $code = $this->_escapeCode($code); // paste PHP_EOL } - $this->_body .= "_name}:{$this->_line}: {$source} */\n $code ?>".PHP_EOL; + $this->_body .= "_name}:{$this->_line}: {$source} */\n $code ?>" . PHP_EOL; } } /** * @param callable[] $cb */ - public function addPostCompile($cb) { + public function addPostCompile($cb) + { $this->_post[] = $cb; } @@ -353,7 +369,8 @@ class Template extends Render { * * @return string */ - public function getBody() { + public function getBody() + { return $this->_body; } @@ -362,26 +379,28 @@ class Template extends Render { * * @return string */ - public function getTemplateCode() { - $before = $this->_before ? $this->_before."\n" : ""; - return "_name."' compiled at ".date('Y-m-d H:i:s')." */\n". - $before. // some code 'before' template - "return new Fenom\\Render(\$fenom, ".$this->_getClosureSource().", ".var_export(array( - "options" => $this->_options, - "provider" => $this->_scm, - "name" => $this->_name, - "base_name" => $this->_base_name, - "time" => $this->_time, - "depends" => $this->_depends - ), true).");\n"; + public function getTemplateCode() + { + $before = $this->_before ? $this->_before . "\n" : ""; + return "_name . "' compiled at " . date('Y-m-d H:i:s') . " */\n" . + $before . // some code 'before' template + "return new Fenom\\Render(\$fenom, " . $this->_getClosureSource() . ", " . var_export(array( + "options" => $this->_options, + "provider" => $this->_scm, + "name" => $this->_name, + "base_name" => $this->_base_name, + "time" => $this->_time, + "depends" => $this->_depends + ), true) . ");\n"; } /** * Return closure code * @return string */ - private function _getClosureSource() { + private function _getClosureSource() + { return "function (\$tpl) {\n?>{$this->_body}_code) { + public function display(array $values) + { + if (!$this->_code) { // evaluate template's code - eval("\$this->_code = ".$this->_getClosureSource().";"); - if(!$this->_code) { + eval("\$this->_code = " . $this->_getClosureSource() . ";"); + if (!$this->_code) { throw new CompileException("Fatal error while creating the template"); } } @@ -408,7 +428,8 @@ class Template extends Render { * Add depends from template * @param Render $tpl */ - public function addDepend(Render $tpl) { + public function addDepend(Render $tpl) + { $this->_depends[$tpl->getScm()][$tpl->getName()] = $tpl->getTime(); } @@ -418,13 +439,15 @@ class Template extends Render { * @param $data * @return string */ - public function out($data) { - if($this->escape) { + public function out($data) + { + if ($this->escape) { return "echo htmlspecialchars($data, ENT_COMPAT, 'UTF-8');"; } else { return "echo $data;"; } } + /** * Tag router * @param Tokenizer $tokens @@ -433,10 +456,11 @@ class Template extends Render { * @throws CompileException * @return string executable PHP code */ - public function parseTag(Tokenizer $tokens) { + public function parseTag(Tokenizer $tokens) + { try { - if($tokens->is(Tokenizer::MACRO_STRING)) { - if($tokens->current() === "ignore") { + if ($tokens->is(Tokenizer::MACRO_STRING)) { + if ($tokens->current() === "ignore") { $this->_ignore = true; $tokens->next(); return ''; @@ -451,11 +475,11 @@ class Template extends Render { return $this->out($this->parseExp($tokens), $tokens); } } catch (InvalidUsageException $e) { - throw new CompileException($e->getMessage()." in {$this} line {$this->_line}", 0, E_ERROR, $this->_name, $this->_line, $e); + throw new CompileException($e->getMessage() . " in {$this} line {$this->_line}", 0, E_ERROR, $this->_name, $this->_line, $e); } catch (\LogicException $e) { - throw new SecurityException($e->getMessage()." in {$this} line {$this->_line}, near '{".$tokens->getSnippetAsString(0,0)."' <- there", 0, E_ERROR, $this->_name, $this->_line, $e); + throw new SecurityException($e->getMessage() . " in {$this} line {$this->_line}, near '{" . $tokens->getSnippetAsString(0, 0) . "' <- there", 0, E_ERROR, $this->_name, $this->_line, $e); } catch (\Exception $e) { - throw new CompileException($e->getMessage()." in {$this} line {$this->_line}, near '{".$tokens->getSnippetAsString(0,0)."' <- there", 0, E_ERROR, $this->_name, $this->_line, $e); + throw new CompileException($e->getMessage() . " in {$this} line {$this->_line}, near '{" . $tokens->getSnippetAsString(0, 0) . "' <- there", 0, E_ERROR, $this->_name, $this->_line, $e); } } @@ -466,18 +490,19 @@ class Template extends Render { * @return string * @throws TokenizeException */ - public function parseEndTag(Tokenizer $tokens) { + public function parseEndTag(Tokenizer $tokens) + { $name = $tokens->getNext(Tokenizer::MACRO_STRING); $tokens->next(); - if(!$this->_stack) { + if (!$this->_stack) { throw new TokenizeException("Unexpected closing of the tag '$name', the tag hasn't been opened"); } /** @var Scope $scope */ $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})"); } - if($scope->is_compiler) { + if ($scope->is_compiler) { return $scope->close($tokens); } else { $code = $this->out($scope->close($tokens)); @@ -490,7 +515,8 @@ class Template extends Render { * Get current scope * @return Scope */ - public function getLastScope() { + public function getLastScope() + { return end($this->_stack); } @@ -503,31 +529,32 @@ class Template extends Render { * @throws TokenizeException * @return string */ - public function parseAct(Tokenizer $tokens) { - if($tokens->is(Tokenizer::MACRO_STRING)) { + public function parseAct(Tokenizer $tokens) + { + if ($tokens->is(Tokenizer::MACRO_STRING)) { $action = $tokens->getAndNext(); } else { return $this->out($this->parseExp($tokens)); // may be math and/or boolean expression } - if($tokens->is("(", T_NAMESPACE, T_DOUBLE_COLON) && !$tokens->isWhiteSpaced()) { // just invoke function or static method + if ($tokens->is("(", T_NAMESPACE, T_DOUBLE_COLON) && !$tokens->isWhiteSpaced()) { // just invoke function or static method $tokens->back(); return $this->out($this->parseExp($tokens)); } - if($tokens->is('.')) { + if ($tokens->is('.')) { $name = $tokens->skip()->get(Tokenizer::MACRO_STRING); - if($action !== "macro") { - $name = $action.".".$name; + if ($action !== "macro") { + $name = $action . "." . $name; } return $this->parseMacroCall($tokens, $name); } - if($tag = $this->_fenom->getTag($action, $this)) { // call some function - switch($tag["type"]) { + if ($tag = $this->_fenom->getTag($action, $this)) { // call some function + switch ($tag["type"]) { case Fenom::BLOCK_COMPILER: $scope = new Scope($action, $this, $this->_line, $tag, count($this->_stack), $this->_body); $code = $scope->open($tokens); - if(!$scope->is_closed) { + if (!$scope->is_closed) { array_push($this->_stack, $scope); } return $code; @@ -547,13 +574,13 @@ class Template extends Render { } } - for($j = $i = count($this->_stack)-1; $i>=0; $i--) { // call function's internal tag - if($this->_stack[$i]->hasTag($action, $j - $i)) { + for ($j = $i = count($this->_stack) - 1; $i >= 0; $i--) { // call function's internal tag + if ($this->_stack[$i]->hasTag($action, $j - $i)) { return $this->_stack[$i]->tag($action, $tokens); } } - if($tags = $this->_fenom->getTagOwners($action)) { // unknown template tag - throw new TokenizeException("Unexpected tag '$action' (this tag can be used with '".implode("', '", $tags)."')"); + if ($tags = $this->_fenom->getTagOwners($action)) { // unknown template tag + throw new TokenizeException("Unexpected tag '$action' (this tag can be used with '" . implode("', '", $tags) . "')"); } else { throw new TokenizeException("Unexpected tag $action"); } @@ -570,69 +597,70 @@ class Template extends Render { * @throws \Exception * @return string */ - public function parseExp(Tokenizer $tokens, $required = false) { - $_exp = array(); // expression as PHP code + public function parseExp(Tokenizer $tokens, $required = false) + { + $_exp = array(); // expression as PHP code $term = false; // last item was variable or value. - // 0 - was operator, but trem required - // false - was operator or no one term - // true - was trem - // 1 - term is strict varaible + // 0 - was operator, but trem required + // false - was operator or no one term + // true - was trem + // 1 - term is strict varaible $cond = false; // last item was operator - while($tokens->valid()) { - if(!$term && $tokens->is(Tokenizer::MACRO_SCALAR, '"', '`', T_ENCAPSED_AND_WHITESPACE)) { // like quoted string + while ($tokens->valid()) { + if (!$term && $tokens->is(Tokenizer::MACRO_SCALAR, '"', '`', T_ENCAPSED_AND_WHITESPACE)) { // like quoted string $_exp[] = $this->parseScalar($tokens, true); $term = true; - } elseif(!$term && $tokens->is(Tokenizer::MACRO_INCDEC)) { // like variable + } elseif (!$term && $tokens->is(Tokenizer::MACRO_INCDEC)) { // like variable $_exp[] = $this->parseVariable($tokens); $term = true; - } elseif(!$term && $tokens->is(T_VARIABLE)) { // like variable too + } elseif (!$term && $tokens->is(T_VARIABLE)) { // like variable too $var = $this->parseVar($tokens); - if($tokens->is(Tokenizer::MACRO_EQUALS)) { + if ($tokens->is(Tokenizer::MACRO_EQUALS)) { $_exp[] = $var; - if($tokens->isLast()) { + if ($tokens->isLast()) { break; } $_exp[] = $tokens->getAndNext(); $term = 0; - } elseif($tokens->is(Tokenizer::MACRO_INCDEC, "|", "!", "?", '(')) { + } elseif ($tokens->is(Tokenizer::MACRO_INCDEC, "|", "!", "?", '(')) { $_exp[] = $this->parseVariable($tokens, 0, $var); $term = true; } else { $_exp[] = $var; $term = 1; } - } elseif(!$term && $tokens->is("(")) { // open bracket + } elseif (!$term && $tokens->is("(")) { // open bracket $tokens->next(); - $_exp[] = "(".$this->parseExp($tokens, true).")"; + $_exp[] = "(" . $this->parseExp($tokens, true) . ")"; $tokens->get(")"); $tokens->next(); $term = 1; - } elseif($tokens->is(T_STRING)) { - if($term) { // parse 'in' or 'is' operators - if(!$_exp) { + } elseif ($tokens->is(T_STRING)) { + if ($term) { // parse 'in' or 'is' operators + if (!$_exp) { break; } $operator = $tokens->current(); - if($operator == "is") { + if ($operator == "is") { $item = array_pop($_exp); $_exp[] = $this->parseIs($tokens, $item, $term === 1); - } elseif($operator == "in" || ($operator == "not" && $tokens->isNextToken("in"))) { + } elseif ($operator == "in" || ($operator == "not" && $tokens->isNextToken("in"))) { $item = array_pop($_exp); $_exp[] = $this->parseIn($tokens, $item, $term === 1); } else { break; } - } else { // function or special value - if($tokens->isSpecialVal()) { + } else { // function or special value + if ($tokens->isSpecialVal()) { $_exp[] = $tokens->getAndNext(); - } elseif($tokens->isNext("(") && !$tokens->getWhitespace()) { + } elseif ($tokens->isNext("(") && !$tokens->getWhitespace()) { $func = $this->_fenom->getModifier($tokens->current(), $this); - if(!$func) { - throw new \Exception("Function ".$tokens->getAndNext()." not found"); + if (!$func) { + throw new \Exception("Function " . $tokens->getAndNext() . " not found"); } $tokens->next(); - $func = $func.$this->parseArgs($tokens); - if($tokens->is('|')) { + $func = $func . $this->parseArgs($tokens); + if ($tokens->is('|')) { $_exp[] = $this->parseModifier($tokens, $func); } else { $_exp[] = $func; @@ -642,52 +670,52 @@ class Template extends Render { } $term = true; } - } elseif(!$term && $tokens->is(T_ISSET, T_EMPTY)) { // empty and isset operators + } elseif (!$term && $tokens->is(T_ISSET, T_EMPTY)) { // empty and isset operators $func = $tokens->getAndNext(); - if($tokens->is("(") && $tokens->isNext(T_VARIABLE)) { + if ($tokens->is("(") && $tokens->isNext(T_VARIABLE)) { $tokens->next(); - $_exp[] = $func."(".$this->parseVar($tokens).")"; + $_exp[] = $func . "(" . $this->parseVar($tokens) . ")"; $tokens->need(')')->next(); } else { - throw new TokenizeException("Unexpected token ".$tokens->getNext().", isset() and empty() accept only variables"); + throw new TokenizeException("Unexpected token " . $tokens->getNext() . ", isset() and empty() accept only variables"); } $term = true; - } elseif(!$term && $tokens->is(Tokenizer::MACRO_UNARY)) { - if(!$tokens->isNext(T_VARIABLE, T_DNUMBER, T_LNUMBER, T_STRING, T_ISSET, T_EMPTY, '(')) { + } elseif (!$term && $tokens->is(Tokenizer::MACRO_UNARY)) { + if (!$tokens->isNext(T_VARIABLE, T_DNUMBER, T_LNUMBER, T_STRING, T_ISSET, T_EMPTY, '(')) { break; } $_exp[] = $tokens->getAndNext(); $term = 0; - } elseif($tokens->is(Tokenizer::MACRO_BINARY)) { // like binary operator, see Tokenizer::MACRO_BINARY - if(!$term) { + } elseif ($tokens->is(Tokenizer::MACRO_BINARY)) { // like binary operator, see Tokenizer::MACRO_BINARY + if (!$term) { throw new UnexpectedTokenException($tokens); } - if($tokens->isLast()) { + if ($tokens->isLast()) { break; } - if($tokens->is(Tokenizer::MACRO_COND)) { - if($cond) { + if ($tokens->is(Tokenizer::MACRO_COND)) { + if ($cond) { break; } $cond = true; } elseif ($tokens->is(Tokenizer::MACRO_BOOLEAN)) { $cond = false; } - $_exp[] = " ".$tokens->getAndNext()." "; + $_exp[] = " " . $tokens->getAndNext() . " "; $term = 0; - } elseif($tokens->is('[')) { - $_exp[] = $this->parseArray($tokens); + } elseif ($tokens->is('[')) { + $_exp[] = $this->parseArray($tokens); } else { break; } } - if($term === 0) { + if ($term === 0) { throw new UnexpectedTokenException($tokens); } - if($required && !$_exp) { + if ($required && !$_exp) { throw new UnexpectedTokenException($tokens); } - return implode('',$_exp); + return implode('', $_exp); } /** @@ -697,50 +725,51 @@ class Template extends Render { * @param int $options * @return string */ - public function parseVar(Tokenizer $tokens, $options = 0) { + public function parseVar(Tokenizer $tokens, $options = 0) + { $var = $tokens->get(T_VARIABLE); - $_var = '$tpl["'.substr($var, 1).'"]'; + $_var = '$tpl["' . substr($var, 1) . '"]'; $tokens->next(); - while($t = $tokens->key()) { - if($t === "." && !($options & self::DENY_ARRAY)) { + while ($t = $tokens->key()) { + if ($t === "." && !($options & self::DENY_ARRAY)) { $key = $tokens->getNext(); - if($tokens->is(T_VARIABLE)) { - $key = "[ ".$this->parseVariable($tokens, self::DENY_ARRAY)." ]"; - } elseif($tokens->is(Tokenizer::MACRO_STRING)) { - $key = '["'.$key.'"]'; + if ($tokens->is(T_VARIABLE)) { + $key = "[ " . $this->parseVariable($tokens, self::DENY_ARRAY) . " ]"; + } elseif ($tokens->is(Tokenizer::MACRO_STRING)) { + $key = '["' . $key . '"]'; $tokens->next(); - } elseif($tokens->is(Tokenizer::MACRO_SCALAR, '"')) { - $key = "[".$this->parseScalar($tokens, false)."]"; + } elseif ($tokens->is(Tokenizer::MACRO_SCALAR, '"')) { + $key = "[" . $this->parseScalar($tokens, false) . "]"; } else { break; } $_var .= $key; - } elseif($t === "[" && !($options & self::DENY_ARRAY)) { + } elseif ($t === "[" && !($options & self::DENY_ARRAY)) { $tokens->next(); - if($tokens->is(Tokenizer::MACRO_STRING)) { - if($tokens->isNext("(")) { - $key = "[".$this->parseExp($tokens)."]"; + if ($tokens->is(Tokenizer::MACRO_STRING)) { + if ($tokens->isNext("(")) { + $key = "[" . $this->parseExp($tokens) . "]"; } else { - $key = '["'.$tokens->current().'"]'; + $key = '["' . $tokens->current() . '"]'; $tokens->next(); } } else { - $key = "[".$this->parseExp($tokens, true)."]"; + $key = "[" . $this->parseExp($tokens, true) . "]"; } $tokens->get("]"); $tokens->next(); $_var .= $key; - } elseif($t === T_DNUMBER) { - $_var .= '['.substr($tokens->getAndNext(), 1).']'; - } elseif($t === T_OBJECT_OPERATOR) { - $_var .= "->".$tokens->getNext(T_STRING); + } elseif ($t === T_DNUMBER) { + $_var .= '[' . substr($tokens->getAndNext(), 1) . ']'; + } elseif ($t === T_OBJECT_OPERATOR) { + $_var .= "->" . $tokens->getNext(T_STRING); $tokens->next(); } else { break; } } - if($this->_options & Fenom::FORCE_VERIFY) { - return 'isset('.$_var.') ? '.$_var.' : null'; + if ($this->_options & Fenom::FORCE_VERIFY) { + return 'isset(' . $_var . ') ? ' . $_var . ' : null'; } else { return $_var; } @@ -761,41 +790,42 @@ class Template extends Render { * @throws InvalidUsageException * @return string */ - public function parseVariable(Tokenizer $tokens, $options = 0, $var = null) { + public function parseVariable(Tokenizer $tokens, $options = 0, $var = null) + { $stained = false; - if(!$var) { - if($tokens->is(Tokenizer::MACRO_INCDEC)) { + if (!$var) { + if ($tokens->is(Tokenizer::MACRO_INCDEC)) { $stained = true; - $var = $tokens->getAndNext().$this->parseVar($tokens, $options); + $var = $tokens->getAndNext() . $this->parseVar($tokens, $options); } else { $var = $this->parseVar($tokens, $options); } - if($tokens->is(T_OBJECT_OPERATOR)) { // parse - $var .= '->'.$tokens->getNext(T_STRING); + if ($tokens->is(T_OBJECT_OPERATOR)) { // parse + $var .= '->' . $tokens->getNext(T_STRING); $tokens->next(); } } - if($tokens->is("(") && $tokens->hasBackList(T_STRING, T_OBJECT_OPERATOR)) { - if($stained) { + if ($tokens->is("(") && $tokens->hasBackList(T_STRING, T_OBJECT_OPERATOR)) { + if ($stained) { throw new InvalidUsageException("Can not increment or decrement of the method result"); } - if($this->_options & Fenom::DENY_METHODS) { + if ($this->_options & Fenom::DENY_METHODS) { throw new \LogicException("Forbidden to call methods"); } $var .= $this->parseArgs($tokens); $stained = true; } - if($tokens->is('?', '!')) { + if ($tokens->is('?', '!')) { return $this->parseTernary($tokens, $var, $tokens->current()); } - if($tokens->is(Tokenizer::MACRO_INCDEC)) { - if($stained) { + if ($tokens->is(Tokenizer::MACRO_INCDEC)) { + if ($stained) { throw new InvalidUsageException("Can not use two increments and/or decrements for one variable"); } $var .= $tokens->getAndNext(); } - if($tokens->is('|') && !($options & self::DENY_MODS)) { + if ($tokens->is('|') && !($options & self::DENY_MODS)) { return $this->parseModifier($tokens, $var); } return $var; @@ -810,32 +840,33 @@ class Template extends Render { * @return string * @throws UnexpectedTokenException */ - public function parseTernary(Tokenizer $tokens, $var, $type) { + public function parseTernary(Tokenizer $tokens, $var, $type) + { $empty = ($type === "?"); $tokens->next(); - if($tokens->is(":")) { + if ($tokens->is(":")) { $tokens->next(); - if($empty) { - return '(empty('.$var.') ? ('.$this->parseExp($tokens, true).') : '.$var.')'; + if ($empty) { + return '(empty(' . $var . ') ? (' . $this->parseExp($tokens, true) . ') : ' . $var . ')'; } else { - return '(isset('.$var.') ? '.$var.' : ('.$this->parseExp($tokens, true).'))'; + return '(isset(' . $var . ') ? ' . $var . ' : (' . $this->parseExp($tokens, true) . '))'; } - } elseif($tokens->is(Tokenizer::MACRO_BINARY, Tokenizer::MACRO_BOOLEAN, Tokenizer::MACRO_MATH) || !$tokens->valid()) { - if($empty) { - return '!empty('.$var.')'; + } elseif ($tokens->is(Tokenizer::MACRO_BINARY, Tokenizer::MACRO_BOOLEAN, Tokenizer::MACRO_MATH) || !$tokens->valid()) { + if ($empty) { + return '!empty(' . $var . ')'; } else { - return 'isset('.$var.')'; + return 'isset(' . $var . ')'; } } else { $expr1 = $this->parseExp($tokens, true); - if(!$tokens->is(":")) { + if (!$tokens->is(":")) { throw new UnexpectedTokenException($tokens, null, "ternary operator"); } $expr2 = $this->parseExp($tokens, true); - if($empty) { - return '(empty('.$var.') ? '.$expr2.' : '.$expr1.')'; + if ($empty) { + return '(empty(' . $var . ') ? ' . $expr2 . ' : ' . $expr1 . ')'; } else { - return '(isset('.$var.') ? '.$expr1.' : '.$expr2.')'; + return '(isset(' . $var . ') ? ' . $expr1 . ' : ' . $expr2 . ')'; } } } @@ -849,9 +880,10 @@ class Template extends Render { * @throws InvalidUsageException * @return string */ - public function parseIs(Tokenizer $tokens, $value, $variable = false) { + public function parseIs(Tokenizer $tokens, $value, $variable = false) + { $tokens->next(); - if($tokens->current() == 'not'){ + if ($tokens->current() == 'not') { $invert = '!'; $equal = '!='; $tokens->next(); @@ -859,28 +891,28 @@ class Template extends Render { $invert = ''; $equal = '=='; } - if($tokens->is(Tokenizer::MACRO_STRING)) { + if ($tokens->is(Tokenizer::MACRO_STRING)) { $action = $tokens->current(); - if(!$variable && ($action == "set" || $action == "empty")) { + if (!$variable && ($action == "set" || $action == "empty")) { $action = "_$action"; $tokens->next(); - return $invert.sprintf(self::$_checkers[$action], $value); - } elseif(isset(self::$_checkers[$action])) { + return $invert . sprintf(self::$_checkers[$action], $value); + } elseif (isset(self::$_checkers[$action])) { $tokens->next(); - return $invert.sprintf(self::$_checkers[$action], $value); - } elseif($tokens->isSpecialVal()) { + return $invert . sprintf(self::$_checkers[$action], $value); + } elseif ($tokens->isSpecialVal()) { $tokens->next(); - return '('.$value.' '.$equal.'= '.$action.')'; + return '(' . $value . ' ' . $equal . '= ' . $action . ')'; } - return $invert.'('.$value.' instanceof \\'.$this->parseName($tokens).')'; - } elseif($tokens->is(T_VARIABLE)) { - return '('.$value.' '.$equal.'= '.$this->parseVariable($tokens).')'; - } elseif($tokens->is(Tokenizer::MACRO_SCALAR)) { - return '('.$value.' '.$equal.'= '.$this->parseScalar($tokens).')'; - } elseif($tokens->is('[')) { - return '('.$value.' '.$equal.'= '.$this->parseArray($tokens).')'; - } elseif($tokens->is(T_NS_SEPARATOR)) { // - return $invert.'('.$value.' instanceof \\'.$this->parseName($tokens).')'; + return $invert . '(' . $value . ' instanceof \\' . $this->parseName($tokens) . ')'; + } elseif ($tokens->is(T_VARIABLE)) { + return '(' . $value . ' ' . $equal . '= ' . $this->parseVariable($tokens) . ')'; + } elseif ($tokens->is(Tokenizer::MACRO_SCALAR)) { + return '(' . $value . ' ' . $equal . '= ' . $this->parseScalar($tokens) . ')'; + } elseif ($tokens->is('[')) { + return '(' . $value . ' ' . $equal . '= ' . $this->parseArray($tokens) . ')'; + } elseif ($tokens->is(T_NS_SEPARATOR)) { // + return $invert . '(' . $value . ' instanceof \\' . $this->parseName($tokens) . ')'; } else { throw new InvalidUsageException("Unknown argument"); } @@ -894,49 +926,50 @@ class Template extends Render { * @throws UnexpectedTokenException * @return string */ - public function parseIn(Tokenizer $tokens, $value) { + public function parseIn(Tokenizer $tokens, $value) + { $checkers = array( "string" => 'is_int(strpos(%2$s, %1$s))', - "list" => "in_array(%s, %s)", - "keys" => "array_key_exists(%s, %s)", - "auto" => '\Fenom\Modifier::in(%s, %s)' + "list" => "in_array(%s, %s)", + "keys" => "array_key_exists(%s, %s)", + "auto" => '\Fenom\Modifier::in(%s, %s)' ); $checker = null; $invert = ''; - if($tokens->current() == 'not'){ + if ($tokens->current() == 'not') { $invert = '!'; $tokens->next(); } - if($tokens->current() !== "in") { + if ($tokens->current() !== "in") { throw new UnexpectedTokenException($tokens); } $tokens->next(); - if($tokens->is(Tokenizer::MACRO_STRING)) { + if ($tokens->is(Tokenizer::MACRO_STRING)) { $checker = $tokens->current(); - if(!isset($checkers[$checker])) { + if (!isset($checkers[$checker])) { throw new UnexpectedTokenException($tokens); } $tokens->next(); } - if($tokens->is('[')) { - if($checker == "string") { + if ($tokens->is('[')) { + if ($checker == "string") { throw new InvalidUsageException("Can not use string operation for array"); - } elseif(!$checker) { + } elseif (!$checker) { $checker = "list"; } - return $invert.sprintf($checkers[$checker], $value, $this->parseArray($tokens)); - } elseif($tokens->is('"', T_ENCAPSED_AND_WHITESPACE, T_CONSTANT_ENCAPSED_STRING)) { - if(!$checker) { + return $invert . sprintf($checkers[$checker], $value, $this->parseArray($tokens)); + } elseif ($tokens->is('"', T_ENCAPSED_AND_WHITESPACE, T_CONSTANT_ENCAPSED_STRING)) { + if (!$checker) { $checker = "string"; - } elseif($checker != "string") { + } elseif ($checker != "string") { throw new InvalidUsageException("Can not use array operation for string"); } - return $invert.sprintf($checkers[$checker], "strval($value)", $this->parseScalar($tokens)); - } elseif($tokens->is(T_VARIABLE, Tokenizer::MACRO_INCDEC)) { - if(!$checker) { + return $invert . sprintf($checkers[$checker], "strval($value)", $this->parseScalar($tokens)); + } elseif ($tokens->is(T_VARIABLE, Tokenizer::MACRO_INCDEC)) { + if (!$checker) { $checker = "auto"; } - return $invert.sprintf($checkers[$checker], $value, $this->parseVariable($tokens)); + return $invert . sprintf($checkers[$checker], $value, $this->parseVariable($tokens)); } else { throw new UnexpectedTokenException($tokens); } @@ -948,13 +981,14 @@ class Template extends Render { * @param Tokenizer $tokens * @return string */ - public function parseName(Tokenizer $tokens) { + public function parseName(Tokenizer $tokens) + { $tokens->skipIf(T_NS_SEPARATOR); $name = ""; - if($tokens->is(T_STRING)) { + if ($tokens->is(T_STRING)) { $name .= $tokens->getAndNext(); - while($tokens->is(T_NS_SEPARATOR)) { - $name .= '\\'.$tokens->next()->get(T_STRING); + while ($tokens->is(T_NS_SEPARATOR)) { + $name .= '\\' . $tokens->next()->get(T_STRING); $tokens->next(); } } @@ -969,10 +1003,11 @@ class Template extends Render { * @return string * @throws TokenizeException */ - public function parseScalar(Tokenizer $tokens, $allow_mods = true) { + public function parseScalar(Tokenizer $tokens, $allow_mods = true) + { $_scalar = ""; - if($token = $tokens->key()) { - switch($token) { + if ($token = $tokens->key()) { + switch ($token) { case T_CONSTANT_ENCAPSED_STRING: case T_LNUMBER: case T_DNUMBER: @@ -983,9 +1018,9 @@ class Template extends Render { $_scalar .= $this->parseSubstr($tokens); break; default: - throw new TokenizeException("Unexpected scalar token '".$tokens->current()."'"); + throw new TokenizeException("Unexpected scalar token '" . $tokens->current() . "'"); } - if($allow_mods && $tokens->is("|")) { + if ($allow_mods && $tokens->is("|")) { return $this->parseModifier($tokens, $_scalar); } } @@ -999,57 +1034,58 @@ class Template extends Render { * @throws UnexpectedTokenException * @return string */ - public function parseSubstr(Tokenizer $tokens) { - if($tokens->is('"',"`")) { + public function parseSubstr(Tokenizer $tokens) + { + if ($tokens->is('"', "`")) { $stop = $tokens->current(); $_str = '"'; $tokens->next(); - while($t = $tokens->key()) { - if($t === T_ENCAPSED_AND_WHITESPACE) { + while ($t = $tokens->key()) { + if ($t === T_ENCAPSED_AND_WHITESPACE) { $_str .= $tokens->current(); $tokens->next(); - } elseif($t === T_VARIABLE) { - if(strlen($_str) > 1) { + } elseif ($t === T_VARIABLE) { + if (strlen($_str) > 1) { $_str .= '".'; } else { $_str = ""; } - $_str .= '$tpl["'.substr($tokens->current(), 1).'"]'; + $_str .= '$tpl["' . substr($tokens->current(), 1) . '"]'; $tokens->next(); - if($tokens->is($stop)) { + if ($tokens->is($stop)) { $tokens->skip(); return $_str; } else { $_str .= '."'; } - } elseif($t === T_CURLY_OPEN) { - if(strlen($_str) > 1) { + } elseif ($t === T_CURLY_OPEN) { + if (strlen($_str) > 1) { $_str .= '".'; } else { $_str = ""; } $tokens->getNext(T_VARIABLE); - $_str .= '('.$this->parseExp($tokens).')'; - if($tokens->is($stop)) { + $_str .= '(' . $this->parseExp($tokens) . ')'; + if ($tokens->is($stop)) { $tokens->next(); return $_str; } else { $_str .= '."'; } - } elseif($t === "}") { + } elseif ($t === "}") { $tokens->next(); - } elseif($t === $stop) { + } elseif ($t === $stop) { $tokens->next(); - return $_str.'"'; + return $_str . '"'; } else { break; } } throw new UnexpectedTokenException($tokens); - } elseif($tokens->is(T_CONSTANT_ENCAPSED_STRING)) { + } elseif ($tokens->is(T_CONSTANT_ENCAPSED_STRING)) { return $tokens->getAndNext(); - } elseif($tokens->is(T_ENCAPSED_AND_WHITESPACE)) { + } elseif ($tokens->is(T_ENCAPSED_AND_WHITESPACE)) { throw new UnexpectedTokenException($tokens); } else { return ""; @@ -1066,45 +1102,46 @@ class Template extends Render { * @throws \Exception * @return string */ - public function parseModifier(Tokenizer $tokens, $value) { - while($tokens->is("|")) { + public function parseModifier(Tokenizer $tokens, $value) + { + while ($tokens->is("|")) { $mods = $this->_fenom->getModifier($tokens->getNext(Tokenizer::MACRO_STRING), $this); - if(!$mods) { - throw new \Exception("Modifier ".$tokens->current()." not found"); + if (!$mods) { + throw new \Exception("Modifier " . $tokens->current() . " not found"); } $tokens->next(); $args = array(); - while($tokens->is(":")) { + while ($tokens->is(":")) { $token = $tokens->getNext(Tokenizer::MACRO_SCALAR, T_VARIABLE, '"', Tokenizer::MACRO_STRING, "(", "["); - if($tokens->is(Tokenizer::MACRO_SCALAR) || $tokens->isSpecialVal()) { + if ($tokens->is(Tokenizer::MACRO_SCALAR) || $tokens->isSpecialVal()) { $args[] = $token; $tokens->next(); - } elseif($tokens->is(T_VARIABLE)) { + } elseif ($tokens->is(T_VARIABLE)) { $args[] = $this->parseVariable($tokens, self::DENY_MODS); - } elseif($tokens->is('"', '`', T_ENCAPSED_AND_WHITESPACE)) { + } elseif ($tokens->is('"', '`', T_ENCAPSED_AND_WHITESPACE)) { $args[] = $this->parseSubstr($tokens); - } elseif($tokens->is('(')) { + } elseif ($tokens->is('(')) { $args[] = $this->parseExp($tokens, true); - } elseif($tokens->is('[')) { + } elseif ($tokens->is('[')) { $args[] = $this->parseArray($tokens); - } elseif($tokens->is(T_STRING) && $tokens->isNext('(')) { - $args[] = $tokens->getAndNext().$this->parseArgs($tokens); + } elseif ($tokens->is(T_STRING) && $tokens->isNext('(')) { + $args[] = $tokens->getAndNext() . $this->parseArgs($tokens); } else { break; } } - if(!is_string($mods)) { // dynamic modifier - $mods = 'call_user_func($tpl->getStorage()->getModifier("'.$mods.'"), '; + if (!is_string($mods)) { // dynamic modifier + $mods = 'call_user_func($tpl->getStorage()->getModifier("' . $mods . '"), '; } else { $mods .= "("; } - if($args) { - $value = $mods.$value.', '.implode(", ", $args).')'; + if ($args) { + $value = $mods . $value . ', ' . implode(", ", $args) . ')'; } else { - $value = $mods.$value.')'; + $value = $mods . $value . ')'; } } return $value; @@ -1118,35 +1155,36 @@ class Template extends Render { * @throws UnexpectedTokenException * @return string */ - public function parseArray(Tokenizer $tokens) { - if($tokens->is("[")) { + public function parseArray(Tokenizer $tokens) + { + if ($tokens->is("[")) { $_arr = "array("; $key = $val = false; $tokens->next(); - while($tokens->valid()) { - if($tokens->is(',') && $val) { + while ($tokens->valid()) { + if ($tokens->is(',') && $val) { $key = true; $val = false; - $_arr .= $tokens->getAndNext().' '; - } elseif($tokens->is(Tokenizer::MACRO_SCALAR, T_VARIABLE, T_STRING, T_EMPTY, T_ISSET, "(", "#") && !$val) { + $_arr .= $tokens->getAndNext() . ' '; + } elseif ($tokens->is(Tokenizer::MACRO_SCALAR, T_VARIABLE, T_STRING, T_EMPTY, T_ISSET, "(", "#") && !$val) { $_arr .= $this->parseExp($tokens, true); $key = false; $val = true; - } elseif($tokens->is('"') && !$val) { + } elseif ($tokens->is('"') && !$val) { $_arr .= $this->parseSubstr($tokens); $key = false; $val = true; - } elseif($tokens->is(T_DOUBLE_ARROW) && $val) { - $_arr .= ' '.$tokens->getAndNext().' '; + } elseif ($tokens->is(T_DOUBLE_ARROW) && $val) { + $_arr .= ' ' . $tokens->getAndNext() . ' '; $key = true; $val = false; - } elseif(!$val && $tokens->is('[')) { + } elseif (!$val && $tokens->is('[')) { $_arr .= $this->parseArray($tokens); $key = false; $val = true; - } elseif($tokens->is(']') && !$key) { + } elseif ($tokens->is(']') && !$key) { $tokens->next(); - return $_arr.')'; + return $_arr . ')'; } else { break; } @@ -1163,21 +1201,22 @@ class Template extends Render { * @return string * @throws InvalidUsageException */ - public function parseConst(Tokenizer $tokens) { + public function parseConst(Tokenizer $tokens) + { $tokens->get('#'); $name = $tokens->getNext(T_STRING); $tokens->next(); - if($tokens->is(T_NAMESPACE)) { + if ($tokens->is(T_NAMESPACE)) { $name .= '\\'; $name .= $tokens->getNext(T_STRING); $tokens->next(); } - if($tokens->is(T_DOUBLE_COLON)) { + if ($tokens->is(T_DOUBLE_COLON)) { $name .= '::'; $name .= $tokens->getNext(T_STRING); $tokens->next(); } - if(defined($name)) { + if (defined($name)) { return $name; } else { throw new InvalidUsageException("Use undefined constant $name"); @@ -1190,42 +1229,43 @@ class Template extends Render { * @return string * @throws InvalidUsageException */ - public function parseMacroCall(Tokenizer $tokens, $name) { + public function parseMacroCall(Tokenizer $tokens, $name) + { $recursive = false; $macro = false; - if(isset($this->macros[ $name ])) { - $macro = $this->macros[ $name ]; + if (isset($this->macros[$name])) { + $macro = $this->macros[$name]; } else { - foreach($this->_stack as $scope) { - if($scope->name == 'macro' && $scope['name'] == $name) { // invoke recursive + foreach ($this->_stack as $scope) { + if ($scope->name == 'macro' && $scope['name'] == $name) { // invoke recursive $recursive = $scope; $macro = $scope['macro']; break; } } - if(!$macro) { + if (!$macro) { throw new InvalidUsageException("Undefined macro '$name'"); } } $tokens->next(); $p = $this->parseParams($tokens); $args = array(); - foreach($macro['args'] as $arg) { - if(isset($p[ $arg ])) { - $args[ $arg ] = $p[ $arg ]; - } elseif(isset($macro['defaults'][ $arg ])) { - $args[ $arg ] = $macro['defaults'][ $arg ]; + foreach ($macro['args'] as $arg) { + if (isset($p[$arg])) { + $args[$arg] = $p[$arg]; + } elseif (isset($macro['defaults'][$arg])) { + $args[$arg] = $macro['defaults'][$arg]; } else { throw new InvalidUsageException("Macro '$name' require '$arg' argument"); } } - $args = $args ? '$tpl = '.Compiler::toArray($args).';' : ''; - if($recursive) { + $args = $args ? '$tpl = ' . Compiler::toArray($args) . ';' : ''; + if ($recursive) { $n = $this->i++; $recursive['recursive'][] = $n; - return '$stack_'.$macro['id'].'[] = array("tpl" => $tpl, "mark" => '.$n.'); '.$args.' goto macro_'.$macro['id'].'; macro_'.$n.':'; + return '$stack_' . $macro['id'] . '[] = array("tpl" => $tpl, "mark" => ' . $n . '); ' . $args . ' goto macro_' . $macro['id'] . '; macro_' . $n . ':'; } else { - return '$_tpl = $tpl; '.$args.' ?>'.$macro["body"].'' . $macro["body"] . 'next(); $arg = $colon = false; - while($tokens->valid()) { - if(!$arg && $tokens->is(T_VARIABLE, T_STRING, "(", Tokenizer::MACRO_SCALAR, '"', Tokenizer::MACRO_UNARY, Tokenizer::MACRO_INCDEC)) { + while ($tokens->valid()) { + if (!$arg && $tokens->is(T_VARIABLE, T_STRING, "(", Tokenizer::MACRO_SCALAR, '"', Tokenizer::MACRO_UNARY, Tokenizer::MACRO_INCDEC)) { $_args .= $this->parseExp($tokens, true); $arg = true; $colon = false; - } elseif(!$arg && $tokens->is('[')) { + } elseif (!$arg && $tokens->is('[')) { $_args .= $this->parseArray($tokens); $arg = true; $colon = false; - } elseif($arg && $tokens->is(',')) { - $_args .= $tokens->getAndNext().' '; + } elseif ($arg && $tokens->is(',')) { + $_args .= $tokens->getAndNext() . ' '; $arg = false; $colon = true; - } elseif(!$colon && $tokens->is(')')) { + } elseif (!$colon && $tokens->is(')')) { $tokens->next(); - return $_args.')'; + return $_args . ')'; } else { break; } } - throw new TokenizeException("Unexpected token '".$tokens->current()."' in argument list"); + throw new TokenizeException("Unexpected token '" . $tokens->current() . "' in argument list"); } /** @@ -1273,18 +1314,19 @@ class Template extends Render { * @param string $static * @return mixed|string */ - public function parsePlainArg(Tokenizer $tokens, &$static) { - if($tokens->is(T_CONSTANT_ENCAPSED_STRING)) { - if($tokens->isNext('|')) { + public function parsePlainArg(Tokenizer $tokens, &$static) + { + if ($tokens->is(T_CONSTANT_ENCAPSED_STRING)) { + if ($tokens->isNext('|')) { return $this->parseExp($tokens, true); } else { $str = $tokens->getAndNext(); $static = stripslashes(substr($str, 1, -1)); return $str; } - } elseif($tokens->is(Tokenizer::MACRO_STRING)) { + } elseif ($tokens->is(Tokenizer::MACRO_STRING)) { $static = $tokens->getAndNext(); - return '"'.addslashes($static).'"'; + return '"' . addslashes($static) . '"'; } else { return $this->parseExp($tokens, true); } @@ -1297,31 +1339,32 @@ class Template extends Render { * * @static * @param Tokenizer $tokens - * @param array $defaults + * @param array $defaults * @throws \Exception * @return array */ - public function parseParams(Tokenizer $tokens, array $defaults = null) { + public function parseParams(Tokenizer $tokens, array $defaults = null) + { $params = array(); - while($tokens->valid()) { - if($tokens->is(Tokenizer::MACRO_STRING)) { + while ($tokens->valid()) { + if ($tokens->is(Tokenizer::MACRO_STRING)) { $key = $tokens->getAndNext(); - if($defaults && !isset($defaults[$key])) { + if ($defaults && !isset($defaults[$key])) { throw new \Exception("Unknown parameter '$key'"); } - if($tokens->is("=")) { + if ($tokens->is("=")) { $tokens->next(); - $params[ $key ] = $this->parseExp($tokens); + $params[$key] = $this->parseExp($tokens); } else { - $params[ $key ] = 'true'; + $params[$key] = 'true'; } - } elseif($tokens->is(Tokenizer::MACRO_SCALAR, '"', '`', T_VARIABLE, "[", '(')) { + } elseif ($tokens->is(Tokenizer::MACRO_SCALAR, '"', '`', T_VARIABLE, "[", '(')) { $params[] = $this->parseExp($tokens); } else { break; } } - if($defaults) { + if ($defaults) { $params += $defaults; } @@ -1329,7 +1372,18 @@ class Template extends Render { } } -class CompileException extends \ErrorException {} -class SecurityException extends CompileException {} -class InvalidUsageException extends \LogicException {} -class TokenizeException extends \RuntimeException {} \ No newline at end of file +class CompileException extends \ErrorException +{ +} + +class SecurityException extends CompileException +{ +} + +class InvalidUsageException extends \LogicException +{ +} + +class TokenizeException extends \RuntimeException +{ +} \ No newline at end of file diff --git a/src/Fenom/Tokenizer.php b/src/Fenom/Tokenizer.php index 9c2aedf..a93b8aa 100644 --- a/src/Fenom/Tokenizer.php +++ b/src/Fenom/Tokenizer.php @@ -12,13 +12,13 @@ namespace Fenom; /** * for */ -class Tokenizer { +class Tokenizer +{ const TOKEN = 0; const TEXT = 1; const WHITESPACE = 2; @@ -90,19 +91,19 @@ class Tokenizer { */ private static $_macros = array( self::MACRO_STRING => array( - \T_ABSTRACT => 1, \T_ARRAY => 1, \T_AS => 1, \T_BREAK => 1, \T_BREAK => 1, \T_CASE => 1, - \T_CATCH => 1, \T_CLASS => 1, \T_CLASS_C => 1, \T_CLONE => 1, \T_CONST => 1, \T_CONTINUE => 1, - \T_DECLARE => 1, \T_DEFAULT => 1, \T_DIR => 1, \T_DO => 1, \T_ECHO => 1, \T_ELSE => 1, - \T_ELSEIF => 1, \T_EMPTY => 1, \T_ENDDECLARE => 1, \T_ENDFOR => 1, \T_ENDFOREACH => 1, \T_ENDIF => 1, - \T_ENDSWITCH => 1, \T_ENDWHILE => 1, \T_EVAL => 1, \T_EXIT => 1, \T_EXTENDS => 1, \T_FILE => 1, - \T_FINAL => 1, \T_FOR => 1, \T_FOREACH => 1, \T_FUNCTION => 1, \T_FUNC_C => 1, \T_GLOBAL => 1, - \T_GOTO => 1, \T_HALT_COMPILER => 1, \T_IF => 1, \T_IMPLEMENTS => 1, \T_INCLUDE => 1, \T_INCLUDE_ONCE => 1, - \T_INSTANCEOF => 1, \T_INSTEADOF => 1, \T_INTERFACE => 1, \T_ISSET => 1, \T_LINE => 1, \T_LIST => 1, - \T_LOGICAL_AND => 1, \T_LOGICAL_OR => 1, \T_LOGICAL_XOR => 1, \T_METHOD_C => 1, \T_NAMESPACE => 1, \T_NS_C => 1, - \T_NEW => 1, \T_PRINT => 1, \T_PRIVATE => 1, \T_PUBLIC => 1, \T_PROTECTED => 1, \T_REQUIRE => 1, - \T_REQUIRE_ONCE => 1,\T_RETURN => 1, \T_RETURN => 1, \T_STRING => 1, \T_SWITCH => 1, \T_THROW => 1, - \T_TRAIT => 1, \T_TRAIT_C => 1, \T_TRY => 1, \T_UNSET => 1, \T_VAR => 1, - \T_WHILE => 1, \T_YIELD => 1, \T_USE => 1 + \T_ABSTRACT => 1, \T_ARRAY => 1, \T_AS => 1, \T_BREAK => 1, \T_BREAK => 1, \T_CASE => 1, + \T_CATCH => 1, \T_CLASS => 1, \T_CLASS_C => 1, \T_CLONE => 1, \T_CONST => 1, \T_CONTINUE => 1, + \T_DECLARE => 1, \T_DEFAULT => 1, \T_DIR => 1, \T_DO => 1, \T_ECHO => 1, \T_ELSE => 1, + \T_ELSEIF => 1, \T_EMPTY => 1, \T_ENDDECLARE => 1, \T_ENDFOR => 1, \T_ENDFOREACH => 1, \T_ENDIF => 1, + \T_ENDSWITCH => 1, \T_ENDWHILE => 1, \T_EVAL => 1, \T_EXIT => 1, \T_EXTENDS => 1, \T_FILE => 1, + \T_FINAL => 1, \T_FOR => 1, \T_FOREACH => 1, \T_FUNCTION => 1, \T_FUNC_C => 1, \T_GLOBAL => 1, + \T_GOTO => 1, \T_HALT_COMPILER => 1, \T_IF => 1, \T_IMPLEMENTS => 1, \T_INCLUDE => 1, \T_INCLUDE_ONCE => 1, + \T_INSTANCEOF => 1, \T_INSTEADOF => 1, \T_INTERFACE => 1, \T_ISSET => 1, \T_LINE => 1, \T_LIST => 1, + \T_LOGICAL_AND => 1, \T_LOGICAL_OR => 1, \T_LOGICAL_XOR => 1, \T_METHOD_C => 1, \T_NAMESPACE => 1, \T_NS_C => 1, + \T_NEW => 1, \T_PRINT => 1, \T_PRIVATE => 1, \T_PUBLIC => 1, \T_PROTECTED => 1, \T_REQUIRE => 1, + \T_REQUIRE_ONCE => 1, \T_RETURN => 1, \T_RETURN => 1, \T_STRING => 1, \T_SWITCH => 1, \T_THROW => 1, + \T_TRAIT => 1, \T_TRAIT_C => 1, \T_TRY => 1, \T_UNSET => 1, \T_VAR => 1, + \T_WHILE => 1, \T_YIELD => 1, \T_USE => 1 ), self::MACRO_INCDEC => array( \T_INC => 1, \T_DEC => 1 @@ -111,41 +112,41 @@ class Tokenizer { "!" => 1, "~" => 1, "-" => 1 ), self::MACRO_BINARY => array( - \T_BOOLEAN_AND => 1, \T_BOOLEAN_OR => 1, \T_IS_GREATER_OR_EQUAL => 1, \T_IS_EQUAL => 1, \T_IS_IDENTICAL => 1, - \T_IS_NOT_EQUAL => 1,\T_IS_NOT_IDENTICAL => 1, \T_IS_SMALLER_OR_EQUAL => 1, \T_LOGICAL_AND => 1, - \T_LOGICAL_OR => 1, \T_LOGICAL_XOR => 1, \T_SL => 1, \T_SR => 1, + \T_BOOLEAN_AND => 1, \T_BOOLEAN_OR => 1, \T_IS_GREATER_OR_EQUAL => 1, \T_IS_EQUAL => 1, \T_IS_IDENTICAL => 1, + \T_IS_NOT_EQUAL => 1, \T_IS_NOT_IDENTICAL => 1, \T_IS_SMALLER_OR_EQUAL => 1, \T_LOGICAL_AND => 1, + \T_LOGICAL_OR => 1, \T_LOGICAL_XOR => 1, \T_SL => 1, \T_SR => 1, "+" => 1, "-" => 1, "*" => 1, "/" => 1, ">" => 1, "<" => 1, "^" => 1, "%" => 1, "&" => 1 ), self::MACRO_BOOLEAN => array( - \T_LOGICAL_OR => 1, \T_LOGICAL_XOR => 1, \T_BOOLEAN_AND => 1, \T_BOOLEAN_OR => 1, \T_LOGICAL_AND => 1 + \T_LOGICAL_OR => 1, \T_LOGICAL_XOR => 1, \T_BOOLEAN_AND => 1, \T_BOOLEAN_OR => 1, \T_LOGICAL_AND => 1 ), self::MACRO_MATH => array( "+" => 1, "-" => 1, "*" => 1, "/" => 1, "^" => 1, "%" => 1, "&" => 1, "|" => 1 ), self::MACRO_COND => array( - \T_IS_EQUAL => 1, \T_IS_IDENTICAL => 1, ">" => 1, "<" => 1, \T_SL => 1, \T_SR => 1, - \T_IS_NOT_EQUAL => 1,\T_IS_NOT_IDENTICAL => 1, \T_IS_SMALLER_OR_EQUAL => 1, + \T_IS_EQUAL => 1, \T_IS_IDENTICAL => 1, ">" => 1, "<" => 1, \T_SL => 1, \T_SR => 1, + \T_IS_NOT_EQUAL => 1, \T_IS_NOT_IDENTICAL => 1, \T_IS_SMALLER_OR_EQUAL => 1, ), self::MACRO_EQUALS => array( - \T_AND_EQUAL => 1, \T_CONCAT_EQUAL => 1,\T_DIV_EQUAL => 1, \T_MINUS_EQUAL => 1, \T_MOD_EQUAL => 1, - \T_MUL_EQUAL => 1, \T_OR_EQUAL => 1, \T_PLUS_EQUAL => 1, \T_SL_EQUAL => 1, \T_SR_EQUAL => 1, - \T_XOR_EQUAL => 1, '=' => 1 + \T_AND_EQUAL => 1, \T_CONCAT_EQUAL => 1, \T_DIV_EQUAL => 1, \T_MINUS_EQUAL => 1, \T_MOD_EQUAL => 1, + \T_MUL_EQUAL => 1, \T_OR_EQUAL => 1, \T_PLUS_EQUAL => 1, \T_SL_EQUAL => 1, \T_SR_EQUAL => 1, + \T_XOR_EQUAL => 1, '=' => 1 ), self::MACRO_SCALAR => array( - \T_LNUMBER => 1, \T_DNUMBER => 1, \T_CONSTANT_ENCAPSED_STRING => 1 + \T_LNUMBER => 1, \T_DNUMBER => 1, \T_CONSTANT_ENCAPSED_STRING => 1 ) ); public static $description = array( - self::MACRO_STRING => 'string', - self::MACRO_INCDEC => 'increment/decrement operator', - self::MACRO_UNARY => 'unary operator', - self::MACRO_BINARY => 'binary operator', + self::MACRO_STRING => 'string', + self::MACRO_INCDEC => 'increment/decrement operator', + self::MACRO_UNARY => 'unary operator', + self::MACRO_BINARY => 'binary operator', self::MACRO_BOOLEAN => 'boolean operator', - self::MACRO_MATH => 'math operator', - self::MACRO_COND => 'conditional operator', - self::MACRO_EQUALS => 'equal operator', - self::MACRO_SCALAR => 'scalar value' + self::MACRO_MATH => 'math operator', + self::MACRO_COND => 'conditional operator', + self::MACRO_EQUALS => 'equal operator', + self::MACRO_SCALAR => 'scalar value' ); /** @@ -159,15 +160,16 @@ class Tokenizer { /** * @param $query */ - public function __construct($query) { + public function __construct($query) + { $tokens = array(-1 => array(\T_WHITESPACE, '', '', 1)); - $_tokens = token_get_all("quotes++; } $tokens[] = array( @@ -178,13 +180,13 @@ class Tokenizer { ); $i++; } elseif ($token[0] === \T_WHITESPACE) { - $tokens[$i-1][2] = $token[1]; + $tokens[$i - 1][2] = $token[1]; } else { $tokens[] = array( $token[0], $token[1], "", - $line = $token[2], + $line = $token[2], token_name($token[0]) // debug ); $i++; @@ -202,7 +204,8 @@ class Tokenizer { * * @return int */ - public function isIncomplete() { + public function isIncomplete() + { return ($this->quotes % 2) || ($this->tokens[$this->_max][0] === T_ENCAPSED_AND_WHITESPACE); } @@ -212,7 +215,8 @@ class Tokenizer { * @link http://php.net/manual/en/iterator.current.php * @return mixed Can return any type. */ - public function current() { + public function current() + { return $this->curr[1]; } @@ -222,8 +226,9 @@ class Tokenizer { * @link http://php.net/manual/en/iterator.next.php * @return Tokenizer */ - public function next() { - if($this->p > $this->_max) { + public function next() + { + if ($this->p > $this->_max) { return $this; } $this->p++; @@ -238,15 +243,16 @@ class Tokenizer { * @param string|int $token * @return bool */ - private function _valid($expects, $token) { - foreach($expects as $expect) { - if(is_string($expect) || $expect < 1000) { - if($expect === $token) { + private function _valid($expects, $token) + { + foreach ($expects as $expect) { + if (is_string($expect) || $expect < 1000) { + if ($expect === $token) { return true; } } else { - if(isset(self::$_macros[ $expect ][ $token ])) { + if (isset(self::$_macros[$expect][$token])) { return true; } } @@ -260,13 +266,14 @@ class Tokenizer { * @throws UnexpectedTokenException * @return mixed */ - public function _next($tokens) { + public function _next($tokens) + { $this->next(); - if(!$this->curr) { + if (!$this->curr) { throw new UnexpectedTokenException($this, $tokens); } - if($tokens) { - if($this->_valid($tokens, $this->key())) { + if ($tokens) { + if ($this->_valid($tokens, $this->key())) { return; } } else { @@ -279,7 +286,8 @@ class Tokenizer { * Fetch next specified token or throw an exception * @return mixed */ - public function getNext(/*int|string $token1, int|string $token2, ... */) { + public function getNext( /*int|string $token1, int|string $token2, ... */) + { $this->_next(func_get_args()); return $this->current(); } @@ -288,7 +296,8 @@ class Tokenizer { * @param $token * @return bool */ - public function isNextToken($token) { + public function isNextToken($token) + { return $this->next ? $this->next[1] == $token : false; } @@ -298,15 +307,16 @@ class Tokenizer { * @param int $limit * @return string */ - public function getSubstr($offset, $limit = 0) { + public function getSubstr($offset, $limit = 0) + { $str = ''; - if(!$limit) { + if (!$limit) { $limit = $this->_max; } else { $limit += $offset; } - for($i = $offset; $i <= $limit; $i++){ - $str .= $this->tokens[$i][1].$this->tokens[$i][2]; + for ($i = $offset; $i <= $limit; $i++) { + $str .= $this->tokens[$i][1] . $this->tokens[$i][2]; } return $str; } @@ -316,8 +326,9 @@ class Tokenizer { * @return mixed * @throws UnexpectedTokenException */ - public function getAndNext() { - if($this->curr) { + public function getAndNext() + { + if ($this->curr) { $cur = $this->curr[1]; $this->next(); return $cur; @@ -331,7 +342,8 @@ class Tokenizer { * @param $token1 * @return bool */ - public function isNext($token1/*, ...*/) { + public function isNext($token1 /*, ...*/) + { return $this->next && $this->_valid(func_get_args(), $this->next[0]); } @@ -340,7 +352,8 @@ class Tokenizer { * @param $token1 * @return bool */ - public function is($token1/*, ...*/) { + public function is($token1 /*, ...*/) + { return $this->curr && $this->_valid(func_get_args(), $this->curr[0]); } @@ -349,7 +362,8 @@ class Tokenizer { * @param $token1 * @return bool */ - public function isPrev($token1/*, ...*/) { + public function isPrev($token1 /*, ...*/) + { return $this->prev && $this->_valid(func_get_args(), $this->prev[0]); } @@ -360,8 +374,9 @@ class Tokenizer { * @throws UnexpectedTokenException * @return mixed */ - public function get($token1 /*, $token2 ...*/) { - if($this->curr && $this->_valid(func_get_args(), $this->curr[0])) { + public function get($token1 /*, $token2 ...*/) + { + if ($this->curr && $this->_valid(func_get_args(), $this->curr[0])) { return $this->curr[1]; } else { throw new UnexpectedTokenException($this, func_get_args()); @@ -372,8 +387,9 @@ class Tokenizer { * Step back * @return Tokenizer */ - public function back() { - if($this->p === 0) { + public function back() + { + if ($this->p === 0) { return $this; } $this->p--; @@ -385,12 +401,13 @@ class Tokenizer { * @param $token1 * @return bool */ - public function hasBackList($token1 /*, $token2 ...*/) { + public function hasBackList($token1 /*, $token2 ...*/) + { $tokens = func_get_args(); $c = $this->p; - foreach($tokens as $token) { + foreach ($tokens as $token) { $c--; - if($c < 0 || $this->tokens[$c][0] !== $token) { + if ($c < 0 || $this->tokens[$c][0] !== $token) { return false; } } @@ -403,8 +420,9 @@ class Tokenizer { * @param string $key * @return mixed */ - public function __get($key) { - switch($key) { + public function __get($key) + { + switch ($key) { case 'curr': return $this->curr = ($this->p <= $this->_max) ? $this->tokens[$this->p] : null; case 'next': @@ -416,7 +434,8 @@ class Tokenizer { } } - public function count() { + public function count() + { return $this->_max; } @@ -424,7 +443,8 @@ class Tokenizer { * Return the key of the current element * @return mixed scalar on success, or null on failure. */ - public function key() { + public function key() + { return $this->curr ? $this->curr[0] : null; } @@ -433,7 +453,8 @@ class Tokenizer { * @return boolean The return value will be casted to boolean and then evaluated. * Returns true on success or false on failure. */ - public function valid() { + public function valid() + { return (bool)$this->curr; } @@ -443,12 +464,13 @@ class Tokenizer { * @param int|string $token * @return string */ - public static function getName($token) { - if(is_string($token)) { + public static function getName($token) + { + if (is_string($token)) { return $token; - } elseif(is_integer($token)) { + } elseif (is_integer($token)) { return token_name($token); - } elseif(is_array($token)) { + } elseif (is_array($token)) { return token_name($token[0]); } else { return null; @@ -461,9 +483,10 @@ class Tokenizer { * @throws UnexpectedTokenException * @return Tokenizer */ - public function skip(/*$token1, $token2, ...*/) { - if(func_num_args()) { - if($this->_valid(func_get_args(), $this->curr[0])) { + public function skip( /*$token1, $token2, ...*/) + { + if (func_num_args()) { + if ($this->_valid(func_get_args(), $this->curr[0])) { $this->next(); return $this; } else { @@ -481,8 +504,9 @@ class Tokenizer { * @param int|string $token1 * @return Tokenizer */ - public function skipIf($token1/*, $token2, ...*/) { - if($this->_valid(func_get_args(), $this->curr[0])) { + public function skipIf($token1 /*, $token2, ...*/) + { + if ($this->_valid(func_get_args(), $this->curr[0])) { $this->next(); } return $this; @@ -495,8 +519,9 @@ class Tokenizer { * @return Tokenizer * @throws UnexpectedTokenException */ - public function need($token1/*, $token2, ...*/) { - if($this->_valid(func_get_args(), $this->curr[0])) { + public function need($token1 /*, $token2, ...*/) + { + if ($this->_valid(func_get_args(), $this->curr[0])) { return $this; } else { throw new UnexpectedTokenException($this, func_get_args()); @@ -509,37 +534,38 @@ class Tokenizer { * @param int $after count tokens after current token * @return array */ - public function getSnippet($before = 0, $after = 0) { + public function getSnippet($before = 0, $after = 0) + { $from = 0; $to = $this->p; - if($before > 0) { - if($before > $this->p) { + if ($before > 0) { + if ($before > $this->p) { $from = $this->p; } else { $from = $before; } - } elseif($before < 0) { + } elseif ($before < 0) { $from = $this->p + $before; - if($from < 0) { + if ($from < 0) { $from = 0; } } - if($after > 0) { + if ($after > 0) { $to = $this->p + $after; - if($to > $this->_max) { + if ($to > $this->_max) { $to = $this->_max; } - } elseif($after < 0) { + } elseif ($after < 0) { $to = $this->_max + $after; - if($to < $this->p) { + if ($to < $this->p) { $to = $this->p; } - } elseif($this->p > $this->_max) { + } elseif ($this->p > $this->_max) { $to = $this->_max; } $code = array(); - for($i=$from; $i<=$to; $i++) { - $code[] = $this->tokens[ $i ]; + for ($i = $from; $i <= $to; $i++) { + $code[] = $this->tokens[$i]; } return $code; @@ -551,10 +577,11 @@ class Tokenizer { * @param int $after * @return string */ - public function getSnippetAsString($before = 0, $after = 0) { + public function getSnippetAsString($before = 0, $after = 0) + { $str = ""; - foreach($this->getSnippet($before, $after) as $token) { - $str .= $token[1].$token[2]; + foreach ($this->getSnippet($before, $after) as $token) { + $str .= $token[1] . $token[2]; } return trim(str_replace("\n", '↵', $str)); } @@ -563,7 +590,8 @@ class Tokenizer { * Check if current is special value: true, TRUE, false, FALSE, null, NULL * @return bool */ - public function isSpecialVal() { + public function isSpecialVal() + { return isset(self::$spec[$this->current()]); } @@ -571,14 +599,16 @@ class Tokenizer { * Check if the token is last * @return bool */ - public function isLast() { + public function isLast() + { return $this->p === $this->_max; } /** * Move pointer to the end */ - public function end() { + public function end() + { $this->p = $this->_max; } @@ -586,7 +616,8 @@ class Tokenizer { * Return line number of the current token * @return mixed */ - public function getLine() { + public function getLine() + { return $this->curr ? $this->curr[3] : $this->_last_no; } @@ -594,33 +625,13 @@ class Tokenizer { * Is current token whitespaced, means previous token has whitespace characters * @return bool */ - public function isWhiteSpaced() { + public function isWhiteSpaced() + { return $this->prev ? (bool)$this->prev[2] : false; } - public function getWhitespace() { + public function getWhitespace() + { return $this->curr ? $this->curr[2] : false; } } - -/** - * Unexpected token - */ -class UnexpectedTokenException extends \RuntimeException { - public function __construct(Tokenizer $tokens, $expect = null, $where = null) { - if($expect && count($expect) == 1 && is_string($expect[0])) { - $expect = ", expect '".$expect[0]."'"; - } else { - $expect = ""; - } - if(!$tokens->curr) { - $this->message = "Unexpected end of ".($where?:"expression")."$expect"; - } elseif($tokens->curr[0] === T_WHITESPACE) { - $this->message = "Unexpected whitespace$expect"; - } elseif($tokens->curr[0] === T_BAD_CHARACTER) { - $this->message = "Unexpected bad characters (below ASCII 32 except \\t, \\n and \\r) in ".($where?:"expression")."$expect"; - } else { - $this->message = "Unexpected token '".$tokens->current()."' in ".($where?:"expression")."$expect"; - } - } -}; diff --git a/src/Fenom/UnexpectedTokenException.php b/src/Fenom/UnexpectedTokenException.php new file mode 100644 index 0000000..c530b04 --- /dev/null +++ b/src/Fenom/UnexpectedTokenException.php @@ -0,0 +1,29 @@ +curr) { + $this->message = "Unexpected end of " . ($where ? : "expression") . "$expect"; + } elseif ($tokens->curr[0] === T_WHITESPACE) { + $this->message = "Unexpected whitespace$expect"; + } elseif ($tokens->curr[0] === T_BAD_CHARACTER) { + $this->message = "Unexpected bad characters (below ASCII 32 except \\t, \\n and \\r) in " . ($where ? : "expression") . "$expect"; + } else { + $this->message = "Unexpected token '" . $tokens->current() . "' in " . ($where ? : "expression") . "$expect"; + } + } +} + +; \ No newline at end of file diff --git a/tests/TestCase.php b/tests/TestCase.php index f021df8..4883d3e 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -2,7 +2,8 @@ namespace Fenom; use Fenom, Fenom\Provider as FS; -class TestCase extends \PHPUnit_Framework_TestCase { +class TestCase extends \PHPUnit_Framework_TestCase +{ /** * @var Fenom */ @@ -10,26 +11,27 @@ class TestCase extends \PHPUnit_Framework_TestCase { public $values = array( "zero" => 0, - "one" => 1, + "one" => 1, "two" => 2, "three" => 3, "float" => 4.5, - "bool" => true, + "bool" => true, 0 => "empty value", 1 => "one value", 2 => "two value", 3 => "three value", ); - public static function getVars() { + public static function getVars() + { return array( "zero" => 0, - "one" => 1, + "one" => 1, "two" => 2, "three" => 3, "float" => 4.5, - "bool" => true, - "obj" => new \StdClass, + "bool" => true, + "obj" => new \StdClass, "list" => array( "a" => 1, "b" => 2 @@ -41,50 +43,57 @@ class TestCase extends \PHPUnit_Framework_TestCase { ); } - public function setUp() { - if(!file_exists(FENOM_RESOURCES.'/compile')) { - mkdir(FENOM_RESOURCES.'/compile', 0777, true); + public function setUp() + { + if (!file_exists(FENOM_RESOURCES . '/compile')) { + mkdir(FENOM_RESOURCES . '/compile', 0777, true); } else { - FS::clean(FENOM_RESOURCES.'/compile/'); + FS::clean(FENOM_RESOURCES . '/compile/'); } - $this->fenom = Fenom::factory(FENOM_RESOURCES.'/template', FENOM_RESOURCES.'/compile'); - $this->fenom->addModifier('dots', __CLASS__.'::dots'); - $this->fenom->addModifier('concat', __CLASS__.'::concat'); - $this->fenom->addFunction('test_function', __CLASS__.'::inlineFunction'); - $this->fenom->addBlockFunction('test_block_function', __CLASS__.'::blockFunction'); + $this->fenom = Fenom::factory(FENOM_RESOURCES . '/template', FENOM_RESOURCES . '/compile'); + $this->fenom->addModifier('dots', __CLASS__ . '::dots'); + $this->fenom->addModifier('concat', __CLASS__ . '::concat'); + $this->fenom->addFunction('test_function', __CLASS__ . '::inlineFunction'); + $this->fenom->addBlockFunction('test_block_function', __CLASS__ . '::blockFunction'); } - public static function dots($value) { - return $value."..."; + public static function dots($value) + { + return $value . "..."; } - public static function concat() { - return call_user_func_array('var_export', func_get_args()); - } + public static function concat() + { + return call_user_func_array('var_export', func_get_args()); + } - public static function inlineFunction($params) { + public static function inlineFunction($params) + { return isset($params["text"]) ? $params["text"] : ""; } - public static function blockFunction($params, $text) { + public static function blockFunction($params, $text) + { return $text; } - public static function setUpBeforeClass() { - if(!file_exists(FENOM_RESOURCES.'/template')) { - mkdir(FENOM_RESOURCES.'/template', 0777, true); + public static function setUpBeforeClass() + { + if (!file_exists(FENOM_RESOURCES . '/template')) { + mkdir(FENOM_RESOURCES . '/template', 0777, true); } else { - FS::clean(FENOM_RESOURCES.'/template/'); + FS::clean(FENOM_RESOURCES . '/template/'); } } - public function tpl($name, $code) { + public function tpl($name, $code) + { $dir = dirname($name); - if($dir != "." && !is_dir(FENOM_RESOURCES.'/template/'.$dir)) { - mkdir(FENOM_RESOURCES.'/template/'.$dir, 0777, true); + if ($dir != "." && !is_dir(FENOM_RESOURCES . '/template/' . $dir)) { + mkdir(FENOM_RESOURCES . '/template/' . $dir, 0777, true); } - file_put_contents(FENOM_RESOURCES.'/template/'.$name, $code); + file_put_contents(FENOM_RESOURCES . '/template/' . $name, $code); } /** @@ -96,20 +105,22 @@ class TestCase extends \PHPUnit_Framework_TestCase { * @param int $options * @param bool $dump dump source and result code (for debug) */ - public function exec($code, $vars, $result, $options = 0, $dump = false) { + public function exec($code, $vars, $result, $options = 0, $dump = false) + { $this->fenom->setOptions($options); $tpl = $this->fenom->compileCode($code, "runtime.tpl"); - if($dump) { - echo "\n========= DUMP BEGIN ===========\n".$code."\n--- to ---\n".$tpl->getBody()."\n========= DUMP END =============\n"; + if ($dump) { + echo "\n========= DUMP BEGIN ===========\n" . $code . "\n--- to ---\n" . $tpl->getBody() . "\n========= DUMP END =============\n"; } $this->assertSame(Modifier::strip($result), Modifier::strip($tpl->fetch($vars), true), "Test $code"); } - public function execTpl($name, $code, $vars, $result, $dump = false) { + public function execTpl($name, $code, $vars, $result, $dump = false) + { $this->tpl($name, $code); $tpl = $this->fenom->getTemplate($name); - if($dump) { - echo "\n========= DUMP BEGIN ===========\n".$code."\n--- to ---\n".$tpl->getBody()."\n========= DUMP END =============\n"; + if ($dump) { + echo "\n========= DUMP BEGIN ===========\n" . $code . "\n--- to ---\n" . $tpl->getBody() . "\n========= DUMP END =============\n"; } $this->assertSame(Modifier::strip($result, true), Modifier::strip($tpl->fetch($vars), true), "Test tpl $name"); } @@ -121,12 +132,13 @@ class TestCase extends \PHPUnit_Framework_TestCase { * @param string $message exception message * @param int $options Fenom's options */ - public function execError($code, $exception, $message, $options = 0) { + public function execError($code, $exception, $message, $options = 0) + { $opt = $this->fenom->getOptions(); $this->fenom->setOptions($options); try { $this->fenom->compileCode($code, "inline.tpl"); - } catch(\Exception $e) { + } catch (\Exception $e) { $this->assertSame($exception, get_class($e), "Exception $code"); $this->assertStringStartsWith($message, $e->getMessage()); $this->fenom->setOptions($opt); @@ -136,28 +148,31 @@ class TestCase extends \PHPUnit_Framework_TestCase { $this->fail("Code $code must be invalid"); } - public function assertRender($tpl, $result, $debug = false) { + public function assertRender($tpl, $result, $debug = false) + { $template = $this->fenom->compileCode($tpl); - if($debug) { - print_r("$tpl:\n".$template->getBody()); + if ($debug) { + print_r("$tpl:\n" . $template->getBody()); } $this->assertSame($result, $template->fetch($this->values)); } - public static function providerNumbers() { - return array( - array('0', 0), - array('77', 77), - array('-33', -33), - array('0.2', 0.2), - array('-0.3', -0.3), - array('1e6', 1e6), - array('-2e6', -2e6), - ); - } + public static function providerNumbers() + { + return array( + array('0', 0), + array('77', 77), + array('-33', -33), + array('0.2', 0.2), + array('-0.3', -0.3), + array('1e6', 1e6), + array('-2e6', -2e6), + ); + } - public static function providerStrings() { + public static function providerStrings() + { return array( array('"str"', 'str'), array('"str\nand\nmany\nlines"', "str\nand\nmany\nlines"), @@ -187,81 +202,92 @@ class TestCase extends \PHPUnit_Framework_TestCase { ); } - public function providerVariables() { - return array(); - } + public function providerVariables() + { + return array(); + } - public static function providerObjects() { - return array(); - } + public static function providerObjects() + { + return array(); + } - public static function providerArrays() { - $scalars = array(); - $data = array( - array('[]', array()), - array('[[],[]]', array(array(), array())), - ); - foreach(self::providerScalars() as $scalar) { - $scalars[0][] = $scalar[0]; - $scalars[1][] = $scalar[1]; + public static function providerArrays() + { + $scalars = array(); + $data = array( + array('[]', array()), + array('[[],[]]', array(array(), array())), + ); + foreach (self::providerScalars() as $scalar) { + $scalars[0][] = $scalar[0]; + $scalars[1][] = $scalar[1]; - $data[] = array( - "[".$scalar[0]."]", - array($scalar[1]) - ); - $data[] = array( - "['some_key' =>".$scalar[0]."]", - array('some_key' => $scalar[1]) - ); - } - $data[] = array( - "[".implode(", ", $scalars[0])."]", - $scalars[1] - ); - return $data; - } + $data[] = array( + "[" . $scalar[0] . "]", + array($scalar[1]) + ); + $data[] = array( + "['some_key' =>" . $scalar[0] . "]", + array('some_key' => $scalar[1]) + ); + } + $data[] = array( + "[" . implode(", ", $scalars[0]) . "]", + $scalars[1] + ); + return $data; + } - public static function providerScalars() { - return array_merge( - self::providerNumbers(), - self::providerStrings() - ); - } + public static function providerScalars() + { + return array_merge( + self::providerNumbers(), + self::providerStrings() + ); + } - public static function providerValues() { - return array_merge( - self::providerScalars(), - self::providerArrays(), - self::providerVariables(), - self::providerObjects() - ); - } + public static function providerValues() + { + return array_merge( + self::providerScalars(), + self::providerArrays(), + self::providerVariables(), + self::providerObjects() + ); + } } -class Fake implements \ArrayAccess { +class Fake implements \ArrayAccess +{ public $vars; - public function offsetExists($offset) { + public function offsetExists($offset) + { return true; } - public function offsetGet($offset) { - if($offset == "object") { + public function offsetGet($offset) + { + if ($offset == "object") { return new self(); } else { return new self($offset); } } - public function offsetSet($offset, $value) { + public function offsetSet($offset, $value) + { $this->vars[$offset] = $value; } - public function offsetUnset($offset) { + public function offsetUnset($offset) + { unset($this->vars[$offset]); } - public function proxy() { - return implode(", ", func_get_args()); - } + public function proxy() + { + return implode(", ", func_get_args()); + } } diff --git a/tests/autoload.php b/tests/autoload.php index 68e6c9a..685492c 100644 --- a/tests/autoload.php +++ b/tests/autoload.php @@ -1,26 +1,27 @@ getTraceAsString()."\n"; + echo "-------\nDump trace: \n" . $e->getTraceAsString() . "\n"; exit(); } -function dump() { - foreach(func_get_args() as $arg) { - fwrite(STDERR, "DUMP: ".call_user_func("print_r", $arg, true)."\n"); +function dump() +{ + foreach (func_get_args() as $arg) { + fwrite(STDERR, "DUMP: " . call_user_func("print_r", $arg, true) . "\n"); - } + } } \ No newline at end of file diff --git a/tests/cases/Fenom/AutoEscapeTest.php b/tests/cases/Fenom/AutoEscapeTest.php index 5044335..f8ecc17 100644 --- a/tests/cases/Fenom/AutoEscapeTest.php +++ b/tests/cases/Fenom/AutoEscapeTest.php @@ -3,9 +3,11 @@ namespace Fenom; -class AutoEscapeTest extends TestCase { +class AutoEscapeTest extends TestCase +{ - public static function providerHTML() { + public static function providerHTML() + { $html = ""; $escaped = htmlspecialchars($html, ENT_COMPAT, 'UTF-8'); $vars = array( @@ -17,7 +19,7 @@ class AutoEscapeTest extends TestCase { array('{$html}, {$html}', "$escaped, $escaped", $vars, \Fenom::AUTO_ESCAPE), array('{raw $html}, {$html}', "$html, $escaped", $vars, \Fenom::AUTO_ESCAPE), array('{raw $html}, {$html}', "$html, $escaped", $vars, \Fenom::AUTO_ESCAPE), - array('{raw "{$html|up}"}, {$html}', strtoupper($html).", $escaped", $vars, \Fenom::AUTO_ESCAPE), + array('{raw "{$html|up}"}, {$html}', strtoupper($html) . ", $escaped", $vars, \Fenom::AUTO_ESCAPE), array('{autoescape true}{$html}{/autoescape}, {$html}', "$escaped, $html", $vars, 0), array('{autoescape false}{$html}{/autoescape}, {$html}', "$html, $escaped", $vars, \Fenom::AUTO_ESCAPE), array('{autoescape true}{$html}{/autoescape}, {$html}', "$escaped, $escaped", $vars, \Fenom::AUTO_ESCAPE), @@ -31,7 +33,7 @@ class AutoEscapeTest extends TestCase { 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('{raw:test_function 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), @@ -56,7 +58,8 @@ class AutoEscapeTest extends TestCase { /** * @dataProvider providerHTML */ - public function testEscaping($tpl, $result, $vars, $options) { + public function testEscaping($tpl, $result, $vars, $options) + { $this->values = $vars; $this->fenom->setOptions($options); $this->assertRender($tpl, $result); diff --git a/tests/cases/Fenom/CommentTest.php b/tests/cases/Fenom/CommentTest.php index 98b791f..dddf17c 100644 --- a/tests/cases/Fenom/CommentTest.php +++ b/tests/cases/Fenom/CommentTest.php @@ -3,29 +3,33 @@ namespace Fenom; -class CommentTest extends TestCase { +class CommentTest extends TestCase +{ /** * @dataProvider providerScalars */ - public function testInline($tpl_val) { + public function testInline($tpl_val) + { $this->assertRender("before {* $tpl_val *} after", "before after"); $this->assertRender("before {* {{$tpl_val}} {{$tpl_val}} *} after", "before after"); $this->assertRender("before {*{{$tpl_val}}*} after", "before after"); } - public function testError() { + public function testError() + { $this->execError('{* ', 'Fenom\CompileException', "Unclosed comment block in line"); } - /** - * @dataProvider providerScalars - */ - public function testMultiLine($tpl_val) { - $this->assertRender( - "before-1\nbefore-2 {* before-3\nbefore-4 $tpl_val after-1\nafter-2 *} after-3\nafter-4{* dummy *}\nafter-5", - "before-1\nbefore-2 after-3\nafter-4\nafter-5" - ); + /** + * @dataProvider providerScalars + */ + public function testMultiLine($tpl_val) + { + $this->assertRender( + "before-1\nbefore-2 {* before-3\nbefore-4 $tpl_val after-1\nafter-2 *} after-3\nafter-4{* dummy *}\nafter-5", + "before-1\nbefore-2 after-3\nafter-4\nafter-5" + ); } } \ No newline at end of file diff --git a/tests/cases/Fenom/CustomProviderTest.php b/tests/cases/Fenom/CustomProviderTest.php index dfa608b..137ead6 100644 --- a/tests/cases/Fenom/CustomProviderTest.php +++ b/tests/cases/Fenom/CustomProviderTest.php @@ -3,14 +3,17 @@ namespace Fenom; -class CustomProviderTest extends TestCase { +class CustomProviderTest extends TestCase +{ - public function setUp() { + public function setUp() + { parent::setUp(); - $this->fenom->addProvider("my", new Provider(FENOM_RESOURCES.'/provider')); + $this->fenom->addProvider("my", new Provider(FENOM_RESOURCES . '/provider')); } - public function testCustom() { + public function testCustom() + { $this->assertRender("start: {include 'my:include.tpl'}", 'start: include template'); //$this->assertRender("start: {import 'my:macros.tpl' as ops} {ops.add a=3 b=6}"); } diff --git a/tests/cases/Fenom/ExtendsTemplateTest.php b/tests/cases/Fenom/ExtendsTemplateTest.php index 4b43f6d..04cdf47 100644 --- a/tests/cases/Fenom/ExtendsTemplateTest.php +++ b/tests/cases/Fenom/ExtendsTemplateTest.php @@ -2,41 +2,44 @@ namespace Fenom; use Fenom, Fenom\TestCase; -class ExtendsTemplateTest extends TestCase { +class ExtendsTemplateTest extends TestCase +{ - public function _testSandbox() { - $this->fenom = Fenom::factory(FENOM_RESOURCES.'/provider', FENOM_RESOURCES.'/compile'); - try { - print_r($this->fenom->getTemplate('use/child.tpl')->getBody()); - } catch (\Exception $e) { - echo "$e"; - } - exit; - } + public function _testSandbox() + { + $this->fenom = Fenom::factory(FENOM_RESOURCES . '/provider', FENOM_RESOURCES . '/compile'); + try { + print_r($this->fenom->getTemplate('use/child.tpl')->getBody()); + } catch (\Exception $e) { + echo "$e"; + } + exit; + } - /** - * Templates skeletons - * @param array $vars - * @return array - */ - public static function templates(array $vars) { + /** + * Templates skeletons + * @param array $vars + * @return array + */ + public static function templates(array $vars) + { return array( array( - "name" => "level.0.tpl", + "name" => "level.0.tpl", "level" => 0, "blocks" => array( "b1" => "default {\$default}", "b2" => "empty 0" ), "result" => array( - "b1" => "default ".$vars["default"], + "b1" => "default " . $vars["default"], "b2" => "empty 0" ), ), array( - "name" => "level.1.tpl", + "name" => "level.1.tpl", "level" => 1, - "use" => false, + "use" => false, "blocks" => array( "b1" => "from level 1" ), @@ -46,9 +49,9 @@ class ExtendsTemplateTest extends TestCase { ), ), array( - "name" => "level.2.tpl", + "name" => "level.2.tpl", "level" => 2, - "use" => false, + "use" => false, "blocks" => array( "b2" => "from level 2", "b4" => "unused block" @@ -59,9 +62,9 @@ class ExtendsTemplateTest extends TestCase { ), ), array( - "name" => "level.3.tpl", + "name" => "level.3.tpl", "level" => 3, - "use" => false, + "use" => false, "blocks" => array( "b1" => "from level 3", "b2" => "also from level 3" @@ -74,35 +77,37 @@ class ExtendsTemplateTest extends TestCase { ); } - /** - * Generate templates by skeletons - * - * @param $block_mask - * @param $extend_mask - * @param array $skels - * @return array - */ - public static function generate($block_mask, $extend_mask, $skels) { + /** + * Generate templates by skeletons + * + * @param $block_mask + * @param $extend_mask + * @param array $skels + * @return array + */ + public static function generate($block_mask, $extend_mask, $skels) + { $t = array(); - foreach($skels as $level => $tpl) { - $src = 'level#'.$level.' '; + foreach ($skels as $level => $tpl) { + $src = 'level#' . $level . ' '; - foreach($tpl["blocks"] as $bname => &$bcode) { - $src .= sprintf($block_mask, $bname, $bname.': '.$bcode)." level#$level "; + foreach ($tpl["blocks"] as $bname => &$bcode) { + $src .= sprintf($block_mask, $bname, $bname . ': ' . $bcode) . " level#$level "; } $dst = "level#0 "; - foreach($tpl["result"] as $bname => &$bcode) { - $dst .= $bname.': '.$bcode.' level#0 '; + foreach ($tpl["result"] as $bname => &$bcode) { + $dst .= $bname . ': ' . $bcode . ' level#0 '; } - if($level) { - $src = sprintf($extend_mask, $level-1).' '.$src; + if ($level) { + $src = sprintf($extend_mask, $level - 1) . ' ' . $src; } - $t[ $tpl["name"] ] = array("src" => $src, "dst" => $dst); + $t[$tpl["name"]] = array("src" => $src, "dst" => $dst); } return $t; } - public function _testTemplateExtends() { + public function _testTemplateExtends() + { $vars = array( "b1" => "b1", "b2" => "b2", @@ -112,39 +117,41 @@ class ExtendsTemplateTest extends TestCase { "default" => 5 ); $tpls = self::generate('{block "%s"}%s{/block}', '{extends "level.%d.tpl"}', self::templates($vars)); - foreach($tpls as $name => $tpl) { + foreach ($tpls as $name => $tpl) { $this->tpl($name, $tpl["src"]); $this->assertSame($this->fenom->fetch($name, $vars), $tpl["dst"]); } - return; + return; $vars["default"]++; $this->fenom->flush(); $tpls = self::generate('{block "{$%s}"}%s{/block}', '{extends "level.%d.tpl"}', self::templates($vars)); arsort($tpls); - foreach($tpls as $name => $tpl) { - $this->tpl("d.".$name, $tpl["src"]); - $this->assertSame($this->fenom->fetch("d.".$name, $vars), $tpl["dst"]); + foreach ($tpls as $name => $tpl) { + $this->tpl("d." . $name, $tpl["src"]); + $this->assertSame($this->fenom->fetch("d." . $name, $vars), $tpl["dst"]); } $vars["default"]++; $this->fenom->flush(); $tpls = self::generate('{block "%s"}%s{/block}', '{extends "$level.%d.tpl"}', self::templates($vars)); arsort($tpls); - foreach($tpls as $name => $tpl) { - $this->tpl("x.".$name, $tpl["src"]); - $this->assertSame($this->fenom->fetch("x.".$name, $vars), $tpl["dst"]); + foreach ($tpls as $name => $tpl) { + $this->tpl("x." . $name, $tpl["src"]); + $this->assertSame($this->fenom->fetch("x." . $name, $vars), $tpl["dst"]); } } - /** - * @group use - */ - public function testUse() { - $this->fenom = Fenom::factory(FENOM_RESOURCES.'/provider', FENOM_RESOURCES.'/compile'); - $this->assertSame("\n block 1 blocks \n block 2 child \n", $this->fenom->fetch('use/child.tpl')); - } + /** + * @group use + */ + public function testUse() + { + $this->fenom = Fenom::factory(FENOM_RESOURCES . '/provider', FENOM_RESOURCES . '/compile'); + $this->assertSame("\n block 1 blocks \n block 2 child \n", $this->fenom->fetch('use/child.tpl')); + } - public function _testParent() { + public function _testParent() + { - } + } } diff --git a/tests/cases/Fenom/FunctionsTest.php b/tests/cases/Fenom/FunctionsTest.php index 755a1e0..cad5e1f 100644 --- a/tests/cases/Fenom/FunctionsTest.php +++ b/tests/cases/Fenom/FunctionsTest.php @@ -1,70 +1,82 @@ fenom->addFunctionSmart('sum', __CLASS__ . '::functionSum'); - $this->fenom->addFunctionSmart('pow', __CLASS__ . '::functionPow'); - $this->fenom->addFunctionSmart('inc', __CLASS__ . '::functionInc'); + $this->fenom->addFunctionSmart('sum', __CLASS__ . '::functionSum'); + $this->fenom->addFunctionSmart('pow', __CLASS__ . '::functionPow'); + $this->fenom->addFunctionSmart('inc', __CLASS__ . '::functionInc'); - $this->tpl('function_params_scalar.tpl', '{pow a=2 n=3}'); - $this->tpl('function_params_dynamic.tpl', '{pow a=$a n=$n}'); - $this->tpl('function_default_param_scalar.tpl', '{pow a=2}'); - $this->tpl('function_default_param_empty_array.tpl', '{sum}'); - $this->tpl('function_default_param_const.tpl', '{inc a=1}'); - $this->tpl('function_array_param.tpl', '{sum of=[1, 2, 3, 4, 5]}'); - $this->tpl('function_array_param_pos.tpl', '{sum [1, 2, 3, 4, 5]}'); - } + $this->tpl('function_params_scalar.tpl', '{pow a=2 n=3}'); + $this->tpl('function_params_dynamic.tpl', '{pow a=$a n=$n}'); + $this->tpl('function_default_param_scalar.tpl', '{pow a=2}'); + $this->tpl('function_default_param_empty_array.tpl', '{sum}'); + $this->tpl('function_default_param_const.tpl', '{inc a=1}'); + $this->tpl('function_array_param.tpl', '{sum of=[1, 2, 3, 4, 5]}'); + $this->tpl('function_array_param_pos.tpl', '{sum [1, 2, 3, 4, 5]}'); + } - public function testFunctionWithParams() { + public function testFunctionWithParams() + { $output = $this->fenom->fetch('function_params_scalar.tpl'); - $this->assertEquals('8', $output); + $this->assertEquals('8', $output); } - public function testFunctionWithDynamicParams() { + public function testFunctionWithDynamicParams() + { $output = $this->fenom->fetch('function_params_dynamic.tpl', array('a' => 3, 'n' => 4)); - $this->assertEquals('81', $output); + $this->assertEquals('81', $output); } - public function testFunctionWithDefaultParamScalar() { - $output = $this->fenom->fetch('function_default_param_scalar.tpl'); - $this->assertEquals('4', $output); - } + public function testFunctionWithDefaultParamScalar() + { + $output = $this->fenom->fetch('function_default_param_scalar.tpl'); + $this->assertEquals('4', $output); + } - public function testFunctionWithDefaultParamArray() { - $output = $this->fenom->fetch('function_default_param_empty_array.tpl'); - $this->assertEquals('0', $output); - } + public function testFunctionWithDefaultParamArray() + { + $output = $this->fenom->fetch('function_default_param_empty_array.tpl'); + $this->assertEquals('0', $output); + } - public function testFunctionWithDefaultParamConst() { - $output = $this->fenom->fetch('function_default_param_const.tpl'); - $this->assertEquals('2', $output); - } + public function testFunctionWithDefaultParamConst() + { + $output = $this->fenom->fetch('function_default_param_const.tpl'); + $this->assertEquals('2', $output); + } - public function testFunctionWithArrayNamedParam() { - $output = $this->fenom->fetch('function_array_param.tpl'); - $this->assertEquals('15', $output); - } + public function testFunctionWithArrayNamedParam() + { + $output = $this->fenom->fetch('function_array_param.tpl'); + $this->assertEquals('15', $output); + } - public function testFunctionWithArrayPositionalParam() { - $output = $this->fenom->fetch('function_array_param_pos.tpl'); - $this->assertEquals('15', $output); - } + public function testFunctionWithArrayPositionalParam() + { + $output = $this->fenom->fetch('function_array_param_pos.tpl'); + $this->assertEquals('15', $output); + } } diff --git a/tests/cases/Fenom/MacrosTest.php b/tests/cases/Fenom/MacrosTest.php index c56bae8..00ecda7 100644 --- a/tests/cases/Fenom/MacrosTest.php +++ b/tests/cases/Fenom/MacrosTest.php @@ -1,9 +1,11 @@ tpl("math.tpl", ' {macro plus(x, y)} @@ -52,31 +54,35 @@ class MacrosTest extends TestCase { {macro.factorial num=10}'); } - public function _testSandbox() { + public function _testSandbox() + { try { - $this->fenom->compile("macro_recursive.tpl"); - $this->fenom->flush(); - var_dump($this->fenom->fetch("macro_recursive.tpl", [])); - } catch(\Exception $e) { - var_dump($e->getMessage().": ".$e->getTraceAsString()); + $this->fenom->compile("macro_recursive.tpl"); + $this->fenom->flush(); + var_dump($this->fenom->fetch("macro_recursive.tpl", [])); + } catch (\Exception $e) { + var_dump($e->getMessage() . ": " . $e->getTraceAsString()); } exit; } - public function testMacros() { + public function testMacros() + { $tpl = $this->fenom->compile('math.tpl'); $this->assertStringStartsWith('x + y = ', trim($tpl->macros["plus"]["body"])); $this->assertSame('Math: x + y = 5 , x - y - z = 6', Modifier::strip($tpl->fetch(array()), true)); } - public function testImport() { + public function testImport() + { $tpl = $this->fenom->compile('import.tpl'); $this->assertSame('Imp: x + y = 3 , x - y - z = 3', Modifier::strip($tpl->fetch(array()), true)); } - public function testImportCustom() { + public function testImportCustom() + { $tpl = $this->fenom->compile('import_custom.tpl'); $this->assertSame('a: x + y = 3 , x - y - z = 3 , new minus macros .', Modifier::strip($tpl->fetch(array()), true)); @@ -86,13 +92,15 @@ class MacrosTest extends TestCase { * @expectedExceptionMessage Undefined macro 'plus' * @expectedException \Fenom\CompileException */ - public function testImportMiss() { + public function testImportMiss() + { $tpl = $this->fenom->compile('import_miss.tpl'); $this->assertSame('a: x + y = 3 , x - y - z = 3 , new minus macros .', Modifier::strip($tpl->fetch(array()), true)); } - public function testRecursive() { + public function testRecursive() + { $this->fenom->compile('macro_recursive.tpl'); $this->fenom->flush(); $tpl = $this->fenom->getTemplate('macro_recursive.tpl'); diff --git a/tests/cases/Fenom/ModifiersTest.php b/tests/cases/Fenom/ModifiersTest.php index abf21ef..3081b31 100644 --- a/tests/cases/Fenom/ModifiersTest.php +++ b/tests/cases/Fenom/ModifiersTest.php @@ -2,11 +2,13 @@ namespace Fenom; -class ModifiersTest extends TestCase { +class ModifiersTest extends TestCase +{ - public static function providerTruncate() { + public static function providerTruncate() + { $lorem = 'Lorem ipsum dolor sit amet'; // en - $uni = 'Лорем ипсум долор сит амет'; // ru + $uni = 'Лорем ипсум долор сит амет'; // ru return array( // ascii chars array($lorem, 'Lorem ip...', 8), @@ -33,7 +35,8 @@ class ModifiersTest extends TestCase { * @param bool $by_words * @param bool $middle */ - public function testTruncate($in, $out, $count, $delim = '...', $by_words = false, $middle = false) { + public function testTruncate($in, $out, $count, $delim = '...', $by_words = false, $middle = false) + { $tpl = $this->fenom->compileCode('{$text|truncate:$count:$delim:$by_words:$middle}'); $this->assertEquals($out, $tpl->fetch(array( "text" => $in, @@ -44,7 +47,8 @@ class ModifiersTest extends TestCase { ))); } - public static function providerUpLow() { + public static function providerUpLow() + { return array( array("up", "lorem", "LOREM"), array("up", "Lorem", "LOREM"), @@ -64,14 +68,16 @@ class ModifiersTest extends TestCase { * @param $in * @param $out */ - public function testUpLow($modifier, $in, $out) { - $tpl = $this->fenom->compileCode('{$text|'.$modifier.'}'); + public function testUpLow($modifier, $in, $out) + { + $tpl = $this->fenom->compileCode('{$text|' . $modifier . '}'); $this->assertEquals($out, $tpl->fetch(array( "text" => $in, ))); } - public static function providerLength() { + public static function providerLength() + { return array( array("length", 6), array("длина", 5), @@ -90,7 +96,8 @@ class ModifiersTest extends TestCase { * @param $in * @param $out */ - public function testLength($in, $out) { + public function testLength($in, $out) + { $tpl = $this->fenom->compileCode('{$data|length}'); $this->assertEquals($out, $tpl->fetch(array( "data" => $in, diff --git a/tests/cases/Fenom/ProviderTest.php b/tests/cases/Fenom/ProviderTest.php index d416adc..bbd1cd7 100644 --- a/tests/cases/Fenom/ProviderTest.php +++ b/tests/cases/Fenom/ProviderTest.php @@ -3,87 +3,97 @@ namespace Fenom; use Fenom; use Fenom\TestCase; -class ProviderTest extends TestCase { +class ProviderTest extends TestCase +{ /** * @var Provider */ public $provider; - public function setUp() { + public function setUp() + { parent::setUp(); $this->tpl("template1.tpl", 'Template 1 {$a}'); $this->tpl("template2.tpl", 'Template 2 {$a}'); $this->tpl("sub/template3.tpl", 'Template 3 {$a}'); - $this->provider = new Provider(FENOM_RESOURCES.'/template'); + $this->provider = new Provider(FENOM_RESOURCES . '/template'); clearstatcache(); } - public function testIsTemplateExists() { + public function testIsTemplateExists() + { clearstatcache(); $this->assertTrue($this->provider->templateExists("template1.tpl")); $this->assertFalse($this->provider->templateExists("unexists.tpl")); } - public function testGetSource() { + public function testGetSource() + { clearstatcache(); $src = $this->provider->getSource("template1.tpl", $time); clearstatcache(); - $this->assertEquals(file_get_contents(FENOM_RESOURCES.'/template/template1.tpl'), $src); - $this->assertEquals(filemtime(FENOM_RESOURCES.'/template/template1.tpl'), $time); + $this->assertEquals(file_get_contents(FENOM_RESOURCES . '/template/template1.tpl'), $src); + $this->assertEquals(filemtime(FENOM_RESOURCES . '/template/template1.tpl'), $time); } /** * @expectedException \RuntimeException */ - public function testGetSourceInvalid() { + public function testGetSourceInvalid() + { $this->provider->getSource("unexists.tpl", $time); } - public function testGetLastModified() { + public function testGetLastModified() + { $time = $this->provider->getLastModified("template1.tpl"); clearstatcache(); - $this->assertEquals(filemtime(FENOM_RESOURCES.'/template/template1.tpl'), $time); + $this->assertEquals(filemtime(FENOM_RESOURCES . '/template/template1.tpl'), $time); } /** * @expectedException \RuntimeException */ - public function testGetLastModifiedInvalid() { + public function testGetLastModifiedInvalid() + { $this->provider->getLastModified("unexists.tpl"); } - public function testVerify() { + public function testVerify() + { $templates = array( - "template1.tpl" => filemtime(FENOM_RESOURCES.'/template/template1.tpl'), - "template2.tpl" => filemtime(FENOM_RESOURCES.'/template/template2.tpl') + "template1.tpl" => filemtime(FENOM_RESOURCES . '/template/template1.tpl'), + "template2.tpl" => filemtime(FENOM_RESOURCES . '/template/template2.tpl') ); clearstatcache(); $this->assertTrue($this->provider->verify($templates)); clearstatcache(); $templates = array( - "template2.tpl" => filemtime(FENOM_RESOURCES.'/template/template2.tpl'), - "template1.tpl" => filemtime(FENOM_RESOURCES.'/template/template1.tpl') + "template2.tpl" => filemtime(FENOM_RESOURCES . '/template/template2.tpl'), + "template1.tpl" => filemtime(FENOM_RESOURCES . '/template/template1.tpl') ); clearstatcache(); $this->assertTrue($this->provider->verify($templates)); } - public function testVerifyInvalid() { + public function testVerifyInvalid() + { $templates = array( - "template1.tpl" => filemtime(FENOM_RESOURCES.'/template/template1.tpl'), - "template2.tpl" => filemtime(FENOM_RESOURCES.'/template/template2.tpl') + 1 + "template1.tpl" => filemtime(FENOM_RESOURCES . '/template/template1.tpl'), + "template2.tpl" => filemtime(FENOM_RESOURCES . '/template/template2.tpl') + 1 ); clearstatcache(); $this->assertFalse($this->provider->verify($templates)); clearstatcache(); $templates = array( - "template1.tpl" => filemtime(FENOM_RESOURCES.'/template/template1.tpl'), + "template1.tpl" => filemtime(FENOM_RESOURCES . '/template/template1.tpl'), "unexists.tpl" => 1234567890 ); $this->assertFalse($this->provider->verify($templates)); } - public function testGetAll() { + public function testGetAll() + { $list = $this->provider->getList(); sort($list); $this->assertSame(array( diff --git a/tests/cases/Fenom/RenderTest.php b/tests/cases/Fenom/RenderTest.php index 8d1c7c1..c71565b 100644 --- a/tests/cases/Fenom/RenderTest.php +++ b/tests/cases/Fenom/RenderTest.php @@ -3,22 +3,25 @@ namespace Fenom; use Fenom, Fenom\Render; -class RenderTest extends \PHPUnit_Framework_TestCase { +class RenderTest extends \PHPUnit_Framework_TestCase +{ /** * @var Render */ public static $render; - public static function setUpBeforeClass() { + public static function setUpBeforeClass() + { self::$render = new Render(Fenom::factory("."), function ($tpl) { - echo "It is render's function ".$tpl["render"]; + echo "It is render's function " . $tpl["render"]; }, array( "name" => "render.tpl" )); } - public function testCreate() { + public function testCreate() + { $r = new Render(Fenom::factory("."), function () { echo "Test render"; }, array( @@ -27,14 +30,16 @@ class RenderTest extends \PHPUnit_Framework_TestCase { $this->assertSame("Test render", $r->fetch(array())); } - public function testDisplay() { + public function testDisplay() + { ob_start(); self::$render->display(array("render" => "display")); $out = ob_get_clean(); $this->assertSame("It is render's function display", $out); } - public function testFetch() { + public function testFetch() + { $this->assertSame("It is render's function fetch", self::$render->fetch(array("render" => "fetch"))); } @@ -42,7 +47,8 @@ class RenderTest extends \PHPUnit_Framework_TestCase { * @expectedException \RuntimeException * @expectedExceptionMessage template error */ - public function testFetchException() { + public function testFetchException() + { $render = new Render(Fenom::factory("."), function () { echo "error"; throw new \RuntimeException("template error"); diff --git a/tests/cases/Fenom/ScopeTest.php b/tests/cases/Fenom/ScopeTest.php index 2018459..9b3a7d5 100644 --- a/tests/cases/Fenom/ScopeTest.php +++ b/tests/cases/Fenom/ScopeTest.php @@ -1,22 +1,26 @@ assertInstanceOf('Fenom\Tokenizer', $tokenizer); $this->assertInstanceOf('Fenom\Scope', $scope); $scope["value"] = true; return "open-tag"; } - public function closeTag($tokenizer, $scope) { + public function closeTag($tokenizer, $scope) + { $this->assertInstanceOf('Fenom\Tokenizer', $tokenizer); $this->assertInstanceOf('Fenom\Scope', $scope); $this->assertTrue($scope["value"]); return "close-tag"; } - public function testBlock() { + public function testBlock() + { /*$scope = new Scope($this->fenom, new Template($this->fenom), 1, array( "open" => array($this, "openTag"), "close" => array($this, "closeTag") @@ -28,5 +32,5 @@ class ScopeTest extends TestCase { $content = " some ?> content\n\nwith /*#9999999#* / many\n\tlines"; $scope->tpl->_body = "start open($tokenizer)." ?>".$content; $this->assertSame($content, $scope->getContent());*/ - } + } } diff --git a/tests/cases/Fenom/TagsTest.php b/tests/cases/Fenom/TagsTest.php index 111483d..ade43e8 100644 --- a/tests/cases/Fenom/TagsTest.php +++ b/tests/cases/Fenom/TagsTest.php @@ -3,54 +3,62 @@ namespace Fenom; -class TagsTest extends TestCase { +class TagsTest extends TestCase +{ - public function _testSandbox() { - try { - var_dump($this->fenom->compileCode('{var $a=Fenom\TestCase::dots("asd")}')->getBody()); - } catch(\Exception $e) { - echo "$e"; - } - exit; - } - - /** - * @dataProvider providerScalars - */ - public function testVar($tpl_val, $val) { - $this->assertRender("{var \$a=$tpl_val}\nVar: {\$a}", "\nVar: ".$val); + public function _testSandbox() + { + try { + var_dump($this->fenom->compileCode('{var $a=Fenom\TestCase::dots("asd")}')->getBody()); + } catch (\Exception $e) { + echo "$e"; + } + exit; } /** * @dataProvider providerScalars */ - public function testVarBlock($tpl_val, $val) { - $this->assertRender("{var \$a}before {{$tpl_val}} after{/var}\nVar: {\$a}", "\nVar: before ".$val." after"); + public function testVar($tpl_val, $val) + { + $this->assertRender("{var \$a=$tpl_val}\nVar: {\$a}", "\nVar: " . $val); } /** * @dataProvider providerScalars */ - public function testVarBlockModified($tpl_val, $val) { - $this->assertRender("{var \$a|low|dots}before {{$tpl_val}} after{/var}\nVar: {\$a}", "\nVar: ".strtolower("before ".$val." after")."..."); + public function testVarBlock($tpl_val, $val) + { + $this->assertRender("{var \$a}before {{$tpl_val}} after{/var}\nVar: {\$a}", "\nVar: before " . $val . " after"); } - public function testCycle() { - $this->assertRender('{for $i=0 to=4}{cycle ["one", "two"]}, {/for}', "one, two, one, two, one, "); + /** + * @dataProvider providerScalars + */ + public function testVarBlockModified($tpl_val, $val) + { + $this->assertRender("{var \$a|low|dots}before {{$tpl_val}} after{/var}\nVar: {\$a}", "\nVar: " . strtolower("before " . $val . " after") . "..."); } - /** - * - */ - public function testCycleIndex() { - $this->assertRender('{var $a=["one", "two"]}{for $i=1 to=5}{cycle $a index=$i}, {/for}', "two, one, two, one, two, "); - } + public function testCycle() + { + $this->assertRender('{for $i=0 to=4}{cycle ["one", "two"]}, {/for}', "one, two, one, two, one, "); + } - /** - * @dataProvider providerScalars - */ - public function testFilter($tpl_val, $val) { - $this->assertRender("{filter|up} before {{$tpl_val}} after {/filter}", strtoupper(" before {$val} after ")); + /** + * + */ + public function testCycleIndex() + { + $this->assertRender('{var $a=["one", "two"]}{for $i=1 to=5}{cycle $a index=$i}, {/for}', "two, one, two, one, two, "); + } + + /** + * @dataProvider providerScalars + */ + public function testFilter($tpl_val, $val) + { + $this->assertRender("{filter|up} before {{$tpl_val}} after {/filter}", strtoupper(" before {$val} after ")); } } \ No newline at end of file diff --git a/tests/cases/Fenom/TemplateTest.php b/tests/cases/Fenom/TemplateTest.php index 738e888..683faf8 100644 --- a/tests/cases/Fenom/TemplateTest.php +++ b/tests/cases/Fenom/TemplateTest.php @@ -9,14 +9,17 @@ use Fenom\Template, * * @package Fenom */ -class TemplateTest extends TestCase { +class TemplateTest extends TestCase +{ - public function setUp() { + public function setUp() + { parent::setUp(); $this->tpl('welcome.tpl', 'Welcome, {$username} ({$email})'); } - public static function providerVars() { + public static function providerVars() + { $a = array("a" => "World"); $obj = new \stdClass; $obj->name = "Object"; @@ -25,70 +28,72 @@ class TemplateTest extends TestCase { $b = array("b" => array("c" => "Username", "c_char" => "c", "mcp" => "Master", 'm{$c}p' => "Unknown", 'obj' => $obj), "c" => "c"); $c = array_replace_recursive($b, array("b" => array(3 => $b["b"], 4 => "Mister"))); return array( - array('hello, {$a}!', $a, 'hello, World!'), - array('hello, {$b.c}!', $b, 'hello, Username!'), - array('hello, {$b."c"}!', $b, 'hello, Username!'), - array('hello, {$b.\'c\'}!', $b, 'hello, Username!'), - array('hello, {$b[c]}!', $b, 'hello, Username!'), - array('hello, {$b["c"]}!', $b, 'hello, Username!'), - array('hello, {$b[\'c\']}!', $b, 'hello, Username!'), - array('hello, {$b[ $b.c_char ]}!', $b, 'hello, Username!'), - array('hello, {$b[ "$c" ]}!', $b, 'hello, Username!'), - array('hello, {$b.$c}!', $b, 'hello, Username!'), - array('hello, {$b."$c"}!', $b, 'hello, Username!'), - array('hello, {$b."{$c}"}!', $b, 'hello, Username!'), - array('hello, {$b[ "{$c}" ]}!', $b, 'hello, Username!'), - array('hello, {$b[ "mcp" ]}!', $b, 'hello, Master!'), - array('hello, {$b[ "m{$c}p" ]}!', $b, 'hello, Master!'), - array('hello, {$b."m{$c}p"}!', $b, 'hello, Master!'), + array('hello, {$a}!', $a, 'hello, World!'), + array('hello, {$b.c}!', $b, 'hello, Username!'), + array('hello, {$b."c"}!', $b, 'hello, Username!'), + array('hello, {$b.\'c\'}!', $b, 'hello, Username!'), + array('hello, {$b[c]}!', $b, 'hello, Username!'), + array('hello, {$b["c"]}!', $b, 'hello, Username!'), + array('hello, {$b[\'c\']}!', $b, 'hello, Username!'), + array('hello, {$b[ $b.c_char ]}!', $b, 'hello, Username!'), + array('hello, {$b[ "$c" ]}!', $b, 'hello, Username!'), + array('hello, {$b.$c}!', $b, 'hello, Username!'), + array('hello, {$b."$c"}!', $b, 'hello, Username!'), + array('hello, {$b."{$c}"}!', $b, 'hello, Username!'), + array('hello, {$b[ "{$c}" ]}!', $b, 'hello, Username!'), + array('hello, {$b[ "mcp" ]}!', $b, 'hello, Master!'), + array('hello, {$b[ "m{$c}p" ]}!', $b, 'hello, Master!'), + array('hello, {$b."m{$c}p"}!', $b, 'hello, Master!'), array('hello, {$b[ "m{$b.c_char}p" ]}!', - $b, 'hello, Master!'), + $b, 'hello, Master!'), array('hello, {$b[ \'m{$c}p\' ]}!', $b, 'hello, Unknown!'), - array('hello, {$b.4}!', $c, 'hello, Mister!'), - array('hello, {$b[4]}!', $c, 'hello, Mister!'), - array('hello, {$b.3.c}!', $c, 'hello, Username!'), - array('hello, {$b.3.$c}!', $c, 'hello, Username!'), - array('hello, {$b.3[$b.c_char]}!', $c, 'hello, Username!'), - array('hello, {$b[3].c}!', $c, 'hello, Username!'), - array('hello, {$b[2+1].c}!', $c, 'hello, Username!'), - array('hello, {$b[9/3].c}!', $c, 'hello, Username!'), - array('hello, {$b[3].$c}!', $c, 'hello, Username!'), + array('hello, {$b.4}!', $c, 'hello, Mister!'), + array('hello, {$b[4]}!', $c, 'hello, Mister!'), + array('hello, {$b.3.c}!', $c, 'hello, Username!'), + array('hello, {$b.3.$c}!', $c, 'hello, Username!'), + array('hello, {$b.3[$b.c_char]}!', $c, 'hello, Username!'), + array('hello, {$b[3].c}!', $c, 'hello, Username!'), + array('hello, {$b[2+1].c}!', $c, 'hello, Username!'), + array('hello, {$b[9/3].c}!', $c, 'hello, Username!'), + array('hello, {$b[3].$c}!', $c, 'hello, Username!'), array('hello, {$b[3][$b.c_char]}!', $c, 'hello, Username!'), array('hello, {$b[ "m{$b.c_char}p" ]} and {$b.3[$b.c_char]}!', - $c, 'hello, Master and Username!'), - array('hello, {$b.obj->name}!', $c, 'hello, Object!'), - array('hello, {$b.obj->list.a}!', $c, 'hello, World!'), - array('hello, {$b[obj]->name}!', $c, 'hello, Object!'), - array('hello, {$b["obj"]->name}!', $c, 'hello, Object!'), + $c, 'hello, Master and Username!'), + array('hello, {$b.obj->name}!', $c, 'hello, Object!'), + array('hello, {$b.obj->list.a}!', $c, 'hello, World!'), + array('hello, {$b[obj]->name}!', $c, 'hello, Object!'), + array('hello, {$b["obj"]->name}!', $c, 'hello, Object!'), - array('hello, {$b."obj"->name}!', $c, 'hello, Object!'), + array('hello, {$b."obj"->name}!', $c, 'hello, Object!'), array('hello, {$b.obj->name|upper}!', - $c, 'hello, OBJECT!'), + $c, 'hello, OBJECT!'), array('hello, {$b.obj->list.a|upper}!', - $c, 'hello, WORLD!'), - array('hello, {$b[ $b.obj->c ]}!', $b, 'hello, Username!'), + $c, 'hello, WORLD!'), + array('hello, {$b[ $b.obj->c ]}!', $b, 'hello, Username!'), array('hello, {$b[ "{$b.obj->c}" ]}!', - $b, 'hello, Username!'), - array('hello, {"World"}!', $a, 'hello, World!'), - array('hello, {"W{$a}d"}!', $a, 'hello, WWorldd!'), + $b, 'hello, Username!'), + array('hello, {"World"}!', $a, 'hello, World!'), + array('hello, {"W{$a}d"}!', $a, 'hello, WWorldd!'), ); } - public static function providerVarsInvalid() { + public static function providerVarsInvalid() + { return array( - array('hello, {$a.}!', 'Fenom\CompileException', "Unexpected end of expression"), - array('hello, {$b[c}!', 'Fenom\CompileException', "Unexpected end of expression"), - array('hello, {$b.c]}!', 'Fenom\CompileException', "Unexpected token ']'"), - array('hello, {$b[ ]}!', 'Fenom\CompileException', "Unexpected token ']'"), - array('hello, {$b[9/].c}!', 'Fenom\CompileException', "Unexpected token ']'"), - array('hello, {$b[3]$c}!', 'Fenom\CompileException', "Unexpected token '\$c'"), - array('hello, {$b[3]c}!', 'Fenom\CompileException', "Unexpected token 'c'"), + array('hello, {$a.}!', 'Fenom\CompileException', "Unexpected end of expression"), + array('hello, {$b[c}!', 'Fenom\CompileException', "Unexpected end of expression"), + array('hello, {$b.c]}!', 'Fenom\CompileException', "Unexpected token ']'"), + array('hello, {$b[ ]}!', 'Fenom\CompileException', "Unexpected token ']'"), + array('hello, {$b[9/].c}!', 'Fenom\CompileException', "Unexpected token ']'"), + array('hello, {$b[3]$c}!', 'Fenom\CompileException', "Unexpected token '\$c'"), + array('hello, {$b[3]c}!', 'Fenom\CompileException', "Unexpected token 'c'"), array('hello, {$b.obj->valid()}!', 'Fenom\SecurityException', "Forbidden to call methods", Fenom::DENY_METHODS), ); } - public static function providerModifiers() { + public static function providerModifiers() + { $b = array( "a" => "World", "b" => array( @@ -97,48 +102,50 @@ class TemplateTest extends TestCase { "c" => "c", "lorem" => "Lorem ipsum dolor sit amet", "next" => " next -->", - "rescue" => "Chip & Dale", - "rescue_html" => "Chip & Dale", - "rescue_url" => "Chip+%26+Dale", - "date" => "26-07-2012", - "time" => 1343323616, - "tags" => "my name is Legion" + "rescue" => "Chip & Dale", + "rescue_html" => "Chip & Dale", + "rescue_url" => "Chip+%26+Dale", + "date" => "26-07-2012", + "time" => 1343323616, + "tags" => "my name is Legion" ); return array( - array('hello, {$a|upper}!', $b, 'hello, WORLD!'), - array('hello, {$b.c|upper}!', $b, 'hello, USERNAME!'), - array('hello, {$b."c"|upper}!', $b, 'hello, USERNAME!'), - array('hello, {$b["C"|lower]|upper}!', $b, 'hello, USERNAME!'), - array('Mod: {$rescue|escape}!', $b, 'Mod: Chip & Dale!'), - array('Mod: {$rescue|escape:"html"}!', $b, 'Mod: Chip & Dale!'), - array('Mod: {$rescue|escape:"url"}!', $b, 'Mod: Chip+%26+Dale!'), - array('Mod: {$rescue|escape:"unknown"}!', $b, 'Mod: Chip & Dale!'), - array('Mod: {$rescue_html|unescape}!', $b, 'Mod: Chip & Dale!'), - array('Mod: {$rescue_html|unescape:"html"}!', $b, 'Mod: Chip & Dale!'), - array('Mod: {$rescue_url|unescape:"url"}!', $b, 'Mod: Chip & Dale!'), - array('Mod: {$rescue|unescape:"unknown"}!', $b, 'Mod: Chip & Dale!'), - array('Mod: {$time|date_format:"%Y %m %d"}!', $b, 'Mod: 2012 07 26!'), - array('Mod: {$date|date_format:"%Y %m %d"}!', $b, 'Mod: 2012 07 26!'), - array('Mod: {$time|date:"Y m d"}!', $b, 'Mod: 2012 07 26!'), - array('Mod: {$date|date:"Y m d"}!', $b, 'Mod: 2012 07 26!'), - array('Mod: {$tags|strip_tags}!', $b, 'Mod: my name is Legion!'), - array('Mod: {$b.c|json_encode}!', $b, 'Mod: "Username"!'), - array('Mod: {time()|date:"Y m d"}!', $b, 'Mod: '.date("Y m d").'!'), + array('hello, {$a|upper}!', $b, 'hello, WORLD!'), + array('hello, {$b.c|upper}!', $b, 'hello, USERNAME!'), + array('hello, {$b."c"|upper}!', $b, 'hello, USERNAME!'), + array('hello, {$b["C"|lower]|upper}!', $b, 'hello, USERNAME!'), + array('Mod: {$rescue|escape}!', $b, 'Mod: Chip & Dale!'), + array('Mod: {$rescue|escape:"html"}!', $b, 'Mod: Chip & Dale!'), + array('Mod: {$rescue|escape:"url"}!', $b, 'Mod: Chip+%26+Dale!'), + array('Mod: {$rescue|escape:"unknown"}!', $b, 'Mod: Chip & Dale!'), + array('Mod: {$rescue_html|unescape}!', $b, 'Mod: Chip & Dale!'), + array('Mod: {$rescue_html|unescape:"html"}!', $b, 'Mod: Chip & Dale!'), + array('Mod: {$rescue_url|unescape:"url"}!', $b, 'Mod: Chip & Dale!'), + array('Mod: {$rescue|unescape:"unknown"}!', $b, 'Mod: Chip & Dale!'), + array('Mod: {$time|date_format:"%Y %m %d"}!', $b, 'Mod: 2012 07 26!'), + array('Mod: {$date|date_format:"%Y %m %d"}!', $b, 'Mod: 2012 07 26!'), + array('Mod: {$time|date:"Y m d"}!', $b, 'Mod: 2012 07 26!'), + array('Mod: {$date|date:"Y m d"}!', $b, 'Mod: 2012 07 26!'), + array('Mod: {$tags|strip_tags}!', $b, 'Mod: my name is Legion!'), + array('Mod: {$b.c|json_encode}!', $b, 'Mod: "Username"!'), + array('Mod: {time()|date:"Y m d"}!', $b, 'Mod: ' . date("Y m d") . '!'), ); } - public static function providerModifiersInvalid() { + public static function providerModifiersInvalid() + { return array( - array('Mod: {$lorem|}!', 'Fenom\CompileException', "Unexpected end of expression"), - array('Mod: {$lorem|str_rot13}!', 'Fenom\CompileException', "Modifier str_rot13 not found", Fenom::DENY_INLINE_FUNCS), - array('Mod: {$lorem|my_encode}!', 'Fenom\CompileException', "Modifier my_encode not found"), - array('Mod: {$lorem|truncate:}!', 'Fenom\CompileException', "Unexpected end of expression"), - array('Mod: {$lorem|truncate:abs}!', 'Fenom\CompileException', "Unexpected token 'abs'"), - array('Mod: {$lorem|truncate:80|}!', 'Fenom\CompileException', "Unexpected end of expression"), + array('Mod: {$lorem|}!', 'Fenom\CompileException', "Unexpected end of expression"), + array('Mod: {$lorem|str_rot13}!', 'Fenom\CompileException', "Modifier str_rot13 not found", Fenom::DENY_INLINE_FUNCS), + array('Mod: {$lorem|my_encode}!', 'Fenom\CompileException', "Modifier my_encode not found"), + array('Mod: {$lorem|truncate:}!', 'Fenom\CompileException', "Unexpected end of expression"), + array('Mod: {$lorem|truncate:abs}!', 'Fenom\CompileException', "Unexpected token 'abs'"), + array('Mod: {$lorem|truncate:80|}!', 'Fenom\CompileException', "Unexpected end of expression"), ); } - public static function providerExpressions() { + public static function providerExpressions() + { $b = array( "x" => $x = 9, "y" => 27, @@ -146,54 +153,56 @@ class TemplateTest extends TestCase { "k" => array("i" => "") ); return array( - array('Exp: {'.$x.'+$y} result', $b, 'Exp: 36 result'), - array('Exp: {$y/'.$x.'} result', $b, 'Exp: 3 result'), - array('Exp: {$y-'.$x.'} result', $b, 'Exp: 18 result'), - array('Exp: {'.$x.'*$y} result', $b, 'Exp: 243 result'), - array('Exp: {$y^'.$x.'} result', $b, 'Exp: 18 result'), + array('Exp: {' . $x . '+$y} result', $b, 'Exp: 36 result'), + array('Exp: {$y/' . $x . '} result', $b, 'Exp: 3 result'), + array('Exp: {$y-' . $x . '} result', $b, 'Exp: 18 result'), + array('Exp: {' . $x . '*$y} result', $b, 'Exp: 243 result'), + array('Exp: {$y^' . $x . '} result', $b, 'Exp: 18 result'), - array('Exp: {$x+$y} result', $b, 'Exp: 36 result'), - array('Exp: {$y/$x} result', $b, 'Exp: 3 result'), - array('Exp: {$y-$x} result', $b, 'Exp: 18 result'), - array('Exp: {$y*$x} result', $b, 'Exp: 243 result'), - array('Exp: {$y^$x} result', $b, 'Exp: 18 result'), - array('Exp: {-($x)} result', $b, 'Exp: -9 result'), - array('Exp: {!$x} result', $b, 'Exp: result'), - array('Exp: {!($x)} result', $b, 'Exp: result'), - array('Exp: {!5} result', $b, 'Exp: result'), - array('Exp: {-1} result', $b, 'Exp: -1 result'), - array('Exp: {$z = 5} {$z} result', $b, 'Exp: 5 5 result'), - array('Exp: {$k.i = "str"} {$k.i} result', $b, 'Exp: str str result'), + array('Exp: {$x+$y} result', $b, 'Exp: 36 result'), + array('Exp: {$y/$x} result', $b, 'Exp: 3 result'), + array('Exp: {$y-$x} result', $b, 'Exp: 18 result'), + array('Exp: {$y*$x} result', $b, 'Exp: 243 result'), + array('Exp: {$y^$x} result', $b, 'Exp: 18 result'), + array('Exp: {-($x)} result', $b, 'Exp: -9 result'), + array('Exp: {!$x} result', $b, 'Exp: result'), + array('Exp: {!($x)} result', $b, 'Exp: result'), + array('Exp: {!5} result', $b, 'Exp: result'), + array('Exp: {-1} result', $b, 'Exp: -1 result'), + array('Exp: {$z = 5} {$z} result', $b, 'Exp: 5 5 result'), + array('Exp: {$k.i = "str"} {$k.i} result', $b, 'Exp: str str result'), array('Exp: {($y*$x - (($x+$y) + $y/$x) ^ $y)/4} result', - $b, 'Exp: 53.75 result'), - array('Exp: {$x+max($x, $y)} result', $b, 'Exp: 36 result'), - array('Exp: {max(1,2)} result', $b, 'Exp: 2 result'), - array('Exp: {round(sin(pi()), 8)} result', $b, 'Exp: 0 result'), + $b, 'Exp: 53.75 result'), + array('Exp: {$x+max($x, $y)} result', $b, 'Exp: 36 result'), + array('Exp: {max(1,2)} result', $b, 'Exp: 2 result'), + array('Exp: {round(sin(pi()), 8)} result', $b, 'Exp: 0 result'), array('Exp: {max($x, $y) + round(sin(pi()), 8) - min($x, $y) +3} result', - $b, 'Exp: 21 result'), + $b, 'Exp: 21 result'), ); } - public static function providerExpressionsInvalid() { + public static function providerExpressionsInvalid() + { return array( - array('If: {-"hi"} end', 'Fenom\CompileException', "Unexpected token '-'"), - array('If: {($a++)++} end', 'Fenom\CompileException', "Unexpected token '++'"), - array('If: {$a + * $c} end', 'Fenom\CompileException', "Unexpected token '*'"), - array('If: {$a + } end', 'Fenom\CompileException', "Unexpected token '+'"), - array('If: {$a + =} end', 'Fenom\CompileException', "Unexpected token '='"), - array('If: {$a + 1 =} end', 'Fenom\CompileException', "Unexpected token '='"), - array('If: {$a + 1 = 6} end', 'Fenom\CompileException', "Unexpected token '='"), - array('If: {/$a} end', 'Fenom\CompileException', "Unexpected token '\$a'"), - array('If: {$a == 5 > 4} end', 'Fenom\CompileException', "Unexpected token '>'"), - array('If: {$a != 5 <= 4} end', 'Fenom\CompileException', "Unexpected token '<='"), - array('If: {$a != 5 => 4} end', 'Fenom\CompileException', "Unexpected token '=>'"), - array('If: {$a + (*6)} end', 'Fenom\CompileException', "Unexpected token '*'"), - array('If: {$a + ( 6} end', 'Fenom\CompileException', "Unexpected end of expression, expect ')'"), + array('If: {-"hi"} end', 'Fenom\CompileException', "Unexpected token '-'"), + array('If: {($a++)++} end', 'Fenom\CompileException', "Unexpected token '++'"), + array('If: {$a + * $c} end', 'Fenom\CompileException', "Unexpected token '*'"), + array('If: {$a + } end', 'Fenom\CompileException', "Unexpected token '+'"), + array('If: {$a + =} end', 'Fenom\CompileException', "Unexpected token '='"), + array('If: {$a + 1 =} end', 'Fenom\CompileException', "Unexpected token '='"), + array('If: {$a + 1 = 6} end', 'Fenom\CompileException', "Unexpected token '='"), + array('If: {/$a} end', 'Fenom\CompileException', "Unexpected token '\$a'"), + array('If: {$a == 5 > 4} end', 'Fenom\CompileException', "Unexpected token '>'"), + array('If: {$a != 5 <= 4} end', 'Fenom\CompileException', "Unexpected token '<='"), + array('If: {$a != 5 => 4} end', 'Fenom\CompileException', "Unexpected token '=>'"), + array('If: {$a + (*6)} end', 'Fenom\CompileException', "Unexpected token '*'"), + array('If: {$a + ( 6} end', 'Fenom\CompileException', "Unexpected end of expression, expect ')'"), ); } - public static function providerInclude() { + public static function providerInclude() + { $a = array( "name" => "welcome", "tpl" => "welcome.tpl", @@ -208,30 +217,32 @@ class TemplateTest extends TestCase { $result3 = 'Include Welcome, Master (flame@dev.null) template'; $result4 = 'Include Welcome, Flame (flame@dev.null) template'; return array( - array('Include {include "welcome.tpl"} template', $a, $result), - array('Include {include $tpl} template', $a, $result), - array('Include {include "$tpl"} template', $a, $result), - array('Include {include "{$tpl}"} template', $a, $result), - array('Include {include "$name.tpl"} template', $a, $result), - array('Include {include "{$name}.tpl"} template', $a, $result), - array('Include {include "{$pr_name|lower}.tpl"} template', $a, $result), - array('Include {include "wel{$fragment}.tpl"} template', $a, $result), - array('Include {include "wel{$pr_fragment|lower}.tpl"} template', $a, $result), - array('Include {include "welcome.tpl" username="Flame"} template', $a, $result2), - array('Include {include "welcome.tpl" email="flame@dev.null"} template', $a, $result3), + array('Include {include "welcome.tpl"} template', $a, $result), + array('Include {include $tpl} template', $a, $result), + array('Include {include "$tpl"} template', $a, $result), + array('Include {include "{$tpl}"} template', $a, $result), + array('Include {include "$name.tpl"} template', $a, $result), + array('Include {include "{$name}.tpl"} template', $a, $result), + array('Include {include "{$pr_name|lower}.tpl"} template', $a, $result), + array('Include {include "wel{$fragment}.tpl"} template', $a, $result), + array('Include {include "wel{$pr_fragment|lower}.tpl"} template', $a, $result), + array('Include {include "welcome.tpl" username="Flame"} template', $a, $result2), + array('Include {include "welcome.tpl" email="flame@dev.null"} template', $a, $result3), array('Include {include "welcome.tpl" username="Flame" email="flame@dev.null"} template', - $a, $result4), + $a, $result4), ); } - public static function providerIncludeInvalid() { + public static function providerIncludeInvalid() + { return array( - array('Include {include} template', 'Fenom\CompileException', "Unexpected end of expression"), - array('Include {include another="welcome.tpl"} template', 'Fenom\CompileException', "Unexpected token '='"), + array('Include {include} template', 'Fenom\CompileException', "Unexpected end of expression"), + array('Include {include another="welcome.tpl"} template', 'Fenom\CompileException', "Unexpected token '='"), ); } - public static function providerIf() { + public static function providerIf() + { $a = array( "val1" => 1, "val0" => 0, @@ -239,97 +250,101 @@ class TemplateTest extends TestCase { "y" => 27 ); return array( - array('if: {if 1} block1 {/if} end', $a, 'if: block1 end'), - array('if: {if 1} block1 {else} block2 {/if} end', $a, 'if: block1 end'), - array('if: {if 0} block1 {/if} end', $a, 'if: end'), - array('if: {if $val0} block1 {else} block2 {/if} end', $a, 'if: block2 end'), - array('if: {if $val1} block1 {else} block2 {/if} end', $a, 'if: block1 end'), - array('if: {if $val1 || $val0} block1 {else} block2 {/if} end', $a, 'if: block1 end'), - array('if: {if $val1 && $val0} block1 {else} block2 {/if} end', $a, 'if: block2 end'), + array('if: {if 1} block1 {/if} end', $a, 'if: block1 end'), + array('if: {if 1} block1 {else} block2 {/if} end', $a, 'if: block1 end'), + array('if: {if 0} block1 {/if} end', $a, 'if: end'), + array('if: {if $val0} block1 {else} block2 {/if} end', $a, 'if: block2 end'), + array('if: {if $val1} block1 {else} block2 {/if} end', $a, 'if: block1 end'), + array('if: {if $val1 || $val0} block1 {else} block2 {/if} end', $a, 'if: block1 end'), + array('if: {if $val1 && $val0} block1 {else} block2 {/if} end', $a, 'if: block2 end'), array('if: {if $x-9} block1 {elseif $x} block2 {else} block3 {/if} end', - $a, 'if: block2 end'), + $a, 'if: block2 end'), array('if: {if round(sin(pi()), 8)} block1 {elseif $x} block2 {else} block3 {/if} end', - $a, 'if: block2 end'), + $a, 'if: block2 end'), array('if: {if round(sin(pi()), 8)} block1 {elseif $val0} block2 {else} block3 {/if} end', - $a, 'if: block3 end'), - array('if: {if empty($val0)} block1 {else} block2 {/if} end', $a, 'if: block1 end'), - array('if: {if $val0?} block1 {else} block2 {/if} end', $a, 'if: block2 end'), - array('if: {if $val1?} block1 {else} block2 {/if} end', $a, 'if: block1 end'), - array('if: {if $val0!} block1 {else} block2 {/if} end', $a, 'if: block1 end'), - array('if: {if $val1!} block1 {else} block2 {/if} end', $a, 'if: block1 end'), - array('if: {if $val0.x.y.z?} block1 {else} block2 {/if} end', $a, 'if: block2 end'), - array('if: {if $val0.x.y.z!} block1 {else} block2 {/if} end', $a, 'if: block2 end'), - array('if: {if true} block1 {else} block2 {/if} end', $a, 'if: block1 end'), - array('if: {if false} block1 {else} block2 {/if} end', $a, 'if: block2 end'), - array('if: {if null} block1 {else} block2 {/if} end', $a, 'if: block2 end'), + $a, 'if: block3 end'), + array('if: {if empty($val0)} block1 {else} block2 {/if} end', $a, 'if: block1 end'), + array('if: {if $val0?} block1 {else} block2 {/if} end', $a, 'if: block2 end'), + array('if: {if $val1?} block1 {else} block2 {/if} end', $a, 'if: block1 end'), + array('if: {if $val0!} block1 {else} block2 {/if} end', $a, 'if: block1 end'), + array('if: {if $val1!} block1 {else} block2 {/if} end', $a, 'if: block1 end'), + array('if: {if $val0.x.y.z?} block1 {else} block2 {/if} end', $a, 'if: block2 end'), + array('if: {if $val0.x.y.z!} block1 {else} block2 {/if} end', $a, 'if: block2 end'), + array('if: {if true} block1 {else} block2 {/if} end', $a, 'if: block1 end'), + array('if: {if false} block1 {else} block2 {/if} end', $a, 'if: block2 end'), + array('if: {if null} block1 {else} block2 {/if} end', $a, 'if: block2 end'), array('if: {if ($val1 || $val0) && $x} block1 {else} block2 {/if} end', - $a, 'if: block1 end'), - array('if: {if $unexist} block1 {else} block2 {/if} end', $a, 'if: block2 end', Fenom::FORCE_VERIFY), + $a, 'if: block1 end'), + array('if: {if $unexist} block1 {else} block2 {/if} end', $a, 'if: block2 end', Fenom::FORCE_VERIFY), ); } - public static function providerIfInvalid() { + public static function providerIfInvalid() + { return array( - array('If: {if} block1 {/if} end', 'Fenom\CompileException', "Unexpected end of expression"), - array('If: {if 1} block1 {elseif} block2 {/if} end', 'Fenom\CompileException', "Unexpected end of expression"), + array('If: {if} block1 {/if} end', 'Fenom\CompileException', "Unexpected end of expression"), + array('If: {if 1} block1 {elseif} block2 {/if} end', 'Fenom\CompileException', "Unexpected end of expression"), array('If: {if 1} block1 {else} block2 {elseif 0} block3 {/if} end', 'Fenom\CompileException', "Incorrect use of the tag {elseif}"), array('If: {if 1} block1 {else} block2 {/if} block3 {elseif 0} end', 'Fenom\CompileException', "Unexpected tag 'elseif' (this tag can be used with 'if')"), ); } - public static function providerCreateVar() { + public static function providerCreateVar() + { $a = array( "x" => 9, "y" => 27, "z" => 99 ); return array( - array('Create: {var $v = $x+$y} Result: {$v} end', $a, 'Create: Result: 36 end'), + array('Create: {var $v = $x+$y} Result: {$v} end', $a, 'Create: Result: 36 end'), array('Create: {var $v = $x + - $y} Result: {$v} end', $a, 'Create: Result: 36 end'), - array('Create: {var $v = $z++} Result: {$v}, {$z} end', $a, 'Create: Result: 99, 100 end'), + $y} Result: {$v} end', $a, 'Create: Result: 36 end'), + array('Create: {var $v = $z++} Result: {$v}, {$z} end', $a, 'Create: Result: 99, 100 end'), array('Create: {var $v = $z++ + 1} Result: {$v}, {$z} end', $a, 'Create: Result: 100, 100 end'), - array('Create: {var $v = --$z} Result: {$v}, {$z} end', $a, 'Create: Result: 98, 98 end'), - array('Create: {var $v = $y/$x} Result: {$v} end', $a, 'Create: Result: 3 end'), - array('Create: {var $v = $y-$x} Result: {$v} end', $a, 'Create: Result: 18 end'), - array('Create: {var $v = $y*$x-2} Result: {$v} end', $a, 'Create: Result: 241 end'), - array('Create: {var $v = ($y^$x)+7} Result: {$v} end', $a, 'Create: Result: 25 end'), + array('Create: {var $v = --$z} Result: {$v}, {$z} end', $a, 'Create: Result: 98, 98 end'), + array('Create: {var $v = $y/$x} Result: {$v} end', $a, 'Create: Result: 3 end'), + array('Create: {var $v = $y-$x} Result: {$v} end', $a, 'Create: Result: 18 end'), + array('Create: {var $v = $y*$x-2} Result: {$v} end', $a, 'Create: Result: 241 end'), + array('Create: {var $v = ($y^$x)+7} Result: {$v} end', $a, 'Create: Result: 25 end'), - array('Create: {var $v = [1,2,3]} Result: {$v.1} end', $a, 'Create: Result: 2 end'), + array('Create: {var $v = [1,2,3]} Result: {$v.1} end', $a, 'Create: Result: 2 end'), array('Create: {var $v = []} Result: {if $v} have items {else} empty {/if} end', - $a, 'Create: Result: empty end'), + $a, 'Create: Result: empty end'), array('Create: {var $v = ["one"|upper => 1, 4 => $x, "three" => 3]} Result: {$v.three}, {$v.4}, {$v.ONE} end', - $a, 'Create: Result: 3, 9, 1 end'), + $a, 'Create: Result: 3, 9, 1 end'), array('Create: {var $v = ["key1" => $y*$x-2, "key2" => ["z" => $z]]} Result: {$v.key1}, {$v.key2.z} end', - $a, 'Create: Result: 241, 99 end'), + $a, 'Create: Result: 241, 99 end'), array('Create: {var $v = count([1,2,3])+7} Result: {$v} end', - $a, 'Create: Result: 10 end'), + $a, 'Create: Result: 10 end'), ); } - public static function providerCreateVarInvalid() { + public static function providerCreateVarInvalid() + { return array( - array('Create: {var $v} Result: {$v} end', 'Fenom\CompileException', "Unclosed tag: {var} opened"), - array('Create: {var $v = } Result: {$v} end', 'Fenom\CompileException', "Unexpected end of expression"), - array('Create: {var $v = 1++} Result: {$v} end', 'Fenom\CompileException', "Unexpected token '++'"), - array('Create: {var $v = c} Result: {$v} end', 'Fenom\CompileException', "Unexpected token 'c'"), - array('Create: {var $v = ($a)++} Result: {$v} end', 'Fenom\CompileException', "Unexpected token '++'"), - array('Create: {var $v = --$a++} Result: {$v} end', 'Fenom\CompileException', "Can not use two increments and/or decrements for one variable"), - array('Create: {var $v = $a|upper++} Result: {$v} end', 'Fenom\CompileException', "Unexpected token '++'"), + array('Create: {var $v} Result: {$v} end', 'Fenom\CompileException', "Unclosed tag: {var} opened"), + array('Create: {var $v = } Result: {$v} end', 'Fenom\CompileException', "Unexpected end of expression"), + array('Create: {var $v = 1++} Result: {$v} end', 'Fenom\CompileException', "Unexpected token '++'"), + array('Create: {var $v = c} Result: {$v} end', 'Fenom\CompileException', "Unexpected token 'c'"), + array('Create: {var $v = ($a)++} Result: {$v} end', 'Fenom\CompileException', "Unexpected token '++'"), + array('Create: {var $v = --$a++} Result: {$v} end', 'Fenom\CompileException', "Can not use two increments and/or decrements for one variable"), + array('Create: {var $v = $a|upper++} Result: {$v} end', 'Fenom\CompileException', "Unexpected token '++'"), array('Create: {var $v = max($a,2)++} Result: {$v} end', 'Fenom\CompileException', "Unexpected token '++'"), - array('Create: {var $v = max($a,2)} Result: {$v} end', 'Fenom\CompileException', "Function max not found", Fenom::DENY_NATIVE_FUNCS), - array('Create: {var $v = 4*} Result: {$v} end', 'Fenom\CompileException', "Unexpected token '*'"), - array('Create: {var $v = ""$a} Result: {$v} end', 'Fenom\CompileException', "Unexpected token '\$a'"), - array('Create: {var $v = [1,2} Result: {$v} end', 'Fenom\CompileException', "Unexpected end of expression"), - array('Create: {var $v = empty(2)} Result: {$v} end', 'Fenom\CompileException', "Unexpected token 2, isset() and empty() accept only variables"), - array('Create: {var $v = isset(2)} Result: {$v} end', 'Fenom\CompileException', "Unexpected token 2, isset() and empty() accept only variables"), + array('Create: {var $v = max($a,2)} Result: {$v} end', 'Fenom\CompileException', "Function max not found", Fenom::DENY_NATIVE_FUNCS), + array('Create: {var $v = 4*} Result: {$v} end', 'Fenom\CompileException', "Unexpected token '*'"), + array('Create: {var $v = ""$a} Result: {$v} end', 'Fenom\CompileException', "Unexpected token '\$a'"), + array('Create: {var $v = [1,2} Result: {$v} end', 'Fenom\CompileException', "Unexpected end of expression"), + array('Create: {var $v = empty(2)} Result: {$v} end', 'Fenom\CompileException', "Unexpected token 2, isset() and empty() accept only variables"), + array('Create: {var $v = isset(2)} Result: {$v} end', 'Fenom\CompileException', "Unexpected token 2, isset() and empty() accept only variables"), ); } - public static function providerTernary() { + public static function providerTernary() + { $a = array( "a" => 1, "em" => "empty", @@ -341,7 +356,7 @@ class TemplateTest extends TestCase { "bool" => false, ), "nonempty" => array( - "array" => array(1,2), + "array" => array(1, 2), "int" => 2, "string" => "abc", "double" => 0.2, @@ -350,49 +365,50 @@ class TemplateTest extends TestCase { ); return array( // ? - array('{if $a?} right {/if}', $a), - array('{if $unexists?} no way {else} right {/if}', $a), - array('{if $empty.array?} no way {else} right {/if}', $a), - array('{if $empty.int?} no way {else} right {/if}', $a), - array('{if $empty.string?} no way {else} right {/if}', $a), - array('{if $empty.double?} no way {else} right {/if}', $a), - array('{if $empty.bool?} no way {else} right {/if}', $a), + array('{if $a?} right {/if}', $a), + array('{if $unexists?} no way {else} right {/if}', $a), + array('{if $empty.array?} no way {else} right {/if}', $a), + array('{if $empty.int?} no way {else} right {/if}', $a), + array('{if $empty.string?} no way {else} right {/if}', $a), + array('{if $empty.double?} no way {else} right {/if}', $a), + array('{if $empty.bool?} no way {else} right {/if}', $a), array('{if $empty.unexist?} no way {else} right {/if}', $a), - array('{if $nonempty.array?} right {/if}', $a), - array('{if $nonempty.int?} right {/if}', $a), - array('{if $nonempty.string?} right {/if}', $a), - array('{if $nonempty.double?} right {/if}', $a), - array('{if $nonempty.bool?} right {/if}', $a), + array('{if $nonempty.array?} right {/if}', $a), + array('{if $nonempty.int?} right {/if}', $a), + array('{if $nonempty.string?} right {/if}', $a), + array('{if $nonempty.double?} right {/if}', $a), + array('{if $nonempty.bool?} right {/if}', $a), // ?: ... - array('{$a?:"empty"}', $a, "1"), - array('{$unexists?:"empty"}', $a, "empty"), - array('{$empty.array?:"empty"}', $a, "empty"), - array('{$empty.int?:"empty"}', $a, "empty"), - array('{$empty.string?:"empty"}', $a, "empty"), - array('{$empty.double?:"empty"}', $a, "empty"), - array('{$empty.bool?:"empty"}', $a, "empty"), - array('{$empty.unexist?:"empty"}', $a, "empty"), + array('{$a?:"empty"}', $a, "1"), + array('{$unexists?:"empty"}', $a, "empty"), + array('{$empty.array?:"empty"}', $a, "empty"), + array('{$empty.int?:"empty"}', $a, "empty"), + array('{$empty.string?:"empty"}', $a, "empty"), + array('{$empty.double?:"empty"}', $a, "empty"), + array('{$empty.bool?:"empty"}', $a, "empty"), + array('{$empty.unexist?:"empty"}', $a, "empty"), // ? ... : .... // ! - array('{if $a!} right {/if}', $a), - array('{if $unexists!} no way {else} right {/if}', $a), - array('{if $empty.array!} right {/if}', $a), - array('{if $empty.int!} right {/if}', $a), - array('{if $empty.string!} right {/if}', $a), - array('{if $empty.double!} right {/if}', $a), - array('{if $empty.bool!} right {/if}', $a), + array('{if $a!} right {/if}', $a), + array('{if $unexists!} no way {else} right {/if}', $a), + array('{if $empty.array!} right {/if}', $a), + array('{if $empty.int!} right {/if}', $a), + array('{if $empty.string!} right {/if}', $a), + array('{if $empty.double!} right {/if}', $a), + array('{if $empty.bool!} right {/if}', $a), array('{if $empty.unexist!} no way {else} right {/if}', $a), - array('{if $nonempty.array!} right {/if}', $a), - array('{if $nonempty.int!} right {/if}', $a), - array('{if $nonempty.string!} right {/if}', $a), - array('{if $nonempty.double!} right {/if}', $a), - array('{if $nonempty.bool!} right {/if}', $a), + array('{if $nonempty.array!} right {/if}', $a), + array('{if $nonempty.int!} right {/if}', $a), + array('{if $nonempty.string!} right {/if}', $a), + array('{if $nonempty.double!} right {/if}', $a), + array('{if $nonempty.bool!} right {/if}', $a), // ! ... : ... // !: ... ); } - public static function providerForeach() { + public static function providerForeach() + { $a = array( "list" => array(1 => "one", 2 => "two", 3 => "three"), "empty" => array() @@ -417,46 +433,49 @@ class TemplateTest extends TestCase { ); } - public static function providerForeachInvalid() { + public static function providerForeachInvalid() + { return array( - array('Foreach: {foreach} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected end of tag {foreach}"), - array('Foreach: {foreach $list} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected end of expression"), - array('Foreach: {foreach $list+1 as $e} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected token '+'"), + array('Foreach: {foreach} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected end of tag {foreach}"), + array('Foreach: {foreach $list} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected end of expression"), + array('Foreach: {foreach $list+1 as $e} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected token '+'"), array('Foreach: {foreach array_random() as $e} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected token 'array_random'"), - array('Foreach: {foreach $list as $e+1} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected token '+'"), - array('Foreach: {foreach $list as $k+1 => $e} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected token '+'"), - array('Foreach: {foreach $list as max($i,1) => $e} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected token 'max'"), + array('Foreach: {foreach $list as $e+1} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected token '+'"), + array('Foreach: {foreach $list as $k+1 => $e} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected token '+'"), + array('Foreach: {foreach $list as max($i,1) => $e} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected token 'max'"), array('Foreach: {foreach $list as max($e,1)} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected token 'max'"), - array('Foreach: {foreach $list => $e} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected token '=>'"), - array('Foreach: {foreach $list $k => $e} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected token '\$k'"), - array('Foreach: {foreach $list as $k =>} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected end of expression"), + array('Foreach: {foreach $list => $e} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected token '=>'"), + array('Foreach: {foreach $list $k => $e} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected token '\$k'"), + array('Foreach: {foreach $list as $k =>} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected end of expression"), array('Foreach: {foreach last=$l $list as $e } {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected token 'last' in tag {foreach}"), array('Foreach: {foreach $list as $e unknown=1} {$e}, {/foreach} end', 'Fenom\CompileException', "Unknown parameter 'unknown'"), - array('Foreach: {foreach $list as $e index=$i+1} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected token '+'"), - array('Foreach: {foreach $list as $e first=$f+1} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected token '+'"), - array('Foreach: {foreach $list as $e last=$l+1} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected token '+'"), - array('Foreach: {foreach $list as $e index=max($i,1)} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected token 'max'"), - array('Foreach: {foreach $list as $e first=max($i,1)} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected token 'max'"), - array('Foreach: {foreach $list as $e last=max($i,1)} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected token 'max'"), - array('Foreach: {foreach $list as $e} {$e}, {foreachelse} {break} {/foreach} end', 'Fenom\CompileException', "Improper usage of the tag {break}"), - array('Foreach: {foreach $list as $e} {$e}, {foreachelse} {continue} {/foreach} end', 'Fenom\CompileException', "Improper usage of the tag {continue}"), + array('Foreach: {foreach $list as $e index=$i+1} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected token '+'"), + array('Foreach: {foreach $list as $e first=$f+1} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected token '+'"), + array('Foreach: {foreach $list as $e last=$l+1} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected token '+'"), + array('Foreach: {foreach $list as $e index=max($i,1)} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected token 'max'"), + array('Foreach: {foreach $list as $e first=max($i,1)} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected token 'max'"), + array('Foreach: {foreach $list as $e last=max($i,1)} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected token 'max'"), + array('Foreach: {foreach $list as $e} {$e}, {foreachelse} {break} {/foreach} end', 'Fenom\CompileException', "Improper usage of the tag {break}"), + array('Foreach: {foreach $list as $e} {$e}, {foreachelse} {continue} {/foreach} end', 'Fenom\CompileException', "Improper usage of the tag {continue}"), ); } - public static function providerIgnores() { + public static function providerIgnores() + { $a = array("a" => "lit. A"); return array( - array('{if 0}none{/if} literal: {$a} end', $a, 'literal: lit. A end'), - array('{if 0}none{/if} literal:{ignore} {$a} {/ignore} end', $a, 'literal: {$a} end'), - array('{if 0}none{/if} literal: { $a} end', $a, 'literal: { $a} end'), - array('{if 0}none{/if} literal: { $a}{$a}{ $a} end', $a, 'literal: { $a}lit. A{ $a} end'), + array('{if 0}none{/if} literal: {$a} end', $a, 'literal: lit. A end'), + array('{if 0}none{/if} literal:{ignore} {$a} {/ignore} end', $a, 'literal: {$a} end'), + array('{if 0}none{/if} literal: { $a} end', $a, 'literal: { $a} end'), + array('{if 0}none{/if} literal: { $a}{$a}{ $a} end', $a, 'literal: { $a}lit. A{ $a} end'), array('{if 0}none{/if} literal: { - $a} end', $a, 'literal: { $a} end'), + $a} end', $a, 'literal: { $a} end'), array('{if 0}none{/if}literal: function () { return 1; } end', $a, 'literal: function () { return 1; } end') ); } - public static function providerSwitch() { + public static function providerSwitch() + { $code1 = 'Switch: {switch $a} {case 1} one {break} {case 2} two {break} @@ -483,15 +502,17 @@ class TemplateTest extends TestCase { ); } - public static function providerSwitchInvalid() { + public static function providerSwitchInvalid() + { return array( - array('Switch: {switch}{case 1} one {break}{/switch} end', 'Fenom\CompileException', "Unexpected end of expression"), - array('Switch: {switch 1}{case} one {break}{/switch} end', 'Fenom\CompileException', "Unexpected end of expression"), - array('Switch: {switch 1}{break}{case} one {/switch} end', 'Fenom\CompileException', "Improper usage of the tag {break}"), + array('Switch: {switch}{case 1} one {break}{/switch} end', 'Fenom\CompileException', "Unexpected end of expression"), + array('Switch: {switch 1}{case} one {break}{/switch} end', 'Fenom\CompileException', "Unexpected end of expression"), + array('Switch: {switch 1}{break}{case} one {/switch} end', 'Fenom\CompileException', "Improper usage of the tag {break}"), ); } - public static function providerWhile() { + public static function providerWhile() + { $a = array("a" => 3); return array( array('While: {while false} block {/while} end', $a, 'While: end'), @@ -501,63 +522,68 @@ class TemplateTest extends TestCase { ); } - public static function providerWhileInvalid() { + public static function providerWhileInvalid() + { return array( - array('While: {while} block {/while} end', 'Fenom\CompileException', "Unexpected end of expression"), + array('While: {while} block {/while} end', 'Fenom\CompileException', "Unexpected end of expression"), ); } - public static function providerFor() { + public static function providerFor() + { $a = array("c" => 1, "s" => 1, "m" => 3); return array( - array('For: {for $a=4 to=6} $a: {$a}, {/for} end', $a, 'For: $a: 4, $a: 5, $a: 6, end'), - array('For: {for $a=4 step=2 to=10} $a: {$a}, {/for} end', $a, 'For: $a: 4, $a: 6, $a: 8, $a: 10, end'), - array('For: {for $a=4 step=-2 to=0} $a: {$a}, {/for} end', $a, 'For: $a: 4, $a: 2, $a: 0, end'), - array('For: {for $a=$c step=$s to=$m} $a: {$a}, {/for} end', $a, 'For: $a: 1, $a: 2, $a: 3, end'), - array('For: {for $a=-1 step=-max(1,2) to=-5} $a: {$a}, {/for} end', $a, 'For: $a: -1, $a: -3, $a: -5, end'), + array('For: {for $a=4 to=6} $a: {$a}, {/for} end', $a, 'For: $a: 4, $a: 5, $a: 6, end'), + array('For: {for $a=4 step=2 to=10} $a: {$a}, {/for} end', $a, 'For: $a: 4, $a: 6, $a: 8, $a: 10, end'), + array('For: {for $a=4 step=-2 to=0} $a: {$a}, {/for} end', $a, 'For: $a: 4, $a: 2, $a: 0, end'), + array('For: {for $a=$c step=$s to=$m} $a: {$a}, {/for} end', $a, 'For: $a: 1, $a: 2, $a: 3, end'), + array('For: {for $a=-1 step=-max(1,2) to=-5} $a: {$a}, {/for} end', $a, 'For: $a: -1, $a: -3, $a: -5, end'), array('For: {for $a=4 step=2 to=10} $a: {$a}, {break} break {/for} end', $a, 'For: $a: 4, end'), array('For: {for $a=4 step=2 to=8} $a: {$a}, {continue} continue {/for} end', - $a, 'For: $a: 4, $a: 6, $a: 8, end'), - array('For: {for $a=4 step=2 to=8 index=$i} $a{$i}: {$a}, {/for} end', $a, 'For: $a0: 4, $a1: 6, $a2: 8, end'), + $a, 'For: $a: 4, $a: 6, $a: 8, end'), + array('For: {for $a=4 step=2 to=8 index=$i} $a{$i}: {$a}, {/for} end', $a, 'For: $a0: 4, $a1: 6, $a2: 8, end'), array('For: {for $a=4 step=2 to=8 index=$i first=$f} {if $f}first{/if} $a{$i}: {$a}, {/for} end', - $a, 'For: first $a0: 4, $a1: 6, $a2: 8, end'), + $a, 'For: first $a0: 4, $a1: 6, $a2: 8, end'), array('For: {for $a=4 step=2 to=8 index=$i first=$f last=$l} {if $f} first {/if} $a{$i}: {$a}, {if $l} last {/if} {/for} end', - $a, 'For: first $a0: 4, $a1: 6, $a2: 8, last end'), - array('For: {for $a=1 to=-1 } $a: {$a}, {forelse} empty {/for} end', $a, 'For: empty end'), + $a, 'For: first $a0: 4, $a1: 6, $a2: 8, last end'), + array('For: {for $a=1 to=-1 } $a: {$a}, {forelse} empty {/for} end', $a, 'For: empty end'), array('For: {for $a=1 to=-1 index=$i first=$f last=$l} {if $f} first {/if} $a{$i}: {$a}, {if $l} last {/if} {forelse} empty {/for} end', - $a, 'For: empty end'), + $a, 'For: empty end'), ); } - public static function providerForInvalid() { + public static function providerForInvalid() + { return array( - array('For: {for} block1 {/for} end', 'Fenom\CompileException', "Unexpected end of expression"), - array('For: {for $a=} block1 {/for} end', 'Fenom\CompileException', "Unexpected end of expression"), - array('For: {for $a+1=3 to=6} block1 {/for} end', 'Fenom\CompileException', "Unexpected token '+'"), - array('For: {for max($a,$b)=3 to=6} block1 {/for} end', 'Fenom\CompileException', "Unexpected token 'max'"), - array('For: {for to=6 $a=3} block1 {/for} end', 'Fenom\CompileException', "Unexpected token 'to'"), + array('For: {for} block1 {/for} end', 'Fenom\CompileException', "Unexpected end of expression"), + array('For: {for $a=} block1 {/for} end', 'Fenom\CompileException', "Unexpected end of expression"), + array('For: {for $a+1=3 to=6} block1 {/for} end', 'Fenom\CompileException', "Unexpected token '+'"), + array('For: {for max($a,$b)=3 to=6} block1 {/for} end', 'Fenom\CompileException', "Unexpected token 'max'"), + array('For: {for to=6 $a=3} block1 {/for} end', 'Fenom\CompileException', "Unexpected token 'to'"), array('For: {for index=$i $a=3 to=6} block1 {/for} end', 'Fenom\CompileException', "Unexpected token 'index'"), array('For: {for first=$i $a=3 to=6} block1 {/for} end', 'Fenom\CompileException', "Unexpected token 'first'"), - array('For: {for last=$i $a=3 to=6} block1 {/for} end', 'Fenom\CompileException', "Unexpected token 'last'"), - array('For: {for $a=4 to=6 unk=4} block1 {/for} end', 'Fenom\CompileException', "Unknown parameter 'unk'"), - array('For: {for $a=4 to=6} $a: {$a}, {forelse} {break} {/for} end', 'Fenom\CompileException', "Improper usage of the tag {break}"), - array('For: {for $a=4 to=6} $a: {$a}, {forelse} {continue} {/for} end', 'Fenom\CompileException', "Improper usage of the tag {continue}"), + array('For: {for last=$i $a=3 to=6} block1 {/for} end', 'Fenom\CompileException', "Unexpected token 'last'"), + array('For: {for $a=4 to=6 unk=4} block1 {/for} end', 'Fenom\CompileException', "Unknown parameter 'unk'"), + array('For: {for $a=4 to=6} $a: {$a}, {forelse} {break} {/for} end', 'Fenom\CompileException', "Improper usage of the tag {break}"), + array('For: {for $a=4 to=6} $a: {$a}, {forelse} {continue} {/for} end', 'Fenom\CompileException', "Improper usage of the tag {continue}"), ); } - public static function providerLayersInvalid() { + public static function providerLayersInvalid() + { return array( array('Layers: {foreach $list as $e} block1 {if 1} {foreachelse} {/if} {/foreach} end', 'Fenom\CompileException', "Unexpected tag 'foreachelse' (this tag can be used with 'foreach')"), - array('Layers: {foreach $list as $e} block1 {if 1} {/foreach} {/if} end', 'Fenom\CompileException', "Unexpected closing of the tag 'foreach'"), - array('Layers: {for $a=4 to=6} block1 {if 1} {forelse} {/if} {/for} end', 'Fenom\CompileException', "Unexpected tag 'forelse' (this tag can be used with 'for')"), - array('Layers: {for $a=4 to=6} block1 {if 1} {/for} {/if} end', 'Fenom\CompileException', "Unexpected closing of the tag 'for'"), - array('Layers: {switch 1} {if 1} {case 1} {/if} {/switch} end', 'Fenom\CompileException', "Unexpected tag 'case' (this tag can be used with 'switch')"), - array('Layers: {/switch} end', 'Fenom\CompileException', "Unexpected closing of the tag 'switch'"), - array('Layers: {if 1} end', 'Fenom\CompileException', "Unclosed tag: {if}"), + array('Layers: {foreach $list as $e} block1 {if 1} {/foreach} {/if} end', 'Fenom\CompileException', "Unexpected closing of the tag 'foreach'"), + array('Layers: {for $a=4 to=6} block1 {if 1} {forelse} {/if} {/for} end', 'Fenom\CompileException', "Unexpected tag 'forelse' (this tag can be used with 'for')"), + array('Layers: {for $a=4 to=6} block1 {if 1} {/for} {/if} end', 'Fenom\CompileException', "Unexpected closing of the tag 'for'"), + array('Layers: {switch 1} {if 1} {case 1} {/if} {/switch} end', 'Fenom\CompileException', "Unexpected tag 'case' (this tag can be used with 'switch')"), + array('Layers: {/switch} end', 'Fenom\CompileException', "Unexpected closing of the tag 'switch'"), + array('Layers: {if 1} end', 'Fenom\CompileException', "Unclosed tag: {if}"), ); } - public static function providerExtends() { + public static function providerExtends() + { return array( array('{extends file="parent.tpl"}{block name="bk1"} block1 {/block}', "Template extended by block1"), array('{extends "parent.tpl"}{block "bk1"} block1 {/block}', "Template extended by block1"), @@ -568,7 +594,8 @@ class TemplateTest extends TestCase { ); } - public static function providerIsOperator() { + public static function providerIsOperator() + { return array( // is {$type} array('{if $one is int} block1 {else} block2 {/if}', 'block1'), @@ -624,7 +651,8 @@ class TemplateTest extends TestCase { ); } - public static function providerInOperator() { + public static function providerInOperator() + { return array( array('{if $one in "qwertyuiop 1"} block1 {else} block2 {/if}', 'block1'), array('{if $one in string "qwertyuiop 1"} block1 {else} block2 {/if}', 'block1'), @@ -641,11 +669,12 @@ class TemplateTest extends TestCase { ); } - public function _testSandbox() { + public function _testSandbox() + { try { var_dump($this->fenom->compileCode('{$one.two->three[e]()}')->getBody()); - } catch(\Exception $e) { - print_r($e->getMessage()."\n".$e->getTraceAsString()); + } catch (\Exception $e) { + print_r($e->getMessage() . "\n" . $e->getTraceAsString()); } exit; } @@ -653,28 +682,32 @@ class TemplateTest extends TestCase { /** * @dataProvider providerVars */ - public function testVars($code, $vars, $result) { + public function testVars($code, $vars, $result) + { $this->exec($code, $vars, $result); - } + } /** * @dataProvider providerVarsInvalid */ - public function testVarsInvalid($code, $exception, $message, $options = 0) { + public function testVarsInvalid($code, $exception, $message, $options = 0) + { $this->execError($code, $exception, $message, $options); } /** * @dataProvider providerModifiers */ - public function testModifiers($code, $vars, $result) { + public function testModifiers($code, $vars, $result) + { $this->exec($code, $vars, $result); } /** * @dataProvider providerModifiersInvalid */ - public function testModifiersInvalid($code, $exception, $message, $options = 0) { + public function testModifiersInvalid($code, $exception, $message, $options = 0) + { $this->execError($code, $exception, $message, $options); } @@ -682,14 +715,16 @@ class TemplateTest extends TestCase { * @group expression * @dataProvider providerExpressions */ - public function testExpressions($code, $vars, $result) { + public function testExpressions($code, $vars, $result) + { $this->exec($code, $vars, $result); } /** * @dataProvider providerExpressionsInvalid */ - public function testExpressionsInvalid($code, $exception, $message, $options = 0) { + public function testExpressionsInvalid($code, $exception, $message, $options = 0) + { $this->execError($code, $exception, $message, $options); } @@ -697,14 +732,16 @@ class TemplateTest extends TestCase { * @group include * @dataProvider providerInclude */ - public function testInclude($code, $vars, $result) { + public function testInclude($code, $vars, $result) + { $this->exec($code, $vars, $result); } /** * @dataProvider providerIncludeInvalid */ - public function testIncludeInvalid($code, $exception, $message, $options = 0) { + public function testIncludeInvalid($code, $exception, $message, $options = 0) + { $this->execError($code, $exception, $message, $options); } @@ -712,28 +749,32 @@ class TemplateTest extends TestCase { * @dataProvider providerIf * @group test-if */ - public function testIf($code, $vars, $result, $options = 0) { + public function testIf($code, $vars, $result, $options = 0) + { $this->exec($code, $vars, $result, $options); } /** * @dataProvider providerIfInvalid */ - public function testIfInvalid($code, $exception, $message, $options = 0) { + public function testIfInvalid($code, $exception, $message, $options = 0) + { $this->execError($code, $exception, $message, $options); } /** * @dataProvider providerCreateVar */ - public function testCreateVar($code, $vars, $result) { + public function testCreateVar($code, $vars, $result) + { $this->exec($code, $vars, $result); } /** * @dataProvider providerCreateVarInvalid */ - public function testCreateVarInvalid($code, $exception, $message, $options = 0) { + public function testCreateVarInvalid($code, $exception, $message, $options = 0) + { $this->execError($code, $exception, $message, $options); } @@ -741,77 +782,88 @@ class TemplateTest extends TestCase { * @group ternary * @dataProvider providerTernary */ - public function testTernary($code, $vars, $result = 'right') { - $this->exec(__FUNCTION__.": $code end", $vars, __FUNCTION__.": $result end"); + public function testTernary($code, $vars, $result = 'right') + { + $this->exec(__FUNCTION__ . ": $code end", $vars, __FUNCTION__ . ": $result end"); } /** * @dataProvider providerForeach */ - public function testForeach($code, $vars, $result) { + public function testForeach($code, $vars, $result) + { $this->exec($code, $vars, $result); } /** * @dataProvider providerForeachInvalid */ - public function testForeachInvalid($code, $exception, $message, $options = 0) { + public function testForeachInvalid($code, $exception, $message, $options = 0) + { $this->execError($code, $exception, $message, $options); } /** * @dataProvider providerFor */ - public function testFor($code, $vars, $result) { + public function testFor($code, $vars, $result) + { $this->exec($code, $vars, $result); } /** * @dataProvider providerForInvalid */ - public function testForInvalid($code, $exception, $message, $options = 0) { + public function testForInvalid($code, $exception, $message, $options = 0) + { $this->execError($code, $exception, $message, $options); } /** * @dataProvider providerIgnores */ - public function testIgnores($code, $vars, $result) { + public function testIgnores($code, $vars, $result) + { $this->exec($code, $vars, $result); } /** * @dataProvider providerSwitch */ - public function testSwitch($code, $vars, $result) { + public function testSwitch($code, $vars, $result) + { $this->exec($code, $vars, $result); } /** * @dataProvider providerSwitchInvalid */ - public function testSwitchInvalid($code, $exception, $message, $options = 0) { + public function testSwitchInvalid($code, $exception, $message, $options = 0) + { $this->execError($code, $exception, $message, $options); } /** * @dataProvider providerWhile */ - public function testWhile($code, $vars, $result) { + public function testWhile($code, $vars, $result) + { $this->exec($code, $vars, $result); } /** * @dataProvider providerWhileInvalid */ - public function testWhileInvalid($code, $exception, $message, $options = 0) { + public function testWhileInvalid($code, $exception, $message, $options = 0) + { $this->execError($code, $exception, $message, $options); } /** * @dataProvider providerLayersInvalid */ - public function testLayersInvalid($code, $exception, $message, $options = 0) { + public function testLayersInvalid($code, $exception, $message, $options = 0) + { $this->execError($code, $exception, $message, $options); } @@ -819,7 +871,8 @@ class TemplateTest extends TestCase { * @group is_operator * @dataProvider providerIsOperator */ - public function testIsOperator($code, $result) { + public function testIsOperator($code, $result) + { $this->exec($code, self::getVars(), $result); } @@ -827,7 +880,8 @@ class TemplateTest extends TestCase { * @group in_operator * @dataProvider providerInOperator */ - public function testInOperator($code, $result) { + public function testInOperator($code, $result) + { $this->exec($code, self::getVars(), $result); } } diff --git a/tests/cases/Fenom/TokenizerTest.php b/tests/cases/Fenom/TokenizerTest.php index 99b2946..af170ed 100644 --- a/tests/cases/Fenom/TokenizerTest.php +++ b/tests/cases/Fenom/TokenizerTest.php @@ -2,58 +2,61 @@ namespace Fenom; use Fenom\Tokenizer; -class TokenizerTest extends \PHPUnit_Framework_TestCase { +class TokenizerTest extends \PHPUnit_Framework_TestCase +{ - public function testTokens() { - $code = 'hello, please resolve this example: sin($x)+tan($x*$t) = {U|[0,1]}'; - $tokens = new Tokenizer($code); - $this->assertSame(T_STRING, $tokens->key()); - $this->assertSame("hello", $tokens->current()); + public function testTokens() + { + $code = 'hello, please resolve this example: sin($x)+tan($x*$t) = {U|[0,1]}'; + $tokens = new Tokenizer($code); + $this->assertSame(T_STRING, $tokens->key()); + $this->assertSame("hello", $tokens->current()); - $this->assertTrue($tokens->isNext(",")); - $this->assertFalse($tokens->isNext("=")); - $this->assertFalse($tokens->isNext(T_STRING)); - $this->assertFalse($tokens->isNext($tokens::MACRO_UNARY)); + $this->assertTrue($tokens->isNext(",")); + $this->assertFalse($tokens->isNext("=")); + $this->assertFalse($tokens->isNext(T_STRING)); + $this->assertFalse($tokens->isNext($tokens::MACRO_UNARY)); - $this->assertFalse($tokens->isNext("=", T_STRING, $tokens::MACRO_UNARY)); - $this->assertTrue($tokens->isNext("=", T_STRING, $tokens::MACRO_UNARY, ",")); + $this->assertFalse($tokens->isNext("=", T_STRING, $tokens::MACRO_UNARY)); + $this->assertTrue($tokens->isNext("=", T_STRING, $tokens::MACRO_UNARY, ",")); - $this->assertSame(",", $tokens->getNext()); - $this->assertSame(",", $tokens->key()); - $this->assertSame("please", $tokens->getNext(T_STRING)); - $this->assertSame("resolve", $tokens->getNext($tokens::MACRO_UNARY, T_STRING)); + $this->assertSame(",", $tokens->getNext()); + $this->assertSame(",", $tokens->key()); + $this->assertSame("please", $tokens->getNext(T_STRING)); + $this->assertSame("resolve", $tokens->getNext($tokens::MACRO_UNARY, T_STRING)); - $tokens->next(); - $tokens->next(); - $tokens->next(); + $tokens->next(); + $tokens->next(); + $tokens->next(); - $this->assertSame(":", $tokens->current()); - $this->assertSame(":", $tokens->key()); + $this->assertSame(":", $tokens->current()); + $this->assertSame(":", $tokens->key()); - $this->assertSame("sin", $tokens->getNext($tokens::MACRO_STRING)); - $this->assertSame("sin", $tokens->current()); - $this->assertSame(T_STRING, $tokens->key()); - $this->assertTrue($tokens->is(T_STRING)); - $this->assertTrue($tokens->is($tokens::MACRO_STRING)); - $this->assertFalse($tokens->is($tokens::MACRO_EQUALS)); - $this->assertFalse($tokens->is(T_DNUMBER)); - $this->assertFalse($tokens->is(":")); - $this->assertSame("(", $tokens->getNext("(",")")); + $this->assertSame("sin", $tokens->getNext($tokens::MACRO_STRING)); + $this->assertSame("sin", $tokens->current()); + $this->assertSame(T_STRING, $tokens->key()); + $this->assertTrue($tokens->is(T_STRING)); + $this->assertTrue($tokens->is($tokens::MACRO_STRING)); + $this->assertFalse($tokens->is($tokens::MACRO_EQUALS)); + $this->assertFalse($tokens->is(T_DNUMBER)); + $this->assertFalse($tokens->is(":")); + $this->assertSame("(", $tokens->getNext("(", ")")); - $tokens->next(); - $tokens->next(); - $this->assertSame("+", $tokens->getNext($tokens::MACRO_BINARY)); - } + $tokens->next(); + $tokens->next(); + $this->assertSame("+", $tokens->getNext($tokens::MACRO_BINARY)); + } - public function testSkip() { + public function testSkip() + { $text = "1 foo: bar ( 3 + double ) "; $tokens = new Tokenizer($text); $tokens->skip()->skip(T_STRING)->skip(':'); try { $tokens->skip(T_STRING)->skip('(')->skip(':'); - } catch(\Exception $e) { + } catch (\Exception $e) { $this->assertInstanceOf('Fenom\UnexpectedTokenException', $e); $this->assertStringStartsWith("Unexpected token '3' in expression, expect ':'", $e->getMessage()); } diff --git a/tests/cases/FenomTest.php b/tests/cases/FenomTest.php index 8131885..4c49846 100644 --- a/tests/cases/FenomTest.php +++ b/tests/cases/FenomTest.php @@ -3,22 +3,25 @@ use Fenom\Render, Fenom\Provider as FS; -class FenomTest extends \Fenom\TestCase { +class FenomTest extends \Fenom\TestCase +{ - public static function providerOptions() { + public static function providerOptions() + { return array( - array("disable_methods", Fenom::DENY_METHODS), - array("disable_native_funcs", Fenom::DENY_INLINE_FUNCS), - array("disable_cache", Fenom::DISABLE_CACHE), - array("force_compile", Fenom::FORCE_COMPILE), - array("auto_reload", Fenom::AUTO_RELOAD), - array("force_include", Fenom::FORCE_INCLUDE), - array("auto_escape", Fenom::AUTO_ESCAPE), - array("force_verify", Fenom::FORCE_VERIFY) + array("disable_methods", Fenom::DENY_METHODS), + array("disable_native_funcs", Fenom::DENY_INLINE_FUNCS), + array("disable_cache", Fenom::DISABLE_CACHE), + array("force_compile", Fenom::FORCE_COMPILE), + array("auto_reload", Fenom::AUTO_RELOAD), + array("force_include", Fenom::FORCE_INCLUDE), + array("auto_escape", Fenom::AUTO_ESCAPE), + array("force_verify", Fenom::FORCE_VERIFY) ); } - public function testCompileFile() { + public function testCompileFile() + { $a = array( "a" => "a", "b" => "b" @@ -29,17 +32,19 @@ class FenomTest extends \Fenom\TestCase { $this->assertSame("Template 2 b", $this->fenom->fetch('template2.tpl', $a)); $this->assertInstanceOf('Fenom\Render', $this->fenom->getTemplate('template1.tpl')); $this->assertInstanceOf('Fenom\Render', $this->fenom->getTemplate('template2.tpl')); - $this->assertSame(3, iterator_count(new FilesystemIterator(FENOM_RESOURCES.'/compile'))); + $this->assertSame(3, iterator_count(new FilesystemIterator(FENOM_RESOURCES . '/compile'))); } - public function testStorage() { + public function testStorage() + { $this->tpl('custom.tpl', 'Custom template'); $this->assertSame("Custom template", $this->fenom->fetch('custom.tpl', array())); $this->tpl('custom.tpl', 'Custom template 2'); $this->assertSame("Custom template", $this->fenom->fetch('custom.tpl', array())); } - public function testCheckMTime() { + public function testCheckMTime() + { $this->fenom->setOptions(Fenom::FORCE_COMPILE); $this->tpl('custom.tpl', 'Custom template'); $this->assertSame("Custom template", $this->fenom->fetch('custom.tpl', array())); @@ -49,7 +54,8 @@ class FenomTest extends \Fenom\TestCase { $this->assertSame("Custom template (new)", $this->fenom->fetch('custom.tpl', array())); } - public function testForceCompile() { + public function testForceCompile() + { $this->fenom->setOptions(Fenom::FORCE_COMPILE); $this->tpl('custom.tpl', 'Custom template'); $this->assertSame("Custom template", $this->fenom->fetch('custom.tpl', array())); @@ -57,7 +63,8 @@ class FenomTest extends \Fenom\TestCase { $this->assertSame("Custom template (new)", $this->fenom->fetch('custom.tpl', array())); } - public function testSetModifier() { + public function testSetModifier() + { $this->fenom->addModifier("mymod", "myMod"); $this->tpl('custom.tpl', 'Custom modifier {$a|mymod}'); $this->assertSame("Custom modifier (myMod)Custom(/myMod)", $this->fenom->fetch('custom.tpl', array("a" => "Custom"))); @@ -66,7 +73,8 @@ class FenomTest extends \Fenom\TestCase { /** * @group add_functions */ - public function testSetFunctions() { + public function testSetFunctions() + { $this->fenom->setOptions(Fenom::FORCE_COMPILE); $this->fenom->addFunction("myfunc", "myFunc"); $this->fenom->addBlockFunction("myblockfunc", "myBlockFunc"); @@ -76,22 +84,24 @@ class FenomTest extends \Fenom\TestCase { $this->assertSame("Custom function Block:foo:this block1:Block", $this->fenom->fetch('custom.tpl', array())); } - public function testSetCompilers() { + public function testSetCompilers() + { $this->fenom->setOptions(Fenom::FORCE_COMPILE); $this->fenom->addCompiler("mycompiler", 'myCompiler'); $this->fenom->addBlockCompiler("myblockcompiler", 'myBlockCompilerOpen', 'myBlockCompilerClose', array( 'tag' => 'myBlockCompilerTag' )); $this->tpl('custom.tpl', 'Custom compiler {mycompiler name="bar"}'); - $this->assertSame("Custom compiler PHP_VERSION: ".PHP_VERSION." (for bar)", $this->fenom->fetch('custom.tpl', array())); + $this->assertSame("Custom compiler PHP_VERSION: " . PHP_VERSION . " (for bar)", $this->fenom->fetch('custom.tpl', array())); $this->tpl('custom.tpl', 'Custom compiler {myblockcompiler name="bar"} block1 {tag name="baz"} block2 {/myblockcompiler}'); - $this->assertSame("Custom compiler PHP_VERSION: ".PHP_VERSION." (for bar) block1 Tag baz of compiler block2 End of compiler", $this->fenom->fetch('custom.tpl', array())); + $this->assertSame("Custom compiler PHP_VERSION: " . PHP_VERSION . " (for bar) block1 Tag baz of compiler block2 End of compiler", $this->fenom->fetch('custom.tpl', array())); } /** * @dataProvider providerOptions */ - public function testOptions($code, $option) { + public function testOptions($code, $option) + { static $options = array(); static $flags = 0; $options[$code] = true; diff --git a/tests/resources/actions.php b/tests/resources/actions.php index 5ae3f7d..eaaff61 100644 --- a/tests/resources/actions.php +++ b/tests/resources/actions.php @@ -1,31 +1,38 @@ parseParams($tokenizer); - return 'echo "PHP_VERSION: ".PHP_VERSION." (for ".'.$p["name"].'.")";'; + return 'echo "PHP_VERSION: ".PHP_VERSION." (for ".' . $p["name"] . '.")";'; } -function myBlockCompilerOpen(Fenom\Tokenizer $tokenizer, Fenom\Scope $scope) { +function myBlockCompilerOpen(Fenom\Tokenizer $tokenizer, Fenom\Scope $scope) +{ return myCompiler($tokenizer, $scope->tpl); } -function myBlockCompilerClose(Fenom\Tokenizer $tokenizer, Fenom\Scope $scope) { +function myBlockCompilerClose(Fenom\Tokenizer $tokenizer, Fenom\Scope $scope) +{ return 'echo "End of compiler";'; } -function myBlockCompilerTag(Fenom\Tokenizer $tokenizer, Fenom\Scope $scope) { +function myBlockCompilerTag(Fenom\Tokenizer $tokenizer, Fenom\Scope $scope) +{ $p = $scope->tpl->parseParams($tokenizer); - return 'echo "Tag ".'.$p["name"].'." of compiler";'; + return 'echo "Tag ".' . $p["name"] . '." of compiler";'; } \ No newline at end of file From e9b7951d7a9702b29d7ac167ed6902454a579b8b Mon Sep 17 00:00:00 2001 From: bzick Date: Mon, 29 Jul 2013 16:15:52 +0400 Subject: [PATCH 09/18] One file - one class (#24) --- src/Fenom/Compiler.php | 2 + src/Fenom/Error/CompileException.php | 18 ++ src/Fenom/Error/InvalidUsageException.php | 18 ++ src/Fenom/Error/SecurityException.php | 18 ++ src/Fenom/Error/TokenizeException.php | 18 ++ .../{ => Error}/UnexpectedTokenException.php | 11 +- src/Fenom/Template.php | 25 +-- src/Fenom/Tokenizer.php | 8 +- tests/cases/Fenom/CommentTest.php | 2 +- tests/cases/Fenom/MacrosTest.php | 2 +- tests/cases/Fenom/TemplateTest.php | 180 +++++++++--------- tests/cases/Fenom/TokenizerTest.php | 2 +- 12 files changed, 189 insertions(+), 115 deletions(-) create mode 100644 src/Fenom/Error/CompileException.php create mode 100644 src/Fenom/Error/InvalidUsageException.php create mode 100644 src/Fenom/Error/SecurityException.php create mode 100644 src/Fenom/Error/TokenizeException.php rename src/Fenom/{ => Error}/UnexpectedTokenException.php (79%) diff --git a/src/Fenom/Compiler.php b/src/Fenom/Compiler.php index 3ab07f4..f20f5dc 100644 --- a/src/Fenom/Compiler.php +++ b/src/Fenom/Compiler.php @@ -8,6 +8,8 @@ * file that was distributed with this source code. */ namespace Fenom; +use Fenom\Error\InvalidUsageException; +use Fenom\Error\UnexpectedTokenException; use Fenom\Tokenizer; use Fenom\Template; use Fenom\Scope; diff --git a/src/Fenom/Error/CompileException.php b/src/Fenom/Error/CompileException.php new file mode 100644 index 0000000..3bb8789 --- /dev/null +++ b/src/Fenom/Error/CompileException.php @@ -0,0 +1,18 @@ +execError('{* ', 'Fenom\CompileException', "Unclosed comment block in line"); + $this->execError('{* ', 'Fenom\Error\CompileException', "Unclosed comment block in line"); } /** diff --git a/tests/cases/Fenom/MacrosTest.php b/tests/cases/Fenom/MacrosTest.php index 00ecda7..d919174 100644 --- a/tests/cases/Fenom/MacrosTest.php +++ b/tests/cases/Fenom/MacrosTest.php @@ -90,7 +90,7 @@ class MacrosTest extends TestCase /** * @expectedExceptionMessage Undefined macro 'plus' - * @expectedException \Fenom\CompileException + * @expectedException \Fenom\Error\CompileException */ public function testImportMiss() { diff --git a/tests/cases/Fenom/TemplateTest.php b/tests/cases/Fenom/TemplateTest.php index 683faf8..64f8e39 100644 --- a/tests/cases/Fenom/TemplateTest.php +++ b/tests/cases/Fenom/TemplateTest.php @@ -81,14 +81,14 @@ class TemplateTest extends TestCase public static function providerVarsInvalid() { return array( - array('hello, {$a.}!', 'Fenom\CompileException', "Unexpected end of expression"), - array('hello, {$b[c}!', 'Fenom\CompileException', "Unexpected end of expression"), - array('hello, {$b.c]}!', 'Fenom\CompileException', "Unexpected token ']'"), - array('hello, {$b[ ]}!', 'Fenom\CompileException', "Unexpected token ']'"), - array('hello, {$b[9/].c}!', 'Fenom\CompileException', "Unexpected token ']'"), - array('hello, {$b[3]$c}!', 'Fenom\CompileException', "Unexpected token '\$c'"), - array('hello, {$b[3]c}!', 'Fenom\CompileException', "Unexpected token 'c'"), - array('hello, {$b.obj->valid()}!', 'Fenom\SecurityException', "Forbidden to call methods", Fenom::DENY_METHODS), + array('hello, {$a.}!', 'Fenom\Error\CompileException', "Unexpected end of expression"), + array('hello, {$b[c}!', 'Fenom\Error\CompileException', "Unexpected end of expression"), + array('hello, {$b.c]}!', 'Fenom\Error\CompileException', "Unexpected token ']'"), + array('hello, {$b[ ]}!', 'Fenom\Error\CompileException', "Unexpected token ']'"), + array('hello, {$b[9/].c}!', 'Fenom\Error\CompileException', "Unexpected token ']'"), + array('hello, {$b[3]$c}!', 'Fenom\Error\CompileException', "Unexpected token '\$c'"), + array('hello, {$b[3]c}!', 'Fenom\Error\CompileException', "Unexpected token 'c'"), + array('hello, {$b.obj->valid()}!', 'Fenom\Error\SecurityException', "Forbidden to call methods", Fenom::DENY_METHODS), ); } @@ -135,12 +135,12 @@ class TemplateTest extends TestCase public static function providerModifiersInvalid() { return array( - array('Mod: {$lorem|}!', 'Fenom\CompileException', "Unexpected end of expression"), - array('Mod: {$lorem|str_rot13}!', 'Fenom\CompileException', "Modifier str_rot13 not found", Fenom::DENY_INLINE_FUNCS), - array('Mod: {$lorem|my_encode}!', 'Fenom\CompileException', "Modifier my_encode not found"), - array('Mod: {$lorem|truncate:}!', 'Fenom\CompileException', "Unexpected end of expression"), - array('Mod: {$lorem|truncate:abs}!', 'Fenom\CompileException', "Unexpected token 'abs'"), - array('Mod: {$lorem|truncate:80|}!', 'Fenom\CompileException', "Unexpected end of expression"), + array('Mod: {$lorem|}!', 'Fenom\Error\CompileException', "Unexpected end of expression"), + array('Mod: {$lorem|str_rot13}!', 'Fenom\Error\CompileException', "Modifier str_rot13 not found", Fenom::DENY_INLINE_FUNCS), + array('Mod: {$lorem|my_encode}!', 'Fenom\Error\CompileException', "Modifier my_encode not found"), + array('Mod: {$lorem|truncate:}!', 'Fenom\Error\CompileException', "Unexpected end of expression"), + array('Mod: {$lorem|truncate:abs}!', 'Fenom\Error\CompileException', "Unexpected token 'abs'"), + array('Mod: {$lorem|truncate:80|}!', 'Fenom\Error\CompileException', "Unexpected end of expression"), ); } @@ -185,19 +185,19 @@ class TemplateTest extends TestCase public static function providerExpressionsInvalid() { return array( - array('If: {-"hi"} end', 'Fenom\CompileException', "Unexpected token '-'"), - array('If: {($a++)++} end', 'Fenom\CompileException', "Unexpected token '++'"), - array('If: {$a + * $c} end', 'Fenom\CompileException', "Unexpected token '*'"), - array('If: {$a + } end', 'Fenom\CompileException', "Unexpected token '+'"), - array('If: {$a + =} end', 'Fenom\CompileException', "Unexpected token '='"), - array('If: {$a + 1 =} end', 'Fenom\CompileException', "Unexpected token '='"), - array('If: {$a + 1 = 6} end', 'Fenom\CompileException', "Unexpected token '='"), - array('If: {/$a} end', 'Fenom\CompileException', "Unexpected token '\$a'"), - array('If: {$a == 5 > 4} end', 'Fenom\CompileException', "Unexpected token '>'"), - array('If: {$a != 5 <= 4} end', 'Fenom\CompileException', "Unexpected token '<='"), - array('If: {$a != 5 => 4} end', 'Fenom\CompileException', "Unexpected token '=>'"), - array('If: {$a + (*6)} end', 'Fenom\CompileException', "Unexpected token '*'"), - array('If: {$a + ( 6} end', 'Fenom\CompileException', "Unexpected end of expression, expect ')'"), + array('If: {-"hi"} end', 'Fenom\Error\CompileException', "Unexpected token '-'"), + array('If: {($a++)++} end', 'Fenom\Error\CompileException', "Unexpected token '++'"), + array('If: {$a + * $c} end', 'Fenom\Error\CompileException', "Unexpected token '*'"), + array('If: {$a + } end', 'Fenom\Error\CompileException', "Unexpected token '+'"), + array('If: {$a + =} end', 'Fenom\Error\CompileException', "Unexpected token '='"), + array('If: {$a + 1 =} end', 'Fenom\Error\CompileException', "Unexpected token '='"), + array('If: {$a + 1 = 6} end', 'Fenom\Error\CompileException', "Unexpected token '='"), + array('If: {/$a} end', 'Fenom\Error\CompileException', "Unexpected token '\$a'"), + array('If: {$a == 5 > 4} end', 'Fenom\Error\CompileException', "Unexpected token '>'"), + array('If: {$a != 5 <= 4} end', 'Fenom\Error\CompileException', "Unexpected token '<='"), + array('If: {$a != 5 => 4} end', 'Fenom\Error\CompileException', "Unexpected token '=>'"), + array('If: {$a + (*6)} end', 'Fenom\Error\CompileException', "Unexpected token '*'"), + array('If: {$a + ( 6} end', 'Fenom\Error\CompileException', "Unexpected end of expression, expect ')'"), ); } @@ -236,8 +236,8 @@ class TemplateTest extends TestCase public static function providerIncludeInvalid() { return array( - array('Include {include} template', 'Fenom\CompileException', "Unexpected end of expression"), - array('Include {include another="welcome.tpl"} template', 'Fenom\CompileException', "Unexpected token '='"), + array('Include {include} template', 'Fenom\Error\CompileException', "Unexpected end of expression"), + array('Include {include another="welcome.tpl"} template', 'Fenom\Error\CompileException', "Unexpected token '='"), ); } @@ -282,10 +282,10 @@ class TemplateTest extends TestCase public static function providerIfInvalid() { return array( - array('If: {if} block1 {/if} end', 'Fenom\CompileException', "Unexpected end of expression"), - array('If: {if 1} block1 {elseif} block2 {/if} end', 'Fenom\CompileException', "Unexpected end of expression"), - array('If: {if 1} block1 {else} block2 {elseif 0} block3 {/if} end', 'Fenom\CompileException', "Incorrect use of the tag {elseif}"), - array('If: {if 1} block1 {else} block2 {/if} block3 {elseif 0} end', 'Fenom\CompileException', "Unexpected tag 'elseif' (this tag can be used with 'if')"), + array('If: {if} block1 {/if} end', 'Fenom\Error\CompileException', "Unexpected end of expression"), + array('If: {if 1} block1 {elseif} block2 {/if} end', 'Fenom\Error\CompileException', "Unexpected end of expression"), + array('If: {if 1} block1 {else} block2 {elseif 0} block3 {/if} end', 'Fenom\Error\CompileException', "Incorrect use of the tag {elseif}"), + array('If: {if 1} block1 {else} block2 {/if} block3 {elseif 0} end', 'Fenom\Error\CompileException', "Unexpected tag 'elseif' (this tag can be used with 'if')"), ); } @@ -325,20 +325,20 @@ class TemplateTest extends TestCase public static function providerCreateVarInvalid() { return array( - array('Create: {var $v} Result: {$v} end', 'Fenom\CompileException', "Unclosed tag: {var} opened"), - array('Create: {var $v = } Result: {$v} end', 'Fenom\CompileException', "Unexpected end of expression"), - array('Create: {var $v = 1++} Result: {$v} end', 'Fenom\CompileException', "Unexpected token '++'"), - array('Create: {var $v = c} Result: {$v} end', 'Fenom\CompileException', "Unexpected token 'c'"), - array('Create: {var $v = ($a)++} Result: {$v} end', 'Fenom\CompileException', "Unexpected token '++'"), - array('Create: {var $v = --$a++} Result: {$v} end', 'Fenom\CompileException', "Can not use two increments and/or decrements for one variable"), - array('Create: {var $v = $a|upper++} Result: {$v} end', 'Fenom\CompileException', "Unexpected token '++'"), - array('Create: {var $v = max($a,2)++} Result: {$v} end', 'Fenom\CompileException', "Unexpected token '++'"), - array('Create: {var $v = max($a,2)} Result: {$v} end', 'Fenom\CompileException', "Function max not found", Fenom::DENY_NATIVE_FUNCS), - array('Create: {var $v = 4*} Result: {$v} end', 'Fenom\CompileException', "Unexpected token '*'"), - array('Create: {var $v = ""$a} Result: {$v} end', 'Fenom\CompileException', "Unexpected token '\$a'"), - array('Create: {var $v = [1,2} Result: {$v} end', 'Fenom\CompileException', "Unexpected end of expression"), - array('Create: {var $v = empty(2)} Result: {$v} end', 'Fenom\CompileException', "Unexpected token 2, isset() and empty() accept only variables"), - array('Create: {var $v = isset(2)} Result: {$v} end', 'Fenom\CompileException', "Unexpected token 2, isset() and empty() accept only variables"), + array('Create: {var $v} Result: {$v} end', 'Fenom\Error\CompileException', "Unclosed tag: {var} opened"), + array('Create: {var $v = } Result: {$v} end', 'Fenom\Error\CompileException', "Unexpected end of expression"), + array('Create: {var $v = 1++} Result: {$v} end', 'Fenom\Error\CompileException', "Unexpected token '++'"), + array('Create: {var $v = c} Result: {$v} end', 'Fenom\Error\CompileException', "Unexpected token 'c'"), + array('Create: {var $v = ($a)++} Result: {$v} end', 'Fenom\Error\CompileException', "Unexpected token '++'"), + array('Create: {var $v = --$a++} Result: {$v} end', 'Fenom\Error\CompileException', "Can not use two increments and/or decrements for one variable"), + array('Create: {var $v = $a|upper++} Result: {$v} end', 'Fenom\Error\CompileException', "Unexpected token '++'"), + array('Create: {var $v = max($a,2)++} Result: {$v} end', 'Fenom\Error\CompileException', "Unexpected token '++'"), + array('Create: {var $v = max($a,2)} Result: {$v} end', 'Fenom\Error\CompileException', "Function max not found", Fenom::DENY_NATIVE_FUNCS), + array('Create: {var $v = 4*} Result: {$v} end', 'Fenom\Error\CompileException', "Unexpected token '*'"), + array('Create: {var $v = ""$a} Result: {$v} end', 'Fenom\Error\CompileException', "Unexpected token '\$a'"), + array('Create: {var $v = [1,2} Result: {$v} end', 'Fenom\Error\CompileException', "Unexpected end of expression"), + array('Create: {var $v = empty(2)} Result: {$v} end', 'Fenom\Error\CompileException', "Unexpected token 2, isset() and empty() accept only variables"), + array('Create: {var $v = isset(2)} Result: {$v} end', 'Fenom\Error\CompileException', "Unexpected token 2, isset() and empty() accept only variables"), ); } @@ -436,27 +436,27 @@ class TemplateTest extends TestCase public static function providerForeachInvalid() { return array( - array('Foreach: {foreach} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected end of tag {foreach}"), - array('Foreach: {foreach $list} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected end of expression"), - array('Foreach: {foreach $list+1 as $e} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected token '+'"), - array('Foreach: {foreach array_random() as $e} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected token 'array_random'"), - array('Foreach: {foreach $list as $e+1} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected token '+'"), - array('Foreach: {foreach $list as $k+1 => $e} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected token '+'"), - array('Foreach: {foreach $list as max($i,1) => $e} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected token 'max'"), - array('Foreach: {foreach $list as max($e,1)} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected token 'max'"), - array('Foreach: {foreach $list => $e} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected token '=>'"), - array('Foreach: {foreach $list $k => $e} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected token '\$k'"), - array('Foreach: {foreach $list as $k =>} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected end of expression"), - array('Foreach: {foreach last=$l $list as $e } {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected token 'last' in tag {foreach}"), - array('Foreach: {foreach $list as $e unknown=1} {$e}, {/foreach} end', 'Fenom\CompileException', "Unknown parameter 'unknown'"), - array('Foreach: {foreach $list as $e index=$i+1} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected token '+'"), - array('Foreach: {foreach $list as $e first=$f+1} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected token '+'"), - array('Foreach: {foreach $list as $e last=$l+1} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected token '+'"), - array('Foreach: {foreach $list as $e index=max($i,1)} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected token 'max'"), - array('Foreach: {foreach $list as $e first=max($i,1)} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected token 'max'"), - array('Foreach: {foreach $list as $e last=max($i,1)} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected token 'max'"), - array('Foreach: {foreach $list as $e} {$e}, {foreachelse} {break} {/foreach} end', 'Fenom\CompileException', "Improper usage of the tag {break}"), - array('Foreach: {foreach $list as $e} {$e}, {foreachelse} {continue} {/foreach} end', 'Fenom\CompileException', "Improper usage of the tag {continue}"), + array('Foreach: {foreach} {$e}, {/foreach} end', 'Fenom\Error\CompileException', "Unexpected end of tag {foreach}"), + array('Foreach: {foreach $list} {$e}, {/foreach} end', 'Fenom\Error\CompileException', "Unexpected end of expression"), + array('Foreach: {foreach $list+1 as $e} {$e}, {/foreach} end', 'Fenom\Error\CompileException', "Unexpected token '+'"), + array('Foreach: {foreach array_random() as $e} {$e}, {/foreach} end', 'Fenom\Error\CompileException', "Unexpected token 'array_random'"), + array('Foreach: {foreach $list as $e+1} {$e}, {/foreach} end', 'Fenom\Error\CompileException', "Unexpected token '+'"), + array('Foreach: {foreach $list as $k+1 => $e} {$e}, {/foreach} end', 'Fenom\Error\CompileException', "Unexpected token '+'"), + array('Foreach: {foreach $list as max($i,1) => $e} {$e}, {/foreach} end', 'Fenom\Error\CompileException', "Unexpected token 'max'"), + array('Foreach: {foreach $list as max($e,1)} {$e}, {/foreach} end', 'Fenom\Error\CompileException', "Unexpected token 'max'"), + array('Foreach: {foreach $list => $e} {$e}, {/foreach} end', 'Fenom\Error\CompileException', "Unexpected token '=>'"), + array('Foreach: {foreach $list $k => $e} {$e}, {/foreach} end', 'Fenom\Error\CompileException', "Unexpected token '\$k'"), + array('Foreach: {foreach $list as $k =>} {$e}, {/foreach} end', 'Fenom\Error\CompileException', "Unexpected end of expression"), + array('Foreach: {foreach last=$l $list as $e } {$e}, {/foreach} end', 'Fenom\Error\CompileException', "Unexpected token 'last' in tag {foreach}"), + array('Foreach: {foreach $list as $e unknown=1} {$e}, {/foreach} end', 'Fenom\Error\CompileException', "Unknown parameter 'unknown'"), + array('Foreach: {foreach $list as $e index=$i+1} {$e}, {/foreach} end', 'Fenom\Error\CompileException', "Unexpected token '+'"), + array('Foreach: {foreach $list as $e first=$f+1} {$e}, {/foreach} end', 'Fenom\Error\CompileException', "Unexpected token '+'"), + array('Foreach: {foreach $list as $e last=$l+1} {$e}, {/foreach} end', 'Fenom\Error\CompileException', "Unexpected token '+'"), + array('Foreach: {foreach $list as $e index=max($i,1)} {$e}, {/foreach} end', 'Fenom\Error\CompileException', "Unexpected token 'max'"), + array('Foreach: {foreach $list as $e first=max($i,1)} {$e}, {/foreach} end', 'Fenom\Error\CompileException', "Unexpected token 'max'"), + array('Foreach: {foreach $list as $e last=max($i,1)} {$e}, {/foreach} end', 'Fenom\Error\CompileException', "Unexpected token 'max'"), + array('Foreach: {foreach $list as $e} {$e}, {foreachelse} {break} {/foreach} end', 'Fenom\Error\CompileException', "Improper usage of the tag {break}"), + array('Foreach: {foreach $list as $e} {$e}, {foreachelse} {continue} {/foreach} end', 'Fenom\Error\CompileException', "Improper usage of the tag {continue}"), ); } @@ -505,9 +505,9 @@ class TemplateTest extends TestCase public static function providerSwitchInvalid() { return array( - array('Switch: {switch}{case 1} one {break}{/switch} end', 'Fenom\CompileException', "Unexpected end of expression"), - array('Switch: {switch 1}{case} one {break}{/switch} end', 'Fenom\CompileException', "Unexpected end of expression"), - array('Switch: {switch 1}{break}{case} one {/switch} end', 'Fenom\CompileException', "Improper usage of the tag {break}"), + array('Switch: {switch}{case 1} one {break}{/switch} end', 'Fenom\Error\CompileException', "Unexpected end of expression"), + array('Switch: {switch 1}{case} one {break}{/switch} end', 'Fenom\Error\CompileException', "Unexpected end of expression"), + array('Switch: {switch 1}{break}{case} one {/switch} end', 'Fenom\Error\CompileException', "Improper usage of the tag {break}"), ); } @@ -525,7 +525,7 @@ class TemplateTest extends TestCase public static function providerWhileInvalid() { return array( - array('While: {while} block {/while} end', 'Fenom\CompileException', "Unexpected end of expression"), + array('While: {while} block {/while} end', 'Fenom\Error\CompileException', "Unexpected end of expression"), ); } @@ -555,30 +555,30 @@ class TemplateTest extends TestCase public static function providerForInvalid() { return array( - array('For: {for} block1 {/for} end', 'Fenom\CompileException', "Unexpected end of expression"), - array('For: {for $a=} block1 {/for} end', 'Fenom\CompileException', "Unexpected end of expression"), - array('For: {for $a+1=3 to=6} block1 {/for} end', 'Fenom\CompileException', "Unexpected token '+'"), - array('For: {for max($a,$b)=3 to=6} block1 {/for} end', 'Fenom\CompileException', "Unexpected token 'max'"), - array('For: {for to=6 $a=3} block1 {/for} end', 'Fenom\CompileException', "Unexpected token 'to'"), - array('For: {for index=$i $a=3 to=6} block1 {/for} end', 'Fenom\CompileException', "Unexpected token 'index'"), - array('For: {for first=$i $a=3 to=6} block1 {/for} end', 'Fenom\CompileException', "Unexpected token 'first'"), - array('For: {for last=$i $a=3 to=6} block1 {/for} end', 'Fenom\CompileException', "Unexpected token 'last'"), - array('For: {for $a=4 to=6 unk=4} block1 {/for} end', 'Fenom\CompileException', "Unknown parameter 'unk'"), - array('For: {for $a=4 to=6} $a: {$a}, {forelse} {break} {/for} end', 'Fenom\CompileException', "Improper usage of the tag {break}"), - array('For: {for $a=4 to=6} $a: {$a}, {forelse} {continue} {/for} end', 'Fenom\CompileException', "Improper usage of the tag {continue}"), + array('For: {for} block1 {/for} end', 'Fenom\Error\CompileException', "Unexpected end of expression"), + array('For: {for $a=} block1 {/for} end', 'Fenom\Error\CompileException', "Unexpected end of expression"), + array('For: {for $a+1=3 to=6} block1 {/for} end', 'Fenom\Error\CompileException', "Unexpected token '+'"), + array('For: {for max($a,$b)=3 to=6} block1 {/for} end', 'Fenom\Error\CompileException', "Unexpected token 'max'"), + array('For: {for to=6 $a=3} block1 {/for} end', 'Fenom\Error\CompileException', "Unexpected token 'to'"), + array('For: {for index=$i $a=3 to=6} block1 {/for} end', 'Fenom\Error\CompileException', "Unexpected token 'index'"), + 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} $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}"), ); } public static function providerLayersInvalid() { return array( - array('Layers: {foreach $list as $e} block1 {if 1} {foreachelse} {/if} {/foreach} end', 'Fenom\CompileException', "Unexpected tag 'foreachelse' (this tag can be used with 'foreach')"), - array('Layers: {foreach $list as $e} block1 {if 1} {/foreach} {/if} end', 'Fenom\CompileException', "Unexpected closing of the tag 'foreach'"), - array('Layers: {for $a=4 to=6} block1 {if 1} {forelse} {/if} {/for} end', 'Fenom\CompileException', "Unexpected tag 'forelse' (this tag can be used with 'for')"), - array('Layers: {for $a=4 to=6} block1 {if 1} {/for} {/if} end', 'Fenom\CompileException', "Unexpected closing of the tag 'for'"), - array('Layers: {switch 1} {if 1} {case 1} {/if} {/switch} end', 'Fenom\CompileException', "Unexpected tag 'case' (this tag can be used with 'switch')"), - array('Layers: {/switch} end', 'Fenom\CompileException', "Unexpected closing of the tag 'switch'"), - array('Layers: {if 1} end', 'Fenom\CompileException', "Unclosed tag: {if}"), + array('Layers: {foreach $list as $e} block1 {if 1} {foreachelse} {/if} {/foreach} end', 'Fenom\Error\CompileException', "Unexpected tag 'foreachelse' (this tag can be used with 'foreach')"), + array('Layers: {foreach $list as $e} block1 {if 1} {/foreach} {/if} end', 'Fenom\Error\CompileException', "Unexpected closing of the tag 'foreach'"), + array('Layers: {for $a=4 to=6} block1 {if 1} {forelse} {/if} {/for} end', 'Fenom\Error\CompileException', "Unexpected tag 'forelse' (this tag can be used with 'for')"), + array('Layers: {for $a=4 to=6} block1 {if 1} {/for} {/if} end', 'Fenom\Error\CompileException', "Unexpected closing of the tag 'for'"), + array('Layers: {switch 1} {if 1} {case 1} {/if} {/switch} end', 'Fenom\Error\CompileException', "Unexpected tag 'case' (this tag can be used with 'switch')"), + array('Layers: {/switch} end', 'Fenom\Error\CompileException', "Unexpected closing of the tag 'switch'"), + array('Layers: {if 1} end', 'Fenom\Error\CompileException', "Unclosed tag: {if}"), ); } diff --git a/tests/cases/Fenom/TokenizerTest.php b/tests/cases/Fenom/TokenizerTest.php index af170ed..d5a3512 100644 --- a/tests/cases/Fenom/TokenizerTest.php +++ b/tests/cases/Fenom/TokenizerTest.php @@ -57,7 +57,7 @@ class TokenizerTest extends \PHPUnit_Framework_TestCase try { $tokens->skip(T_STRING)->skip('(')->skip(':'); } catch (\Exception $e) { - $this->assertInstanceOf('Fenom\UnexpectedTokenException', $e); + $this->assertInstanceOf('Fenom\Error\UnexpectedTokenException', $e); $this->assertStringStartsWith("Unexpected token '3' in expression, expect ':'", $e->getMessage()); } $this->assertTrue($tokens->valid()); From e68f3dd99a51fc835a05000caf6ac0bebc9a2b4a Mon Sep 17 00:00:00 2001 From: bzick Date: Mon, 29 Jul 2013 16:32:54 +0400 Subject: [PATCH 10/18] Disable sandbox --- tests/cases/Fenom/MacrosTest.php | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/cases/Fenom/MacrosTest.php b/tests/cases/Fenom/MacrosTest.php index d919174..42e36c6 100644 --- a/tests/cases/Fenom/MacrosTest.php +++ b/tests/cases/Fenom/MacrosTest.php @@ -54,17 +54,17 @@ class MacrosTest extends TestCase {macro.factorial num=10}'); } - public function _testSandbox() - { - try { - $this->fenom->compile("macro_recursive.tpl"); - $this->fenom->flush(); - var_dump($this->fenom->fetch("macro_recursive.tpl", [])); - } catch (\Exception $e) { - var_dump($e->getMessage() . ": " . $e->getTraceAsString()); - } - exit; - } +// public function _testSandbox() +// { +// try { +// $this->fenom->compile("macro_recursive.tpl"); +// $this->fenom->flush(); +// var_dump($this->fenom->fetch("macro_recursive.tpl", [])); +// } catch (\Exception $e) { +// var_dump($e->getMessage() . ": " . $e->getTraceAsString()); +// } +// exit; +// } public function testMacros() { From f1d252a3ccdc56257c8141361a4daa023b35606d Mon Sep 17 00:00:00 2001 From: bzick Date: Thu, 1 Aug 2013 01:05:19 +0400 Subject: [PATCH 11/18] Add filters for templates (#30) --- src/Fenom.php | 86 ++++++++++++++++++++++---------- src/Fenom/Template.php | 25 +++++++--- tests/cases/Fenom/MacrosTest.php | 4 +- tests/cases/FenomTest.php | 20 ++++++++ 4 files changed, 99 insertions(+), 36 deletions(-) diff --git a/src/Fenom.php b/src/Fenom.php index bdaae07..6ff7393 100644 --- a/src/Fenom.php +++ b/src/Fenom.php @@ -7,8 +7,8 @@ * For the full copyright and license information, please view the license.md * file that was distributed with this source code. */ -use Fenom\Template, - Fenom\ProviderInterface; +use Fenom\ProviderInterface; +use Fenom\Template; /** * Fenom Template Engine @@ -67,10 +67,26 @@ class Fenom "disable_statics" => self::DENY_STATICS, ); + /** + * @var callable[] + */ + public $pre_filters = array(); + + /** + * @var callable[] + */ + public $filters = array(); + + /** + * @var callable[] + */ + public $post_filters = array(); + /** * @var Fenom\Render[] Templates storage */ protected $_storage = array(); + /** * @var string compile directory */ @@ -81,10 +97,6 @@ class Fenom */ protected $_options = 0; - protected $_on_pre_cmp = array(); - protected $_on_cmp = array(); - protected $_on_post_cmp = array(); - /** * @var ProviderInterface */ @@ -98,28 +110,28 @@ class Fenom * @var string[] list of modifiers [modifier_name => callable] */ protected $_modifiers = array( - "upper" => 'strtoupper', - "up" => 'strtoupper', - "lower" => 'strtolower', - "low" => 'strtolower', + "upper" => 'strtoupper', + "up" => 'strtoupper', + "lower" => 'strtolower', + "low" => 'strtolower', "date_format" => 'Fenom\Modifier::dateFormat', - "date" => 'Fenom\Modifier::date', - "truncate" => 'Fenom\Modifier::truncate', - "escape" => 'Fenom\Modifier::escape', - "e" => 'Fenom\Modifier::escape', // alias of escape - "unescape" => 'Fenom\Modifier::unescape', - "strip" => 'Fenom\Modifier::strip', - "length" => 'Fenom\Modifier::length', - "iterable" => 'Fenom\Modifier::isIterable' + "date" => 'Fenom\Modifier::date', + "truncate" => 'Fenom\Modifier::truncate', + "escape" => 'Fenom\Modifier::escape', + "e" => 'Fenom\Modifier::escape', // alias of escape + "unescape" => 'Fenom\Modifier::unescape', + "strip" => 'Fenom\Modifier::strip', + "length" => 'Fenom\Modifier::length', + "iterable" => 'Fenom\Modifier::isIterable' ); /** * @var array of allowed PHP functions */ protected $_allowed_funcs = array( - "count" => 1, "is_string" => 1, "is_array" => 1, "is_numeric" => 1, "is_int" => 1, - "is_object" => 1, "strtotime" => 1, "gettype" => 1, "is_double" => 1, "json_encode" => 1, "json_decode" => 1, - "ip2long" => 1, "long2ip" => 1, "strip_tags" => 1, "nl2br" => 1, "explode" => 1, "implode" => 1 + "count" => 1, "is_string" => 1, "is_array" => 1, "is_numeric" => 1, "is_int" => 1, + "is_object" => 1, "strtotime" => 1, "gettype" => 1, "is_double" => 1, "json_encode" => 1, "json_decode" => 1, + "ip2long" => 1, "long2ip" => 1, "strip_tags" => 1, "nl2br" => 1, "explode" => 1, "implode" => 1 ); /** @@ -292,27 +304,47 @@ class Fenom /** * * @param callable $cb + * @return self */ - public function addPreCompileFilter($cb) + public function addPreFilter($cb) { - $this->_on_pre_cmp[] = $cb; + $this->pre_filters[] = $cb; + return $this; + } + + public function getPreFilters() { + return $this->pre_filters; } /** * * @param callable $cb + * @return self */ - public function addPostCompileFilter($cb) + public function addPostFilter($cb) { - $this->_on_post_cmp[] = $cb; + $this->post_filters[] = $cb; + return $this; + } + + + public function getPostFilters() { + return $this->post_filters; } /** * @param callable $cb + * @return self */ - public function addCompileFilter($cb) + public function addFilter($cb) { - $this->_on_cmp[] = $cb; + $this->filters[] = $cb; + return $this; + } + + + public function getFilters() { + return $this->filters; } /** diff --git a/src/Fenom/Template.php b/src/Fenom/Template.php index 63efc36..3dc6293 100644 --- a/src/Fenom/Template.php +++ b/src/Fenom/Template.php @@ -8,8 +8,8 @@ * file that was distributed with this source code. */ namespace Fenom; -use Fenom, - Fenom\Error\UnexpectedTokenException; +use Fenom; +use Fenom\Error\UnexpectedTokenException; use Fenom\Error\CompileException; use Fenom\Error\InvalidUsageException; use Fenom\Error\SecurityException; @@ -97,7 +97,7 @@ class Template extends Render private $_before; - private $_filter = array(); + private $_filters = array(); private static $_checkers = array( 'integer' => 'is_int(%s)', @@ -136,6 +136,7 @@ class Template extends Render { $this->_fenom = $fenom; $this->_options = $options; + $this->_filters = $this->_fenom->getFilters(); } /** @@ -196,6 +197,9 @@ 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); + } while (($start = strpos($this->_src, '{', $pos)) !== false) { // search open-symbol of tags switch ($this->_src[$start + 1]) { // check next character @@ -275,11 +279,15 @@ class Template extends Render } } $this->addDepend($this); // for 'verify' performance + foreach($this->_fenom->getPostFilters() as $filter) { + $this->_body = call_user_func($filter, $this->_body, $this); + } } /** - * Execute some code in loading cache + * Execute some code at loading cache * @param $code + * @return void */ public function before($code) { @@ -303,15 +311,18 @@ class Template extends Render private function _appendText($text) { $this->_line += substr_count($text, "\n"); - if ($this->_filter) { + if ($this->_filters) { if (strpos($text, "_filters as $filter) { + $text = call_user_func($filter, $text, $this); + } $this->_body .= $text; } else { $fragments = explode("_filter as $filter) { - $fragment = call_user_func($filter, $fragment); + foreach ($this->_filters as $filter) { + $fragment = call_user_func($filter, $fragment, $this); } } } diff --git a/tests/cases/Fenom/MacrosTest.php b/tests/cases/Fenom/MacrosTest.php index 42e36c6..ded112f 100644 --- a/tests/cases/Fenom/MacrosTest.php +++ b/tests/cases/Fenom/MacrosTest.php @@ -47,7 +47,7 @@ class MacrosTest extends TestCase $this->tpl("macro_recursive.tpl", '{macro factorial(num)} {if $num} - {$num} {macro.factorial num=$num-1} + {$num} {macro.factorial num=$num-1} {$num} {/if} {/macro} @@ -104,6 +104,6 @@ class MacrosTest extends TestCase $this->fenom->compile('macro_recursive.tpl'); $this->fenom->flush(); $tpl = $this->fenom->getTemplate('macro_recursive.tpl'); - $this->assertSame("10 9 8 7 6 5 4 3 2 1", Modifier::strip($tpl->fetch(array()), true)); + $this->assertSame("10 9 8 7 6 5 4 3 2 1 1 2 3 4 5 6 7 8 9 10", Modifier::strip($tpl->fetch(array()), true)); } } diff --git a/tests/cases/FenomTest.php b/tests/cases/FenomTest.php index 4c49846..0a1a4bd 100644 --- a/tests/cases/FenomTest.php +++ b/tests/cases/FenomTest.php @@ -114,4 +114,24 @@ class FenomTest extends \Fenom\TestCase // printf("remove %010b from option %010b, flags %010b\n", $option, $this->fenom->getOptions(), $flags & ~$option); // $this->assertSame($this->fenom->getOptions(), $flags & ~$option); } + + public function testFilter() { + $punit = $this; + $this->fenom->addPreFilter(function ($src, $tpl) use ($punit) { + $this->assertInstanceOf('Fenom\Template', $tpl); + return "== $src =="; + }); + + $this->fenom->addPostFilter(function ($code, $tpl) use ($punit) { + $this->assertInstanceOf('Fenom\Template', $tpl); + return "+++ $code +++"; + }); + + $this->fenom->addFilter(function ($text, $tpl) use ($punit) { + $this->assertInstanceOf('Fenom\Template', $tpl); + return "|--- $text ---|"; + }); + + $this->assertSame('+++ |--- == hello ---||--- world == ---| +++', $this->fenom->compileCode('hello {var $user} god {/var} world')->fetch(array())); + } } \ No newline at end of file From c27df815457011cd524c050c5424ef53429a0786 Mon Sep 17 00:00:00 2001 From: bzick Date: Fri, 2 Aug 2013 20:29:18 +0400 Subject: [PATCH 12/18] Split parseExp (for #3) --- src/Fenom/Compiler.php | 9 + src/Fenom/Template.php | 268 +++++++++++++++++------------ src/Fenom/Tokenizer.php | 2 +- tests/cases/Fenom/TemplateTest.php | 7 +- 4 files changed, 168 insertions(+), 118 deletions(-) diff --git a/src/Fenom/Compiler.php b/src/Fenom/Compiler.php index f20f5dc..1ebb623 100644 --- a/src/Fenom/Compiler.php +++ b/src/Fenom/Compiler.php @@ -32,6 +32,15 @@ class Compiler */ public static function tagInclude(Tokenizer $tokens, Template $tpl) { + $name = false; +// if($tokens->is('[')) { +// $tokens->next(); +// if(!$name && $tokens->is(T_CONSTANT_ENCAPSED_STRING)) { +// if($tpl->getStorage()->templateExists($_name = substr($tokens->getAndNext(), 1, -1))) { +// $name = $_name; +// } +// } +// } $cname = $tpl->parsePlainArg($tokens, $name); $p = $tpl->parseParams($tokens); if ($p) { // if we have additionally variables diff --git a/src/Fenom/Template.php b/src/Fenom/Template.php index 3dc6293..ce8248d 100644 --- a/src/Fenom/Template.php +++ b/src/Fenom/Template.php @@ -603,135 +603,165 @@ class Template extends Render } /** - * Parse expressions. The mix of math operations, boolean operations, scalars, arrays and variables. + * Parse expressions. The mix of operations and terms. * - * @static * @param Tokenizer $tokens * @param bool $required - * @throws TokenizeException - * @throws UnexpectedTokenException - * @throws \Exception * @return string + * @throws Error\UnexpectedTokenException */ - public function parseExp(Tokenizer $tokens, $required = false) - { - $_exp = array(); // expression as PHP code - $term = false; // last item was variable or value. - // 0 - was operator, but trem required - // false - was operator or no one term - // true - was trem - // 1 - term is strict varaible - $cond = false; // last item was operator - while ($tokens->valid()) { - if (!$term && $tokens->is(Tokenizer::MACRO_SCALAR, '"', '`', T_ENCAPSED_AND_WHITESPACE)) { // like quoted string - $_exp[] = $this->parseScalar($tokens, true); - $term = true; - } elseif (!$term && $tokens->is(Tokenizer::MACRO_INCDEC)) { // like variable - $_exp[] = $this->parseVariable($tokens); - $term = true; - } elseif (!$term && $tokens->is(T_VARIABLE)) { // like variable too - $var = $this->parseVar($tokens); - if ($tokens->is(Tokenizer::MACRO_EQUALS)) { - $_exp[] = $var; - if ($tokens->isLast()) { - break; - } - $_exp[] = $tokens->getAndNext(); - $term = 0; - } elseif ($tokens->is(Tokenizer::MACRO_INCDEC, "|", "!", "?", '(')) { - $_exp[] = $this->parseVariable($tokens, 0, $var); - $term = true; - } else { - $_exp[] = $var; - $term = 1; - } - } elseif (!$term && $tokens->is("(")) { // open bracket - $tokens->next(); - $_exp[] = "(" . $this->parseExp($tokens, true) . ")"; - $tokens->get(")"); - $tokens->next(); - $term = 1; - } elseif ($tokens->is(T_STRING)) { - if ($term) { // parse 'in' or 'is' operators - if (!$_exp) { - break; - } - $operator = $tokens->current(); - if ($operator == "is") { - $item = array_pop($_exp); - $_exp[] = $this->parseIs($tokens, $item, $term === 1); - } elseif ($operator == "in" || ($operator == "not" && $tokens->isNextToken("in"))) { - $item = array_pop($_exp); - $_exp[] = $this->parseIn($tokens, $item, $term === 1); - } else { - break; - } - } else { // function or special value - if ($tokens->isSpecialVal()) { - $_exp[] = $tokens->getAndNext(); - } elseif ($tokens->isNext("(") && !$tokens->getWhitespace()) { - $func = $this->_fenom->getModifier($tokens->current(), $this); - if (!$func) { - throw new \Exception("Function " . $tokens->getAndNext() . " not found"); - } - $tokens->next(); - $func = $func . $this->parseArgs($tokens); - if ($tokens->is('|')) { - $_exp[] = $this->parseModifier($tokens, $func); - } else { - $_exp[] = $func; - } - } else { - break; - } - $term = true; - } - } elseif (!$term && $tokens->is(T_ISSET, T_EMPTY)) { // empty and isset operators - $func = $tokens->getAndNext(); - if ($tokens->is("(") && $tokens->isNext(T_VARIABLE)) { - $tokens->next(); - $_exp[] = $func . "(" . $this->parseVar($tokens) . ")"; - $tokens->need(')')->next(); - } else { - throw new TokenizeException("Unexpected token " . $tokens->getNext() . ", isset() and empty() accept only variables"); - } - $term = true; - } elseif (!$term && $tokens->is(Tokenizer::MACRO_UNARY)) { - if (!$tokens->isNext(T_VARIABLE, T_DNUMBER, T_LNUMBER, T_STRING, T_ISSET, T_EMPTY, '(')) { - break; - } - $_exp[] = $tokens->getAndNext(); - $term = 0; - } elseif ($tokens->is(Tokenizer::MACRO_BINARY)) { // like binary operator, see Tokenizer::MACRO_BINARY - if (!$term) { - throw new UnexpectedTokenException($tokens); - } - if ($tokens->isLast()) { - break; - } + public function parseExp(Tokenizer $tokens, $required = false) { + $exp = array(); + $var = false; // last term was: true - variable, false - mixed + $op = false; // last exp was operator + $cond = false; // was conditional operator + while($tokens->valid()) { + // parse term + $term = $this->parseTerm($tokens, $var); + if($term !== false) { + $exp[] = $term; + $op = false; + } else { + break; + } + + if(!$tokens->valid()) { + break; + } + + // parse operator + if($tokens->is(Tokenizer::MACRO_BINARY)) { if ($tokens->is(Tokenizer::MACRO_COND)) { if ($cond) { break; } $cond = true; - } elseif ($tokens->is(Tokenizer::MACRO_BOOLEAN)) { - $cond = false; } - $_exp[] = " " . $tokens->getAndNext() . " "; - $term = 0; - } elseif ($tokens->is('[')) { - $_exp[] = $this->parseArray($tokens); + $op = $tokens->getAndNext(); + } elseif($tokens->is(Tokenizer::MACRO_EQUALS)) { + if(!$var) { + break; + } + $op = $tokens->getAndNext(); + } elseif($tokens->is(T_STRING)) { + if (!$exp) { + break; + } + $operator = $tokens->current(); + if ($operator == "is") { + $item = array_pop($exp); + $exp[] = $this->parseIs($tokens, $item, $var); + } elseif ($operator == "in" || ($operator == "not" && $tokens->isNextToken("in"))) { + $item = array_pop($exp); + $exp[] = $this->parseIn($tokens, $item, $var); + } else { + break; + } + } elseif($tokens->is('~')) { + // string concat coming soon } else { break; } + if($op) { + $exp[] = $op; + } } - if ($term === 0) { + + if ($op) { throw new UnexpectedTokenException($tokens); } - if ($required && !$_exp) { + if ($required && !$exp) { throw new UnexpectedTokenException($tokens); } - return implode('', $_exp); + return implode(' ', $exp); + } + + /** + * Parse any term: -2, ++$var, 'adf'|mod:4 + * + * @param Tokenizer $tokens + * @param bool $is_var + * @return bool|string + * @throws Error\UnexpectedTokenException + * @throws Error\TokenizeException + * @throws \Exception + */ + public function parseTerm(Tokenizer $tokens, &$is_var = false) { + $is_var = false; + $unary = ""; + term: { + if($tokens->is(T_LNUMBER, T_DNUMBER)) { + return $unary.$this->parseScalar($tokens, true); + } elseif($tokens->is(T_CONSTANT_ENCAPSED_STRING, '"', T_ENCAPSED_AND_WHITESPACE)) { + if($unary) { + throw new UnexpectedTokenException($tokens->back()); + } + return $this->parseScalar($tokens, true); + } elseif($tokens->is(T_VARIABLE)) { + $var = $this->parseVar($tokens); + if ($tokens->is(Tokenizer::MACRO_INCDEC, "|", "!", "?")) { + return $unary.$this->parseVariable($tokens, 0, $var); + } elseif($tokens->is("(") && $tokens->hasBackList(T_STRING)) { // method call + return $unary.$this->parseVariable($tokens, 0, $var); + } elseif($unary) { + return $unary.$var; + } else { + $is_var = true; + return $var; + } + } elseif($tokens->is(Tokenizer::MACRO_INCDEC)) { + return $unary.$this->parseVariable($tokens); + } elseif($tokens->is("(")) { + $tokens->next(); + $exp = $unary."(" . $this->parseExp($tokens, true).")"; + $tokens->need(")")->next(); + return $exp; + } elseif($tokens->is(Tokenizer::MACRO_UNARY)) { + if($unary) { + throw new UnexpectedTokenException($tokens); + } + $unary = $tokens->getAndNext(); + goto term; + } elseif($tokens->is(T_STRING)) { + if ($tokens->isSpecialVal()) { + return $unary.$tokens->getAndNext(); + } elseif ($tokens->isNext("(") && !$tokens->getWhitespace()) { + $func = $this->_fenom->getModifier($tokens->current(), $this); + if (!$func) { + throw new \Exception("Function " . $tokens->getAndNext() . " not found"); + } + $tokens->next(); + $func = $func . $this->parseArgs($tokens); + if ($tokens->is('|')) { + return $unary.$this->parseModifier($tokens, $func); + } else { + return $unary.$func; + } + } else { + return false; + } + } elseif($tokens->is(T_ISSET, T_EMPTY)) { + $func = $tokens->getAndNext(); + if ($tokens->is("(") && $tokens->isNext(T_VARIABLE)) { + $tokens->next(); + $exp = $func . "(" . $this->parseVar($tokens) . ")"; + $tokens->need(')')->next(); + return $unary.$exp; + } else { + throw new TokenizeException("Unexpected token " . $tokens->getNext() . ", isset() and empty() accept only variables"); + } + } elseif($tokens->is('[')) { + if($unary) { + throw new UnexpectedTokenException($tokens->back()); + } + return $this->parseArray($tokens); + } elseif($unary) { + $tokens->back(); + throw new UnexpectedTokenException($tokens); + } else { + return false; + } + } } /** @@ -1031,7 +1061,7 @@ class Template extends Render break; case T_ENCAPSED_AND_WHITESPACE: case '"': - $_scalar .= $this->parseSubstr($tokens); + $_scalar .= $this->parseQuote($tokens); break; default: throw new TokenizeException("Unexpected scalar token '" . $tokens->current() . "'"); @@ -1050,7 +1080,7 @@ class Template extends Render * @throws UnexpectedTokenException * @return string */ - public function parseSubstr(Tokenizer $tokens) + public function parseQuote(Tokenizer $tokens) { if ($tokens->is('"', "`")) { $stop = $tokens->current(); @@ -1108,6 +1138,16 @@ class Template extends Render } } + /** + * @param Tokenizer $tokens + * @param null $first_member + */ + public function parseConcat(Tokenizer $tokens, $first_member = null) { + $concat = array(); + if($first_member) { + } + } + /** * Parse modifiers * |modifier:1:2.3:'string':false:$var:(4+5*$var3)|modifier2:"str {$var+3} ing":$arr.item @@ -1137,7 +1177,7 @@ class Template extends Render } elseif ($tokens->is(T_VARIABLE)) { $args[] = $this->parseVariable($tokens, self::DENY_MODS); } elseif ($tokens->is('"', '`', T_ENCAPSED_AND_WHITESPACE)) { - $args[] = $this->parseSubstr($tokens); + $args[] = $this->parseQuote($tokens); } elseif ($tokens->is('(')) { $args[] = $this->parseExp($tokens, true); } elseif ($tokens->is('[')) { @@ -1187,7 +1227,7 @@ class Template extends Render $key = false; $val = true; } elseif ($tokens->is('"') && !$val) { - $_arr .= $this->parseSubstr($tokens); + $_arr .= $this->parseQuote($tokens); $key = false; $val = true; } elseif ($tokens->is(T_DOUBLE_ARROW) && $val) { diff --git a/src/Fenom/Tokenizer.php b/src/Fenom/Tokenizer.php index 210b18b..abea858 100644 --- a/src/Fenom/Tokenizer.php +++ b/src/Fenom/Tokenizer.php @@ -328,7 +328,7 @@ class Tokenizer * @return mixed * @throws UnexpectedTokenException */ - public function getAndNext() + public function getAndNext(/* $token1, ... */) { if ($this->curr) { $cur = $this->curr[1]; diff --git a/tests/cases/Fenom/TemplateTest.php b/tests/cases/Fenom/TemplateTest.php index 64f8e39..8ea7e12 100644 --- a/tests/cases/Fenom/TemplateTest.php +++ b/tests/cases/Fenom/TemplateTest.php @@ -186,9 +186,10 @@ class TemplateTest extends TestCase { return array( array('If: {-"hi"} end', 'Fenom\Error\CompileException', "Unexpected token '-'"), + array('If: {-[1,2]} end', 'Fenom\Error\CompileException', "Unexpected token '-'"), array('If: {($a++)++} end', 'Fenom\Error\CompileException', "Unexpected token '++'"), array('If: {$a + * $c} end', 'Fenom\Error\CompileException', "Unexpected token '*'"), - array('If: {$a + } end', 'Fenom\Error\CompileException', "Unexpected token '+'"), + array('If: {$a + } end', 'Fenom\Error\CompileException', "Unexpected end of expression"), array('If: {$a + =} end', 'Fenom\Error\CompileException', "Unexpected token '='"), array('If: {$a + 1 =} end', 'Fenom\Error\CompileException', "Unexpected token '='"), array('If: {$a + 1 = 6} end', 'Fenom\Error\CompileException', "Unexpected token '='"), @@ -334,7 +335,7 @@ class TemplateTest extends TestCase array('Create: {var $v = $a|upper++} Result: {$v} end', 'Fenom\Error\CompileException', "Unexpected token '++'"), array('Create: {var $v = max($a,2)++} Result: {$v} end', 'Fenom\Error\CompileException', "Unexpected token '++'"), array('Create: {var $v = max($a,2)} Result: {$v} end', 'Fenom\Error\CompileException', "Function max not found", Fenom::DENY_NATIVE_FUNCS), - array('Create: {var $v = 4*} Result: {$v} end', 'Fenom\Error\CompileException', "Unexpected token '*'"), + array('Create: {var $v = 4*} Result: {$v} end', 'Fenom\Error\CompileException', "Unexpected end of expression"), array('Create: {var $v = ""$a} Result: {$v} end', 'Fenom\Error\CompileException', "Unexpected token '\$a'"), array('Create: {var $v = [1,2} Result: {$v} end', 'Fenom\Error\CompileException', "Unexpected end of expression"), array('Create: {var $v = empty(2)} Result: {$v} end', 'Fenom\Error\CompileException', "Unexpected token 2, isset() and empty() accept only variables"), @@ -672,7 +673,7 @@ class TemplateTest extends TestCase public function _testSandbox() { try { - var_dump($this->fenom->compileCode('{$one.two->three[e]()}')->getBody()); + var_dump($this->fenom->compileCode('{if 0 is empty} block1 {else} block2 {/if}')->getBody()); } catch (\Exception $e) { print_r($e->getMessage() . "\n" . $e->getTraceAsString()); } From e51eb9f3d851fe1dc0aaec67a62c0d3f0675d986 Mon Sep 17 00:00:00 2001 From: bzick Date: Fri, 2 Aug 2013 21:50:04 +0400 Subject: [PATCH 13/18] Format code --- src/Fenom.php | 39 +++++---- src/Fenom/Template.php | 175 +++++++++++++++++++------------------- src/Fenom/Tokenizer.php | 2 +- tests/cases/FenomTest.php | 3 +- 4 files changed, 113 insertions(+), 106 deletions(-) diff --git a/src/Fenom.php b/src/Fenom.php index 6ff7393..bb1af3b 100644 --- a/src/Fenom.php +++ b/src/Fenom.php @@ -110,28 +110,28 @@ class Fenom * @var string[] list of modifiers [modifier_name => callable] */ protected $_modifiers = array( - "upper" => 'strtoupper', - "up" => 'strtoupper', - "lower" => 'strtolower', - "low" => 'strtolower', + "upper" => 'strtoupper', + "up" => 'strtoupper', + "lower" => 'strtolower', + "low" => 'strtolower', "date_format" => 'Fenom\Modifier::dateFormat', - "date" => 'Fenom\Modifier::date', - "truncate" => 'Fenom\Modifier::truncate', - "escape" => 'Fenom\Modifier::escape', - "e" => 'Fenom\Modifier::escape', // alias of escape - "unescape" => 'Fenom\Modifier::unescape', - "strip" => 'Fenom\Modifier::strip', - "length" => 'Fenom\Modifier::length', - "iterable" => 'Fenom\Modifier::isIterable' + "date" => 'Fenom\Modifier::date', + "truncate" => 'Fenom\Modifier::truncate', + "escape" => 'Fenom\Modifier::escape', + "e" => 'Fenom\Modifier::escape', // alias of escape + "unescape" => 'Fenom\Modifier::unescape', + "strip" => 'Fenom\Modifier::strip', + "length" => 'Fenom\Modifier::length', + "iterable" => 'Fenom\Modifier::isIterable' ); /** * @var array of allowed PHP functions */ protected $_allowed_funcs = array( - "count" => 1, "is_string" => 1, "is_array" => 1, "is_numeric" => 1, "is_int" => 1, - "is_object" => 1, "strtotime" => 1, "gettype" => 1, "is_double" => 1, "json_encode" => 1, "json_decode" => 1, - "ip2long" => 1, "long2ip" => 1, "strip_tags" => 1, "nl2br" => 1, "explode" => 1, "implode" => 1 + "count" => 1, "is_string" => 1, "is_array" => 1, "is_numeric" => 1, "is_int" => 1, + "is_object" => 1, "strtotime" => 1, "gettype" => 1, "is_double" => 1, "json_encode" => 1, "json_decode" => 1, + "ip2long" => 1, "long2ip" => 1, "strip_tags" => 1, "nl2br" => 1, "explode" => 1, "implode" => 1 ); /** @@ -312,7 +312,8 @@ class Fenom return $this; } - public function getPreFilters() { + public function getPreFilters() + { return $this->pre_filters; } @@ -328,7 +329,8 @@ class Fenom } - public function getPostFilters() { + public function getPostFilters() + { return $this->post_filters; } @@ -343,7 +345,8 @@ class Fenom } - public function getFilters() { + public function getFilters() + { return $this->filters; } diff --git a/src/Fenom/Template.php b/src/Fenom/Template.php index ce8248d..c585e75 100644 --- a/src/Fenom/Template.php +++ b/src/Fenom/Template.php @@ -197,7 +197,7 @@ class Template extends Render { $end = $pos = 0; $this->escape = $this->_options & Fenom::AUTO_ESCAPE; - foreach($this->_fenom->getPreFilters() as $filter) { + foreach ($this->_fenom->getPreFilters() as $filter) { $this->_src = call_user_func($filter, $this->_src, $this); } @@ -279,7 +279,7 @@ class Template extends Render } } $this->addDepend($this); // for 'verify' performance - foreach($this->_fenom->getPostFilters() as $filter) { + foreach ($this->_fenom->getPostFilters() as $filter) { $this->_body = call_user_func($filter, $this->_body, $this); } } @@ -610,27 +610,28 @@ class Template extends Render * @return string * @throws Error\UnexpectedTokenException */ - public function parseExp(Tokenizer $tokens, $required = false) { - $exp = array(); - $var = false; // last term was: true - variable, false - mixed - $op = false; // last exp was operator + public function parseExp(Tokenizer $tokens, $required = false) + { + $exp = array(); + $var = false; // last term was: true - variable, false - mixed + $op = false; // last exp was operator $cond = false; // was conditional operator - while($tokens->valid()) { + while ($tokens->valid()) { // parse term $term = $this->parseTerm($tokens, $var); - if($term !== false) { + if ($term !== false) { $exp[] = $term; $op = false; } else { break; } - if(!$tokens->valid()) { + if (!$tokens->valid()) { break; } // parse operator - if($tokens->is(Tokenizer::MACRO_BINARY)) { + if ($tokens->is(Tokenizer::MACRO_BINARY)) { if ($tokens->is(Tokenizer::MACRO_COND)) { if ($cond) { break; @@ -638,12 +639,12 @@ class Template extends Render $cond = true; } $op = $tokens->getAndNext(); - } elseif($tokens->is(Tokenizer::MACRO_EQUALS)) { - if(!$var) { + } elseif ($tokens->is(Tokenizer::MACRO_EQUALS)) { + if (!$var) { break; } $op = $tokens->getAndNext(); - } elseif($tokens->is(T_STRING)) { + } elseif ($tokens->is(T_STRING)) { if (!$exp) { break; } @@ -657,12 +658,12 @@ class Template extends Render } else { break; } - } elseif($tokens->is('~')) { + } elseif ($tokens->is('~')) { // string concat coming soon } else { break; } - if($op) { + if ($op) { $exp[] = $op; } } @@ -686,83 +687,84 @@ class Template extends Render * @throws Error\TokenizeException * @throws \Exception */ - public function parseTerm(Tokenizer $tokens, &$is_var = false) { + public function parseTerm(Tokenizer $tokens, &$is_var = false) + { $is_var = false; - $unary = ""; + $unary = ""; term: { - if($tokens->is(T_LNUMBER, T_DNUMBER)) { - return $unary.$this->parseScalar($tokens, true); - } elseif($tokens->is(T_CONSTANT_ENCAPSED_STRING, '"', T_ENCAPSED_AND_WHITESPACE)) { - if($unary) { - throw new UnexpectedTokenException($tokens->back()); - } - return $this->parseScalar($tokens, true); - } elseif($tokens->is(T_VARIABLE)) { - $var = $this->parseVar($tokens); - if ($tokens->is(Tokenizer::MACRO_INCDEC, "|", "!", "?")) { - return $unary.$this->parseVariable($tokens, 0, $var); - } elseif($tokens->is("(") && $tokens->hasBackList(T_STRING)) { // method call - return $unary.$this->parseVariable($tokens, 0, $var); - } elseif($unary) { - return $unary.$var; - } else { - $is_var = true; - return $var; - } - } elseif($tokens->is(Tokenizer::MACRO_INCDEC)) { - return $unary.$this->parseVariable($tokens); - } elseif($tokens->is("(")) { - $tokens->next(); - $exp = $unary."(" . $this->parseExp($tokens, true).")"; - $tokens->need(")")->next(); - return $exp; - } elseif($tokens->is(Tokenizer::MACRO_UNARY)) { - if($unary) { - throw new UnexpectedTokenException($tokens); - } - $unary = $tokens->getAndNext(); - goto term; - } elseif($tokens->is(T_STRING)) { - if ($tokens->isSpecialVal()) { - return $unary.$tokens->getAndNext(); - } elseif ($tokens->isNext("(") && !$tokens->getWhitespace()) { - $func = $this->_fenom->getModifier($tokens->current(), $this); - if (!$func) { - throw new \Exception("Function " . $tokens->getAndNext() . " not found"); - } - $tokens->next(); - $func = $func . $this->parseArgs($tokens); - if ($tokens->is('|')) { - return $unary.$this->parseModifier($tokens, $func); - } else { - return $unary.$func; - } - } else { - return false; - } - } elseif($tokens->is(T_ISSET, T_EMPTY)) { - $func = $tokens->getAndNext(); - if ($tokens->is("(") && $tokens->isNext(T_VARIABLE)) { - $tokens->next(); - $exp = $func . "(" . $this->parseVar($tokens) . ")"; - $tokens->need(')')->next(); - return $unary.$exp; - } else { - throw new TokenizeException("Unexpected token " . $tokens->getNext() . ", isset() and empty() accept only variables"); - } - } elseif($tokens->is('[')) { - if($unary) { - throw new UnexpectedTokenException($tokens->back()); - } - return $this->parseArray($tokens); - } elseif($unary) { - $tokens->back(); + if ($tokens->is(T_LNUMBER, T_DNUMBER)) { + return $unary . $this->parseScalar($tokens, true); + } elseif ($tokens->is(T_CONSTANT_ENCAPSED_STRING, '"', T_ENCAPSED_AND_WHITESPACE)) { + if ($unary) { + throw new UnexpectedTokenException($tokens->back()); + } + return $this->parseScalar($tokens, true); + } elseif ($tokens->is(T_VARIABLE)) { + $var = $this->parseVar($tokens); + if ($tokens->is(Tokenizer::MACRO_INCDEC, "|", "!", "?")) { + return $unary . $this->parseVariable($tokens, 0, $var); + } elseif ($tokens->is("(") && $tokens->hasBackList(T_STRING)) { // method call + return $unary . $this->parseVariable($tokens, 0, $var); + } elseif ($unary) { + return $unary . $var; + } else { + $is_var = true; + return $var; + } + } elseif ($tokens->is(Tokenizer::MACRO_INCDEC)) { + return $unary . $this->parseVariable($tokens); + } elseif ($tokens->is("(")) { + $tokens->next(); + $exp = $unary . "(" . $this->parseExp($tokens, true) . ")"; + $tokens->need(")")->next(); + return $exp; + } elseif ($tokens->is(Tokenizer::MACRO_UNARY)) { + if ($unary) { throw new UnexpectedTokenException($tokens); + } + $unary = $tokens->getAndNext(); + goto term; + } elseif ($tokens->is(T_STRING)) { + if ($tokens->isSpecialVal()) { + return $unary . $tokens->getAndNext(); + } elseif ($tokens->isNext("(") && !$tokens->getWhitespace()) { + $func = $this->_fenom->getModifier($tokens->current(), $this); + if (!$func) { + throw new \Exception("Function " . $tokens->getAndNext() . " not found"); + } + $tokens->next(); + $func = $func . $this->parseArgs($tokens); + if ($tokens->is('|')) { + return $unary . $this->parseModifier($tokens, $func); + } else { + return $unary . $func; + } } else { return false; } + } elseif ($tokens->is(T_ISSET, T_EMPTY)) { + $func = $tokens->getAndNext(); + if ($tokens->is("(") && $tokens->isNext(T_VARIABLE)) { + $tokens->next(); + $exp = $func . "(" . $this->parseVar($tokens) . ")"; + $tokens->need(')')->next(); + return $unary . $exp; + } else { + throw new TokenizeException("Unexpected token " . $tokens->getNext() . ", isset() and empty() accept only variables"); + } + } elseif ($tokens->is('[')) { + if ($unary) { + throw new UnexpectedTokenException($tokens->back()); + } + return $this->parseArray($tokens); + } elseif ($unary) { + $tokens->back(); + throw new UnexpectedTokenException($tokens); + } else { + return false; } } + } /** * Parse simple variable (without modifier etc) @@ -1142,9 +1144,10 @@ class Template extends Render * @param Tokenizer $tokens * @param null $first_member */ - public function parseConcat(Tokenizer $tokens, $first_member = null) { + public function parseConcat(Tokenizer $tokens, $first_member = null) + { $concat = array(); - if($first_member) { + if ($first_member) { } } diff --git a/src/Fenom/Tokenizer.php b/src/Fenom/Tokenizer.php index abea858..1acf388 100644 --- a/src/Fenom/Tokenizer.php +++ b/src/Fenom/Tokenizer.php @@ -328,7 +328,7 @@ class Tokenizer * @return mixed * @throws UnexpectedTokenException */ - public function getAndNext(/* $token1, ... */) + public function getAndNext( /* $token1, ... */) { if ($this->curr) { $cur = $this->curr[1]; diff --git a/tests/cases/FenomTest.php b/tests/cases/FenomTest.php index 0a1a4bd..0d03978 100644 --- a/tests/cases/FenomTest.php +++ b/tests/cases/FenomTest.php @@ -115,7 +115,8 @@ class FenomTest extends \Fenom\TestCase // $this->assertSame($this->fenom->getOptions(), $flags & ~$option); } - public function testFilter() { + public function testFilter() + { $punit = $this; $this->fenom->addPreFilter(function ($src, $tpl) use ($punit) { $this->assertInstanceOf('Fenom\Template', $tpl); From 43925ae6400ed5712078b4b6da3e9e8f5aa56436 Mon Sep 17 00:00:00 2001 From: bzick Date: Fri, 2 Aug 2013 23:01:06 +0400 Subject: [PATCH 14/18] Fix ternary operator --- src/Fenom/Template.php | 4 +--- tests/cases/Fenom/TemplateTest.php | 6 +++++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Fenom/Template.php b/src/Fenom/Template.php index c585e75..924dc48 100644 --- a/src/Fenom/Template.php +++ b/src/Fenom/Template.php @@ -907,9 +907,7 @@ class Template extends Render } } else { $expr1 = $this->parseExp($tokens, true); - if (!$tokens->is(":")) { - throw new UnexpectedTokenException($tokens, null, "ternary operator"); - } + $tokens->need(':')->skip(); $expr2 = $this->parseExp($tokens, true); if ($empty) { return '(empty(' . $var . ') ? ' . $expr2 . ' : ' . $expr1 . ')'; diff --git a/tests/cases/Fenom/TemplateTest.php b/tests/cases/Fenom/TemplateTest.php index 8ea7e12..eefe174 100644 --- a/tests/cases/Fenom/TemplateTest.php +++ b/tests/cases/Fenom/TemplateTest.php @@ -404,7 +404,11 @@ class TemplateTest extends TestCase array('{if $nonempty.double!} right {/if}', $a), array('{if $nonempty.bool!} right {/if}', $a), // ! ... : ... + array('{$unexists ! "no way" : "right"}', $a), + array('{$a ! "right" : "no way"}', $a), // !: ... + array('{$unexists !: "right"}', $a), + array('{$a !: "right"}', $a, '1'), ); } @@ -673,7 +677,7 @@ class TemplateTest extends TestCase public function _testSandbox() { try { - var_dump($this->fenom->compileCode('{if 0 is empty} block1 {else} block2 {/if}')->getBody()); + var_dump($this->fenom->compileCode('{$a!"no way":"right"}')->getBody()); } catch (\Exception $e) { print_r($e->getMessage() . "\n" . $e->getTraceAsString()); } From 1e7cf29290bb3fbd8dc76a1e9af8aa954570c991 Mon Sep 17 00:00:00 2001 From: bzick Date: Fri, 2 Aug 2013 23:30:44 +0400 Subject: [PATCH 15/18] Tests++ --- tests/cases/Fenom/TemplateTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/cases/Fenom/TemplateTest.php b/tests/cases/Fenom/TemplateTest.php index eefe174..4df1a58 100644 --- a/tests/cases/Fenom/TemplateTest.php +++ b/tests/cases/Fenom/TemplateTest.php @@ -389,6 +389,8 @@ class TemplateTest extends TestCase array('{$empty.bool?:"empty"}', $a, "empty"), array('{$empty.unexist?:"empty"}', $a, "empty"), // ? ... : .... + array('{$unexists ? "no way" : "right"}', $a), + array('{$a ? "right" : "no way"}', $a), // ! array('{if $a!} right {/if}', $a), array('{if $unexists!} no way {else} right {/if}', $a), From 09f952686b146f5a86ff3bb301b9397e04780182 Mon Sep 17 00:00:00 2001 From: bzick Date: Sat, 3 Aug 2013 18:56:17 +0400 Subject: [PATCH 16/18] Add string concatenation (#3) --- docs/operators.md | 4 + src/Fenom/Template.php | 157 +++++++++++++++-------------- tests/TestCase.php | 10 +- tests/cases/Fenom/TemplateTest.php | 20 +++- 4 files changed, 113 insertions(+), 78 deletions(-) diff --git a/docs/operators.md b/docs/operators.md index ad264a4..c0dd526 100644 --- a/docs/operators.md +++ b/docs/operators.md @@ -81,6 +81,10 @@ Operators * `--$a` - decrement the variable and use it * `$a--` - use the variable and decrement it +### String operator + +* `$a ~ $b` - return concatenation of variables `$a` and `$b` + ### Ternary operator * `$a ? $b : $c` - returns `$b` if `$a` is not empty, and `$c` otherwise diff --git a/src/Fenom/Template.php b/src/Fenom/Template.php index 924dc48..8d6c16e 100644 --- a/src/Fenom/Template.php +++ b/src/Fenom/Template.php @@ -615,36 +615,34 @@ class Template extends Render $exp = array(); $var = false; // last term was: true - variable, false - mixed $op = false; // last exp was operator - $cond = false; // was conditional operator + $cond = false; // was comparison operator while ($tokens->valid()) { // parse term - $term = $this->parseTerm($tokens, $var); + $term = $this->parseTerm($tokens, $var); // term of the expression if ($term !== false) { $exp[] = $term; $op = false; } else { break; } - if (!$tokens->valid()) { break; } - // parse operator - if ($tokens->is(Tokenizer::MACRO_BINARY)) { - if ($tokens->is(Tokenizer::MACRO_COND)) { + if ($tokens->is(Tokenizer::MACRO_BINARY)) { // binary operator: $a + $b, $a <= $b, ... + if ($tokens->is(Tokenizer::MACRO_COND)) { // comparison operator if ($cond) { break; } $cond = true; } $op = $tokens->getAndNext(); - } elseif ($tokens->is(Tokenizer::MACRO_EQUALS)) { + } elseif ($tokens->is(Tokenizer::MACRO_EQUALS)) { // assignment operator: $a = 4, $a += 3, ... if (!$var) { break; } $op = $tokens->getAndNext(); - } elseif ($tokens->is(T_STRING)) { + } elseif ($tokens->is(T_STRING)) { // test or containment operator: $a in $b, $a is set, ... if (!$exp) { break; } @@ -658,8 +656,17 @@ class Template extends Render } else { break; } - } elseif ($tokens->is('~')) { - // string concat coming soon + } elseif ($tokens->is('~')) { // string concatenation operator: 'asd' ~ $var + $concat = array(array_pop($exp)); + while($tokens->is('~')) { + $tokens->next(); + if($tokens->is(T_LNUMBER, T_DNUMBER)) { + $concat[] = "strval(".$this->parseTerm($tokens).")"; + } else { + $concat[] = $this->parseTerm($tokens); + } + } + $exp[] = "(".implode(".", $concat).")"; } else { break; } @@ -692,79 +699,79 @@ class Template extends Render $is_var = false; $unary = ""; term: { - if ($tokens->is(T_LNUMBER, T_DNUMBER)) { - return $unary . $this->parseScalar($tokens, true); - } elseif ($tokens->is(T_CONSTANT_ENCAPSED_STRING, '"', T_ENCAPSED_AND_WHITESPACE)) { - if ($unary) { - throw new UnexpectedTokenException($tokens->back()); - } - return $this->parseScalar($tokens, true); - } elseif ($tokens->is(T_VARIABLE)) { - $var = $this->parseVar($tokens); - if ($tokens->is(Tokenizer::MACRO_INCDEC, "|", "!", "?")) { - return $unary . $this->parseVariable($tokens, 0, $var); - } elseif ($tokens->is("(") && $tokens->hasBackList(T_STRING)) { // method call - return $unary . $this->parseVariable($tokens, 0, $var); - } elseif ($unary) { - return $unary . $var; - } else { - $is_var = true; - return $var; - } - } elseif ($tokens->is(Tokenizer::MACRO_INCDEC)) { - return $unary . $this->parseVariable($tokens); - } elseif ($tokens->is("(")) { - $tokens->next(); - $exp = $unary . "(" . $this->parseExp($tokens, true) . ")"; - $tokens->need(")")->next(); - return $exp; - } elseif ($tokens->is(Tokenizer::MACRO_UNARY)) { - if ($unary) { - throw new UnexpectedTokenException($tokens); - } - $unary = $tokens->getAndNext(); - goto term; - } elseif ($tokens->is(T_STRING)) { - if ($tokens->isSpecialVal()) { - return $unary . $tokens->getAndNext(); - } elseif ($tokens->isNext("(") && !$tokens->getWhitespace()) { - $func = $this->_fenom->getModifier($tokens->current(), $this); - if (!$func) { - throw new \Exception("Function " . $tokens->getAndNext() . " not found"); + if ($tokens->is(T_LNUMBER, T_DNUMBER)) { + return $unary . $this->parseScalar($tokens, true); + } elseif ($tokens->is(T_CONSTANT_ENCAPSED_STRING, '"', T_ENCAPSED_AND_WHITESPACE)) { + if ($unary) { + throw new UnexpectedTokenException($tokens->back()); } - $tokens->next(); - $func = $func . $this->parseArgs($tokens); - if ($tokens->is('|')) { - return $unary . $this->parseModifier($tokens, $func); + return $this->parseScalar($tokens, true); + } elseif ($tokens->is(T_VARIABLE)) { + $var = $this->parseVar($tokens); + if ($tokens->is(Tokenizer::MACRO_INCDEC, "|", "!", "?")) { + return $unary . $this->parseVariable($tokens, 0, $var); + } elseif ($tokens->is("(") && $tokens->hasBackList(T_STRING)) { // method call + return $unary . $this->parseVariable($tokens, 0, $var); + } elseif ($unary) { + return $unary . $var; } else { - return $unary . $func; + $is_var = true; + return $var; } + } elseif ($tokens->is(Tokenizer::MACRO_INCDEC)) { + return $unary . $this->parseVariable($tokens); + } elseif ($tokens->is("(")) { + $tokens->next(); + $exp = $unary . "(" . $this->parseExp($tokens, true) . ")"; + $tokens->need(")")->next(); + return $exp; + } elseif ($tokens->is(Tokenizer::MACRO_UNARY)) { + if ($unary) { + throw new UnexpectedTokenException($tokens); + } + $unary = $tokens->getAndNext(); + goto term; + } elseif ($tokens->is(T_STRING)) { + if ($tokens->isSpecialVal()) { + return $unary . $tokens->getAndNext(); + } elseif ($tokens->isNext("(") && !$tokens->getWhitespace()) { + $func = $this->_fenom->getModifier($tokens->current(), $this); + if (!$func) { + throw new \Exception("Function " . $tokens->getAndNext() . " not found"); + } + $tokens->next(); + $func = $func . $this->parseArgs($tokens); + if ($tokens->is('|')) { + return $unary . $this->parseModifier($tokens, $func); + } else { + return $unary . $func; + } + } else { + return false; + } + } elseif ($tokens->is(T_ISSET, T_EMPTY)) { + $func = $tokens->getAndNext(); + if ($tokens->is("(") && $tokens->isNext(T_VARIABLE)) { + $tokens->next(); + $exp = $func . "(" . $this->parseVar($tokens) . ")"; + $tokens->need(')')->next(); + return $unary . $exp; + } else { + throw new TokenizeException("Unexpected token " . $tokens->getNext() . ", isset() and empty() accept only variables"); + } + } elseif ($tokens->is('[')) { + if ($unary) { + throw new UnexpectedTokenException($tokens->back()); + } + return $this->parseArray($tokens); + } elseif ($unary) { + $tokens->back(); + throw new UnexpectedTokenException($tokens); } else { return false; } - } elseif ($tokens->is(T_ISSET, T_EMPTY)) { - $func = $tokens->getAndNext(); - if ($tokens->is("(") && $tokens->isNext(T_VARIABLE)) { - $tokens->next(); - $exp = $func . "(" . $this->parseVar($tokens) . ")"; - $tokens->need(')')->next(); - return $unary . $exp; - } else { - throw new TokenizeException("Unexpected token " . $tokens->getNext() . ", isset() and empty() accept only variables"); - } - } elseif ($tokens->is('[')) { - if ($unary) { - throw new UnexpectedTokenException($tokens->back()); - } - return $this->parseArray($tokens); - } elseif ($unary) { - $tokens->back(); - throw new UnexpectedTokenException($tokens); - } else { - return false; } } - } /** * Parse simple variable (without modifier etc) diff --git a/tests/TestCase.php b/tests/TestCase.php index 4883d3e..8d4b56f 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -34,7 +34,9 @@ class TestCase extends \PHPUnit_Framework_TestCase "obj" => new \StdClass, "list" => array( "a" => 1, - "b" => 2 + "one" => 1, + "b" => 2, + "two" => 2 ), 0 => "empty value", 1 => "one value", @@ -204,7 +206,11 @@ class TestCase extends \PHPUnit_Framework_TestCase public function providerVariables() { - return array(); + return array( + array('$one', 1), + array('$list.one', 1), + array('$list[$$.DEVELOP]', 1), + ); } public static function providerObjects() diff --git a/tests/cases/Fenom/TemplateTest.php b/tests/cases/Fenom/TemplateTest.php index 4df1a58..c439331 100644 --- a/tests/cases/Fenom/TemplateTest.php +++ b/tests/cases/Fenom/TemplateTest.php @@ -676,10 +676,20 @@ class TemplateTest extends TestCase ); } + public static function providerConcat() { + return array( + array('{"string" ~ $one ~ up("end")}', "string1END"), + array('{"string" ~ $one++ ~ "end"}', "string1end"), + array('{"string" ~ ++$one ~ "end"}', "string2end"), + array('{"string" ~ "one" ~ "end"}', "stringoneend"), + array('{"string" ~ 1 ~ "end"}', "string1end"), + ); + } + public function _testSandbox() { try { - var_dump($this->fenom->compileCode('{$a!"no way":"right"}')->getBody()); + var_dump($this->fenom->compileCode('{$a++~"hi"~time("Y:m:d")}')->getBody()); } catch (\Exception $e) { print_r($e->getMessage() . "\n" . $e->getTraceAsString()); } @@ -891,5 +901,13 @@ class TemplateTest extends TestCase { $this->exec($code, self::getVars(), $result); } + + /** + * @dataProvider providerConcat + */ + public function testConcat($code, $result) + { + $this->exec($code, self::getVars(), $result); + } } From c7a90789de42f841b7e79d40342d6698176d61e2 Mon Sep 17 00:00:00 2001 From: bzick Date: Mon, 5 Aug 2013 13:07:16 +0400 Subject: [PATCH 17/18] Done #28 --- CHANGELOG.md | 8 +++++++ src/Fenom/Compiler.php | 23 +++++-------------- src/Fenom/Render.php | 13 ++++++++++- src/Fenom/Template.php | 39 +++++++++++++++++++++----------- tests/cases/Fenom/MacrosTest.php | 19 ++++++++-------- 5 files changed, 62 insertions(+), 40 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ad40e2..35a2e85 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,8 +3,16 @@ CHANGELOG ## 1.2.0 +- Feature #28: macros may be called recursively - Feature #29: add {unset} tag - Add hook for loading modifiers and tags +- Add hook for loading modifiers and tags +- Feature #3: Add string operator '~' +- Improve parsers: parserExp, parserVar, parserVariable, parserMacro +- Fix ternary bug +- Bugs-- +- Tests++ +- Docs++ ## 1.1.0 diff --git a/src/Fenom/Compiler.php b/src/Fenom/Compiler.php index 1ebb623..904e5ee 100644 --- a/src/Fenom/Compiler.php +++ b/src/Fenom/Compiler.php @@ -873,7 +873,7 @@ class Compiler public static function macroOpen(Tokenizer $tokens, Scope $scope) { $scope["name"] = $tokens->get(Tokenizer::MACRO_STRING); - $scope["recursive"] = array(); + $scope["recursive"] = false; $args = array(); $defaults = array(); if (!$tokens->valid()) { @@ -897,10 +897,11 @@ class Compiler } $tokens->skipIf(')'); $scope["macro"] = array( - "id" => $scope->tpl->i++, + "name" => $scope["name"], "args" => $args, "defaults" => $defaults, - "body" => "" + "body" => "", + "recursive" => false ); return; } @@ -912,21 +913,9 @@ class Compiler public static function macroClose(Tokenizer $tokens, Scope $scope) { if ($scope["recursive"]) { - $switch = "switch(\$call['mark']) {\n"; - foreach ($scope["recursive"] as $mark) { - $switch .= "case $mark: goto macro_$mark;\n"; - } - $switch .= "}"; - $stack = '$stack_' . $scope["macro"]['id']; - $scope["macro"]["body"] = '' . $scope->cutContent() . ''; - } else { - $scope["macro"]["body"] = $scope->cutContent(); + $scope["macro"]["recursive"] = true; } + $scope["macro"]["body"] = $scope->cutContent(); $scope->tpl->macros[$scope["name"]] = $scope["macro"]; } diff --git a/src/Fenom/Render.php b/src/Fenom/Render.php index 15a8b1d..f01bd53 100644 --- a/src/Fenom/Render.php +++ b/src/Fenom/Render.php @@ -21,7 +21,8 @@ class Render extends \ArrayObject "base_name" => "", "scm" => false, "time" => 0, - "depends" => array() + "depends" => array(), + "macros" => array() ); /** * @var \Closure @@ -81,6 +82,7 @@ class Render extends \ArrayObject $this->_scm = $props["scm"]; $this->_time = $props["time"]; $this->_depends = $props["depends"]; + $this->_macros = $props["macros"]; $this->_code = $code; } @@ -183,6 +185,15 @@ class Render extends \ArrayObject return true; } + /** + * Get internal macro + * @param $name + * @return mixed + */ + public function getMacro($name) { + return $this->_macros[$name]; + } + /** * Execute template and write into output * @param array $values for template diff --git a/src/Fenom/Template.php b/src/Fenom/Template.php index 8d6c16e..0f22787 100644 --- a/src/Fenom/Template.php +++ b/src/Fenom/Template.php @@ -397,18 +397,31 @@ class Template extends Render */ public function getTemplateCode() { + + if($this->macros) { + $macros = array(); + foreach($this->macros as $m) { + if($m["recursive"]) { + $macros[] = "\t\t'".$m["name"]."' => function (\$tpl) {\n?>".$m["body"]."_before ? $this->_before . "\n" : ""; return "_name . "' compiled at " . date('Y-m-d H:i:s') . " */\n" . $before . // some code 'before' template - "return new Fenom\\Render(\$fenom, " . $this->_getClosureSource() . ", " . var_export(array( - "options" => $this->_options, - "provider" => $this->_scm, - "name" => $this->_name, - "base_name" => $this->_base_name, - "time" => $this->_time, - "depends" => $this->_depends - ), true) . ");\n"; + "return new Fenom\\Render(\$fenom, " . $this->_getClosureSource() . ", array(\n". + "\t'options' => {$this->_options},\n". + "\t'provider' => ".var_export($this->_scm, true).",\n". + "\t'name' => ".var_export($this->_name, true).",\n". + "\t'base_name' => ".var_export($this->_base_name, true).",\n". + "\t'time' => {$this->_time},\n". + "\t'depends' => ".var_export($this->_base_name, true).",\n". + "\t'macros' => array({$macros}), + ));\n"; } /** @@ -1323,14 +1336,14 @@ class Template extends Render throw new InvalidUsageException("Macro '$name' require '$arg' argument"); } } - $args = $args ? '$tpl = ' . Compiler::toArray($args) . ';' : ''; + $n = $this->i++; if ($recursive) { - $n = $this->i++; - $recursive['recursive'][] = $n; - return '$stack_' . $macro['id'] . '[] = array("tpl" => $tpl, "mark" => ' . $n . '); ' . $args . ' goto macro_' . $macro['id'] . '; macro_' . $n . ':'; + $recursive['recursive'] = true; + $body = '$tpl->getMacro("'.$name.'")->__invoke($tpl);'; } else { - return '$_tpl = $tpl; ' . $args . ' ?>' . $macro["body"] . ''.$macro["body"].'exchangeArray(' . Compiler::toArray($args) . ');' . PHP_EOL . $body . PHP_EOL . '$tpl->exchangeArray($_tpl'.$n.'); unset($_tpl'.$n.');'; } /** diff --git a/tests/cases/Fenom/MacrosTest.php b/tests/cases/Fenom/MacrosTest.php index ded112f..7b5c32c 100644 --- a/tests/cases/Fenom/MacrosTest.php +++ b/tests/cases/Fenom/MacrosTest.php @@ -54,17 +54,18 @@ class MacrosTest extends TestCase {macro.factorial num=10}'); } -// public function _testSandbox() -// { -// try { -// $this->fenom->compile("macro_recursive.tpl"); + public function _testSandbox() + { + try { + $this->fenom->compile("macro_recursive.tpl"); // $this->fenom->flush(); // var_dump($this->fenom->fetch("macro_recursive.tpl", [])); -// } catch (\Exception $e) { -// var_dump($e->getMessage() . ": " . $e->getTraceAsString()); -// } -// exit; -// } + var_dump( $this->fenom->compile("macro_recursive.tpl")->getTemplateCode()); + } catch (\Exception $e) { + var_dump($e->getMessage() . ": " . $e->getTraceAsString()); + } + exit; + } public function testMacros() { From b632dc5bdf2926eec101d521339958d63a193fe5 Mon Sep 17 00:00:00 2001 From: bzick Date: Mon, 5 Aug 2013 19:26:06 +0400 Subject: [PATCH 18/18] Fix link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2993bfc..af16c28 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Fenom - Template Engine for PHP * Simple * [Flexible](./docs/ext/extensions.md) * [Lightweight](./docs/benchmark.md#stats) -* [Powerful](./docs/main.md) +* [Powerful](./docs/readme.md) * Easy to use: Simple template