diff --git a/README.md b/README.md index fb1a3ad..f4c540a 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ -Fenom - awesome template engine for PHP -========================== +Fenom - Template Engine for PHP +=============================== -> Composer package: `{"bzick/fenom": "dev-master"}`. See on [Packagist.org](https://packagist.org/packages/bzick/fenom) +> 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) -## [About](./docs/about.md) :: [Documentation](./docs/main.md) :: [Benchmark](./docs/benchmark.md) :: [Articles](./docs/articles.md) +## [Usage](./docs/usage.md) :: [Documentation](./docs/main.md) :: [Benchmark](./docs/benchmark.md) :: [Articles](./docs/articles.md) -* Simplest known [syntax](./docs/syntax.md) +* Simple [syntax](./docs/syntax.md) * [Fast](./docs/benchmark.md) * [Secure](./docs/settings.md) * [Simple](./ideology.md) diff --git a/benchmark/run.php b/benchmark/run.php index e18c00f..6f5d6e0 100644 --- a/benchmark/run.php +++ b/benchmark/run.php @@ -14,27 +14,27 @@ echo "Testing a lot output...\n"; Benchmark::runs("smarty3", 'echo/smarty.tpl', __DIR__.'/templates/echo/data.json'); Benchmark::runs("twig", 'echo/twig.tpl', __DIR__.'/templates/echo/data.json'); Benchmark::runs("fenom", 'echo/smarty.tpl', __DIR__.'/templates/echo/data.json'); -if(extension_loaded("phalcon")) { - Benchmark::runs("volt", 'echo/twig.tpl', __DIR__.'/templates/echo/data.json'); -} +//if(extension_loaded("phalcon")) { +// Benchmark::runs("volt", 'echo/twig.tpl', __DIR__.'/templates/echo/data.json'); +//} echo "\nTesting 'foreach' of big array...\n"; Benchmark::runs("smarty3", 'foreach/smarty.tpl', __DIR__.'/templates/foreach/data.json'); Benchmark::runs("twig", 'foreach/twig.tpl', __DIR__.'/templates/foreach/data.json'); Benchmark::runs("fenom", 'foreach/smarty.tpl', __DIR__.'/templates/foreach/data.json'); -if(extension_loaded("phalcon")) { - Benchmark::runs("volt", 'foreach/twig.tpl', __DIR__.'/templates/foreach/data.json'); -} +//if(extension_loaded("phalcon")) { +// Benchmark::runs("volt", 'foreach/twig.tpl', __DIR__.'/templates/foreach/data.json'); +//} echo "\nTesting deep 'inheritance'...\n"; Benchmark::runs("smarty3", 'inheritance/smarty/b100.tpl', __DIR__.'/templates/foreach/data.json'); Benchmark::runs("twig", 'inheritance/twig/b100.tpl', __DIR__.'/templates/foreach/data.json'); Benchmark::runs("fenom", 'inheritance/smarty/b100.tpl', __DIR__.'/templates/foreach/data.json'); -if(extension_loaded("phalcon")) { - Benchmark::runs("volt", 'inheritance/twig/b100.tpl', __DIR__.'/templates/foreach/data.json'); -} +//if(extension_loaded("phalcon")) { +// Benchmark::runs("volt", 'inheritance/twig/b100.tpl', __DIR__.'/templates/foreach/data.json'); +//} echo "\nDone. Cleanup.\n"; //passthru("rm -rf ".__DIR__."/compile/*"); diff --git a/benchmark/scripts/bootstrap.php b/benchmark/scripts/bootstrap.php index cc914b7..174ffe9 100644 --- a/benchmark/scripts/bootstrap.php +++ b/benchmark/scripts/bootstrap.php @@ -77,7 +77,7 @@ class Benchmark { } public static function run($engine, $template, $data, $double, $message) { - passthru(sprintf("php -dmemory_limit=512M -dxdebug.max_nesting_level=1024 %s/run.php --engine '%s' --template '%s' --data '%s' --message '%s' %s", __DIR__, $engine, $template, $data, $message, $double ? '--double' : '')); + passthru(sprintf(PHP_BINARY." -dmemory_limit=512M -dxdebug.max_nesting_level=1024 %s/run.php --engine '%s' --template '%s' --data '%s' --message '%s' %s", __DIR__, $engine, $template, $data, $message, $double ? '--double' : '')); } /** diff --git a/composer.json b/composer.json index e461f79..5cc9d88 100644 --- a/composer.json +++ b/composer.json @@ -1,5 +1,5 @@ { - "name": "bzick/fenom", + "name": "fenom/fenom", "type": "library", "description": "Fenom - fast template engine for PHP", "homepage": "http://bzick.github.io/fenom/", diff --git a/docs/about.md b/docs/about.md deleted file mode 100644 index fa4c1b8..0000000 --- a/docs/about.md +++ /dev/null @@ -1,11 +0,0 @@ -About Fenom [RU] -================ - -Fenom - самый быстрый, гибкий и тонкий шаблонизатор для PHP, унаследовавший синтаксис от Smarty3 и улучшив его. -Пожалуй это единственный шаблонизатор, который не использет ни регулярные выражения, как Twig, ни лексер от BISON, как Smarty3. -Вы не найдёте ни одного регулярного выражения в ядре Fenom, но тем не менее ядро простое, компактное и очень быстрое. - -* Скорость. Разбор шаблонов постоен на основе нативного [токенайзера](http://docs.php.net/tokenizer). Шаблон преобразуется в исполняемый PHP код, - который может быть закеширован на файловой системе. -* Безопасность. Разборщик шаблон кроптоливо проверяет каждый токен, тем самым не пропуская возможные уязвимоти и фатальные ошибки при исполнении шпблона. -* Гибкость. Любой компонент можно переопределить, по желанию. \ No newline at end of file diff --git a/docs/install.md b/docs/install.md index 892162c..b22f5c1 100644 --- a/docs/install.md +++ b/docs/install.md @@ -5,11 +5,11 @@ For installation use [composer](http://getcompoer.org). Add in your `composer.js ```json { "require": { - "bzick/fenom": "dev-master" + "fenom/fenom": "dev-master" } } ``` or use shell -`composer require bzick/fenom` +`composer require fenom/fenom` If you do not use composer - use `psr-0` format for loading Fenom's classes. diff --git a/docs/operators.md b/docs/operators.md index 9a47f33..58cb463 100644 --- a/docs/operators.md +++ b/docs/operators.md @@ -1,22 +1,67 @@ Operators ========= -Math +### Math -`+ - / *` +Operators: `+ - / *` -Bitwize +```smarty +{$a + $b * $c/$d - $e*5 + 1e3} +``` -`| & << >> |= &= <<= >>=` +### Boolean -Unary +Operators: `|| && and or < > <= >= == === !== !=` -`^ ~ - !` +```smarty +{if $a && $b >= 5 && $c != 3} {/if} +``` -Boolean +### Bitwize -`|| && and or < > <= >= == === !== !=` +Operators: `| & << >> |= &= <<= >>=` -Ternar +```smarty +{if $a & 1} {var $b |= $flags} {/if} +``` -`? :` +### Unary + +Operators: `^ ~ - !` + +```smarty +{var $b |= $flags & ^$c} +``` + +### Ternar + +Operators: `? :` + +```smarty +{var $a = true} +{$a ? 5 : 10} {* outputs 5 *} +{var $a = false} +{$a ? 5 : 10} {* outputs 10 *} +``` + +### Variable operator + +Checking variable value +```smarty +{if $a?} {* instead of {if !empty($a)} *} +``` + +Checking variable existence +```smarty +{if $a!} {* instead of {if isset($a)} *} +``` + +Get default if variable is empty +```smarty +{$a?:"some text"} {* instead of {if empty($a) ? "some text" : $a} *} +``` + +Get default if variable doesn't exist +```smarty +{$a!:"some text"} {* instead of {if isset($a) ? $a : "some text"} *} +``` diff --git a/docs/settings.md b/docs/settings.md index d71c63f..6ea4f02 100644 --- a/docs/settings.md +++ b/docs/settings.md @@ -25,6 +25,7 @@ $fenom->setOptions($options); * **auto_reload**, `Fenom::AUTO_RELOAD`, пересобирать шаблон если его оригинал был изменён (замедляет работу шаблонизатора). * **force_compile**, `Fenom::FORCE_COMPILE`, пересобирать шаблон при каждом вызове (сильно замедляет работу шаблонизатора). * **force_include**, `Fenom::FORCE_INCLUDE`, оптимизировать вставку шаблона в шаблон. Это увеличит производительность и размер собранного шаблона. +Опция активируется если имя шаблона задано явно и скалярно. ```php $fenom->setOptions(array( diff --git a/docs/usage.md b/docs/usage.md index 6e65c41..a05d99c 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -1,20 +1,38 @@ Basic usage =========== -### Creating template engine +### Initialize Fenom +Use factory method ```php -$fenom = Fenom::factory('/path/to/templates', '/path/to/template/cache', $options); +$fenom = Fenom::factory('/path/to/templates', '/path/to/compiled/template', $options); +``` -//or - -$fenom = new Fenom(new FSProvider('/path/to/templates')); +Use `new` operator +```php +$fenom = new Fenom(new Provider('/path/to/templates')); $fenom->setCompileDir('/path/to/template/cache'); $fenom->setOptions($options); ``` -### Output template result +### Render template +Output template ```php $fenom->display("template/name.tpl", $vars); +``` + +Get template into the variable +```php +$result = $fenom->fetch("template/name.tpl", $vars); +``` + +Create pipe-line into callback +```php +$fenom->export( + "template/sitemap.tpl", + $vars, + $callback = [new SplFileObject("/tmp/sitemap.xml", "w"), "fwrite"], // pipe to file /tmp/sitemap.xml + $chunk_size = 1e6 // chunk size for callback +); ``` \ No newline at end of file diff --git a/src/Fenom/Compiler.php b/src/Fenom/Compiler.php index 764f3ab..51a65c0 100644 --- a/src/Fenom/Compiler.php +++ b/src/Fenom/Compiler.php @@ -23,7 +23,7 @@ class Compiler { * @static * @param Tokenizer $tokens * @param Template $tpl - * @throws ImproperUseException + * @throws InvalidUsageException * @return string */ public static function tagInclude(Tokenizer $tokens, Template $tpl) { @@ -68,12 +68,12 @@ class Compiler { * @static * @param Tokenizer $tokens * @param Scope $scope - * @throws ImproperUseException + * @throws InvalidUsageException * @return string */ public static function tagElseIf(Tokenizer $tokens, Scope $scope) { if($scope["else"]) { - throw new ImproperUseException('Incorrect use of the tag {elseif}'); + throw new InvalidUsageException('Incorrect use of the tag {elseif}'); } return '} elseif('.$scope->tpl->parseExp($tokens, true).') {'; } @@ -99,7 +99,7 @@ class Compiler { * @param Tokenizer $tokens * @param Scope $scope * @throws UnexpectedTokenException - * @throws ImproperUseException + * @throws InvalidUsageException * @return string */ public static function foreachOpen(Tokenizer $tokens, Scope $scope) { @@ -132,7 +132,7 @@ class Compiler { while($token = $tokens->key()) { $param = $tokens->get(T_STRING); if(!isset($p[ $param ])) { - throw new ImproperUseException("Unknown parameter '$param' in {foreach}"); + throw new InvalidUsageException("Unknown parameter '$param' in {foreach}"); } $tokens->getNext("="); $tokens->next(); @@ -197,7 +197,7 @@ class Compiler { * @param Tokenizer $tokens * @param Scope $scope * @return string - * @throws ImproperUseException + * @throws InvalidUsageException */ public static function forOpen(Tokenizer $tokens, Scope $scope) { $p = array("index" => false, "first" => false, "last" => false, "step" => 1, "to" => false, "max" => false, "min" => false); @@ -218,7 +218,7 @@ class Compiler { $condition = "$var >= {$p['to']}"; if($p["last"]) $c = "($var + {$p['step']}) < {$p['to']}"; } else { - throw new ImproperUseException("Invalid step value if {for}"); + throw new InvalidUsageException("Invalid step value if {for}"); } } else { $condition = "({$p['step']} > 0 && $var <= {$p['to']} || {$p['step']} < 0 && $var >= {$p['to']})"; @@ -323,14 +323,14 @@ class Compiler { * @static * @param Tokenizer $tokens * @param Scope $scope - * @throws ImproperUseException + * @throws InvalidUsageException * @return string */ public static function tagContinue($tokens, Scope $scope) { if(empty($scope["no-continue"])) { return 'continue;'; } else { - throw new ImproperUseException("Improper usage of the tag {continue}"); + throw new InvalidUsageException("Improper usage of the tag {continue}"); } } @@ -358,14 +358,14 @@ class Compiler { * @static * @param Tokenizer $tokens * @param Scope $scope - * @throws ImproperUseException + * @throws InvalidUsageException * @return string */ public static function tagBreak($tokens, Scope $scope) { if(empty($scope["no-break"])) { return 'break;'; } else { - throw new ImproperUseException("Improper usage of the tag {break}"); + throw new InvalidUsageException("Improper usage of the tag {break}"); } } @@ -373,12 +373,14 @@ class Compiler { * Dispatch {extends} tag * @param Tokenizer $tokens * @param Template $tpl - * @throws ImproperUseException + * @throws InvalidUsageException * @return string */ public static function tagExtends(Tokenizer $tokens, Template $tpl) { if(!empty($tpl->_extends)) { - throw new ImproperUseException("Only one {extends} allowed"); + throw new InvalidUsageException("Only one {extends} allowed"); + } elseif($tpl->getStackSize()) { + throw new InvalidUsageException("Tags {extends} can not be nested"); } $tpl_name = $tpl->parsePlainArg($tokens, $name); if(empty($tpl->_extended)) { @@ -446,10 +448,13 @@ class Compiler { * Tag {use ...} * @param Tokenizer $tokens * @param Template $tpl - * @throws ImproperUseException + * @throws InvalidUsageException * @return string */ 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) { $donor = $tpl->getStorage()->getRawTemplate()->load($name, false); @@ -469,11 +474,12 @@ class Compiler { $tpl->addDepend($donor); return '?>'.$donor->getBody().'_compatible = true; - return '$donor = $tpl->getStorage()->getTemplate('.$cname.', \Fenom\Template::EXTENDED);'.PHP_EOL. +// throw new InvalidUsageException('template name must be given explicitly yet'); + // under construction + $tpl->_compatible = true; + return '$donor = $tpl->getStorage()->getTemplate('.$cname.', \Fenom\Template::EXTENDED);'.PHP_EOL. '$donor->fetch((array)$tpl);'.PHP_EOL. '$tpl->b += (array)$donor->b'; -// throw new ImproperUseException('template name must be given explicitly'); } } @@ -482,9 +488,13 @@ class Compiler { * @param Tokenizer $tokens * @param Scope $scope * @return string - * @throws ImproperUseException + * @throws InvalidUsageException */ 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); $scope["name"] = $name; } @@ -553,7 +563,7 @@ class Compiler { public static function tagParent($tokens, Scope $scope) { if(empty($scope->tpl->_extends)) { - throw new ImproperUseException("Tag {parent} may be declared in childs"); + throw new InvalidUsageException("Tag {parent} may be declared in childs"); } } @@ -710,7 +720,7 @@ class Compiler { * @param Tokenizer $tokens * @param Template $tpl * @return string - * @throws ImproperUseException + * @throws InvalidUsageException */ public static function tagCycle(Tokenizer $tokens, Template $tpl) { if($tokens->is("[")) { @@ -721,7 +731,7 @@ class Compiler { if($tokens->valid()) { $p = $tpl->parseParams($tokens); if(empty($p["index"])) { - throw new ImproperUseException("Cycle may contain only index attribute"); + throw new InvalidUsageException("Cycle may contain only index attribute"); } else { return 'echo '.__CLASS__.'::cycle('.$exp.', '.$p["index"].')'; } @@ -747,7 +757,7 @@ class Compiler { * @param Tokenizer $tokens * @param Template $tpl * @throws UnexpectedTokenException - * @throws ImproperUseException + * @throws InvalidUsageException * @return string */ public static function tagImport(Tokenizer $tokens, Template $tpl) { @@ -775,7 +785,7 @@ class Compiler { $tpl->parsePlainArg($tokens, $name); if(!$name) { - throw new ImproperUseException("Invalid usage tag {import}"); + throw new InvalidUsageException("Invalid usage tag {import}"); } if($tokens->is(T_AS)) { $alias = $tokens->next()->get(Tokenizer::MACRO_STRING); @@ -812,7 +822,7 @@ class Compiler { * * @param Tokenizer $tokens * @param Scope $scope - * @throws ImproperUseException + * @throws InvalidUsageException */ public static function macroOpen(Tokenizer $tokens, Scope $scope) { $scope["name"] = $tokens->get(Tokenizer::MACRO_STRING); @@ -832,7 +842,7 @@ class Compiler { if($tokens->is(T_CONSTANT_ENCAPSED_STRING, T_LNUMBER, T_DNUMBER) || $tokens->isSpecialVal()) { $scope["defaults"][ $param ] = $tokens->getAndNext(); } else { - throw new ImproperUseException("Macro parameters may have only scalar defaults"); + throw new InvalidUsageException("Macro parameters may have only scalar defaults"); } } $tokens->skipIf(','); diff --git a/src/Fenom/Template.php b/src/Fenom/Template.php index e01ae0c..4707462 100644 --- a/src/Fenom/Template.php +++ b/src/Fenom/Template.php @@ -106,6 +106,14 @@ class Template extends Render { $this->_options = $options; } + /** + * Get tag stack size + * @return int + */ + public function getStackSize() { + return count($this->_stack); + } + /** * Load source from provider * @param string $name @@ -408,7 +416,7 @@ class Template extends Render { } else { return $code = "echo ".$this->parseExp($tokens).";"; } - } catch (ImproperUseException $e) { + } catch (InvalidUsageException $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); @@ -937,7 +945,7 @@ class Template extends Render { * * @param Tokenizer $tokens * @return string - * @throws ImproperUseException + * @throws InvalidUsageException */ public function parseConst(Tokenizer $tokens) { $tokens->get('#'); @@ -956,7 +964,7 @@ class Template extends Render { if(defined($name)) { return $name; } else { - throw new ImproperUseException("Use undefined constant $name"); + throw new InvalidUsageException("Use undefined constant $name"); } } @@ -964,7 +972,7 @@ class Template extends Render { * @param Tokenizer $tokens * @param $name * @return string - * @throws ImproperUseException + * @throws InvalidUsageException */ public function parseMacro(Tokenizer $tokens, $name) { if(isset($this->macros[ $name ])) { @@ -977,13 +985,13 @@ class Template extends Render { } elseif(isset($macro["defaults"][ $arg ])) { $args[ $arg ] = $macro["defaults"][ $arg ]; } else { - throw new ImproperUseException("Macro '$name' require '$arg' argument"); + throw new InvalidUsageException("Macro '$name' require '$arg' argument"); } } $args = $args ? '$tpl = '.Compiler::toArray($args).';' : ''; return '$_tpl = $tpl; '.$args.' ?>'.$macro["body"].'