diff --git a/README.md b/README.md index 5217cd6..4c461ca 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Cytro - awesome template engine for PHP > Composer package: `{"bzick/cytro": "dev-master"}`. See on [Packagist.org](https://packagist.org/packages/bzick/cytro) -[![Build Status](https://travis-ci.org/bzick/aspect.png?branch=master)](https://travis-ci.org/bzick/cytro) +[![Build Status](https://travis-ci.org/bzick/cytro.png?branch=master)](https://travis-ci.org/bzick/cytro) ## [About](./docs/about.md) :: [Documentation](./docs/main.md) :: [Benchmark](./docs/benchmark.md) :: [Articles](./docs/articles.md) * Simplest known [syntax](./docs/syntax.md) diff --git a/benchmark/run.php b/benchmark/run.php index 8cde7b6..aa6125f 100644 --- a/benchmark/run.php +++ b/benchmark/run.php @@ -11,21 +11,30 @@ echo "Done\n"; 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("cytro", 'echo/smarty.tpl', __DIR__.'/templates/echo/data.json'); +Benchmark::runs("smarty3", 'echo/smarty.tpl', __DIR__.'/templates/echo/data.json'); +Benchmark::runs("twig", 'echo/twig.tpl', __DIR__.'/templates/echo/data.json'); +Benchmark::runs("cytro", 'echo/smarty.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("twig", 'foreach/twig.tpl', __DIR__.'/templates/foreach/data.json'); Benchmark::runs("cytro", 'foreach/smarty.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("cytro", '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'); +//} echo "\nDone. Cleanup.\n"; //passthru("rm -rf ".__DIR__."/compile/*"); diff --git a/benchmark/scripts/bootstrap.php b/benchmark/scripts/bootstrap.php index d9af065..fe8da29 100644 --- a/benchmark/scripts/bootstrap.php +++ b/benchmark/scripts/bootstrap.php @@ -45,7 +45,7 @@ class Benchmark { public static function cytro($tpl, $data, $double, $message) { - $cytro = Cytro::factory(__DIR__.'/../templates', __DIR__."/../compile/"); + $cytro = Cytro::factory(__DIR__.'/../templates', __DIR__."/../compile"); if($double) { $cytro->fetch($tpl, $data); @@ -55,6 +55,27 @@ class Benchmark { printf(self::$t, __FUNCTION__, $message, round(microtime(true)-$start, 4), round(memory_get_peak_usage()/1024/1024, 2)); } + public static function volt($tpl, $data, $double, $message) { + $view = new \Phalcon\Mvc\View(); + //$view->setViewsDir(__DIR__.'/../templates'); + $volt = new \Phalcon\Mvc\View\Engine\Volt($view); + + + $volt->setOptions(array( + "compiledPath" => __DIR__.'/../compile', + "compiledExtension" => __DIR__."/../.compile" + )); + + if($double) { + $volt->render($tpl, $data); + } + + $start = microtime(true); + var_dump($tpl); + $volt->render(__DIR__.'/../templates/'.$tpl, $data); + printf(self::$t, __FUNCTION__, $message, round(microtime(true)-$start, 4), round(memory_get_peak_usage()/1024/1024, 2)); + } + 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' : '')); } diff --git a/docs/ext/tags.md b/docs/ext/tags.md index 96f8f5f..3f76508 100644 --- a/docs/ext/tags.md +++ b/docs/ext/tags.md @@ -15,7 +15,7 @@ $cytro->addFunction(string $function_name, callable $callback[, callable $parser ``` В данном случае запускается стандартный парсер, который автоматически разберет аргументы тега, которые должны быть в формате HTML аттрибутов и отдаст их в функцию ассоциативным массивом. -В данном случае вы можете переопределить парсер на произвольный в формате `function (Aspect\Tokenizer $tokenizer, Aspect\Template $template)` +В данном случае вы можете переопределить парсер на произвольный в формате `function (Cytro\Tokenizer $tokenizer, Cytro\Template $template)` Существует более совершенный способ добавления функции: ```php @@ -42,7 +42,7 @@ $cytro->addBlockFunction(string $function_name, callable $callback[, callable $p $cytro->addCompiler(string $compiler, callable $parser); ``` -Парсер должен принимать `Aspect\Tokenizer $tokenizer`, `Aspect\Template $template` и возвращать PHP код. +Парсер должен принимать `Cytro\Tokenizer $tokenizer`, `Cytro\Template $template` и возвращать PHP код. Компилятор так же можно импортировать из класса автоматически ```php diff --git a/src/Cytro/Compiler.php b/src/Cytro/Compiler.php index c07db6a..32089c7 100644 --- a/src/Cytro/Compiler.php +++ b/src/Cytro/Compiler.php @@ -391,7 +391,10 @@ class Compiler { }*/ if($name) { // static extends $tpl->_extends = $tpl->getStorage()->getRawTemplate()->load($name, false); - $tpl->_compatible = &$tpl->_extends->_compatible; +// $tpl->_compatible = &$tpl->_extends->_compatible; + if(!isset($tpl->_compatible)) { + $tpl->_compatible = &$tpl->_extends->_compatible;; + } $tpl->addDepend($tpl->_extends); return ""; } else { // dynamic extends @@ -411,6 +414,7 @@ class Compiler { $t = $t->_extends; if(is_object($t)) { $t->_extended = true; + $tpl->addDepend($t); $t->_compatible = &$tpl->_compatible; $t->blocks = &$tpl->blocks; $t->compile(); @@ -451,11 +455,6 @@ class Compiler { return '?>'.$donor->getBody().'getStorage()->getTemplate('.$cname.'); '; - - //$tpl->_compatible = true; - //$tpl->_ = false; } } @@ -482,28 +481,26 @@ class Compiler { $tpl = $scope->tpl; if(isset($tpl->_extends)) { // is child if($scope["name"]) { // is scalar name - if(!isset($tpl->blocks[ $scope["name"] ])) { // is block still doesn't preset - if($tpl->_compatible) { // is compatible mode - $scope->replaceContent( - 'blocks['.$scope["cname"].'])) { '. - '$tpl->b['.$scope["cname"].'] = function($tpl) { ?>'.PHP_EOL. - $scope->getContent(). - "".PHP_EOL - ); - } else { - $tpl->blocks[ $scope["name"] ] = $scope->getContent(); - $scope->replaceContent( - 'b['.$scope["cname"].'] = function($tpl) { ?>'.PHP_EOL. + if($tpl->_compatible) { // is compatible mode + $scope->replaceContent( + 'b['.$scope["cname"].'])) { '. + '$tpl->b['.$scope["cname"].'] = function($tpl) { ?>'.PHP_EOL. $scope->getContent(). - "".PHP_EOL - ); - } + "".PHP_EOL + ); + } 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 + ); } } else { // dynamic name $tpl->_compatible = true; // enable compatible mode $scope->replaceContent( - 'b['.$scope["cname"].'])) { '. + 'b['.$scope["cname"].'])) { '. '$tpl->b['.$scope["cname"].'] = function($tpl) { ?>'.PHP_EOL. $scope->getContent(). "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. + 'b['.$scope["cname"].'])) { echo $tpl->b['.$scope["cname"].']->__invoke($tpl); } else {?>'.PHP_EOL. $tpl->blocks[ $scope["name"] ]. ''.PHP_EOL ); @@ -524,7 +521,7 @@ class Compiler { } } elseif(isset($tpl->_extended) && $tpl->_compatible || empty($tpl->_extended)) { $scope->replaceContent( - 'b['.$scope["cname"].'])) { echo $tpl->b['.$scope["cname"].']->__invoke($tpl); } else {?>'.PHP_EOL. + 'b['.$scope["cname"].'])) { echo $tpl->b['.$scope["cname"].']->__invoke($tpl); } else {?>'.PHP_EOL. $scope->getContent(). ''.PHP_EOL ); diff --git a/src/Cytro/Modifier.php b/src/Cytro/Modifier.php index b5c1c40..161b168 100644 --- a/src/Cytro/Modifier.php +++ b/src/Cytro/Modifier.php @@ -80,8 +80,8 @@ class Modifier { } /** - * Crop string by length - * UTF8 support + * Crop string by length (support unicode) + * * @param string $string text witch will be truncate * @param int $length maximum symbols of result string * @param string $etc place holder truncated symbols diff --git a/src/Cytro/Template.php b/src/Cytro/Template.php index f92cac3..421d776 100644 --- a/src/Cytro/Template.php +++ b/src/Cytro/Template.php @@ -13,7 +13,7 @@ use Cytro; /** * Template compiler * - * @package aspect + * @package Cytro * @author Ivan Shalganov */ class Template extends Render { @@ -64,6 +64,8 @@ class Template extends Render { */ private $_ignore = false; + private $_filter = array(); + /** * Just factory * @@ -129,12 +131,12 @@ class Template extends Render { * @throws CompileException */ public function compile() { - if(!isset($this->_src)) { + if(!isset($this->_src)) { // already compiled return; } $pos = 0; $frag = ""; - while(($start = strpos($this->_src, '{', $pos)) !== false) { // search open-char of tags + while(($start = strpos($this->_src, '{', $pos)) !== false) { // search open-symbol of tags switch($this->_src[$start + 1]) { // check next char case "\n": case "\r": case "\t": case " ": case "}": // ignore the tag $pos = $start + 1; // try find tags after the current char @@ -174,7 +176,7 @@ class Template extends Render { $this->_appendText($_frag); $tokens = new Tokenizer($_tag); $this->_appendCode($this->_tag($tokens), $tag); - if($tokens->key()) { // if tokenizer still have tokens + 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); } @@ -217,11 +219,17 @@ class Template extends Render { private function _appendText($text) { if($this->_filter) { if(strpos($text, "_body .= $text; + } else { + $fragments = explode("_filter as $filter) { + $fragment = call_user_func($filter, $fragment); + } + } + } + $this->_body .= implode('', $fragments); } } else { $this->_body .= str_replace("'.PHP_EOL, $text); diff --git a/tests/cases/Cytro/ExtendsTemplateTest.php b/tests/cases/Cytro/ExtendsTemplateTest.php index 750b174..2cadf51 100644 --- a/tests/cases/Cytro/ExtendsTemplateTest.php +++ b/tests/cases/Cytro/ExtendsTemplateTest.php @@ -90,79 +90,17 @@ class ExtendsTemplateTest extends TestCase { $tpls = self::generate('{block "%s"}%s{/block}', '{extends "level.%d.tpl"}'); foreach($tpls as $name => $tpl) { $this->tpl($name, $tpl["src"]); - //var_dump($src, "----\n\n----", $dst);ob_flush();fgetc(STDIN); +// var_dump($src, "----\n\n----", $dst);ob_flush();fgetc(STDIN); $this->assertSame($this->cytro->fetch($name, $vars), $tpl["dst"]); } $tpls = self::generate('{block "{$%s}"}%s{/block}', '{extends "level.%d.tpl"}'); arsort($tpls); foreach($tpls as $name => $tpl) { $this->tpl("d.".$name, $tpl["src"]); - //var_dump($src, "----\n\n----", $dst);ob_flush();fgetc(STDIN); +// var_dump($tpl["src"], "----\n\n----", $tpl["dst"]);ob_flush();fgetc(STDIN); $this->assertSame($this->cytro->fetch("d.".$name, $vars), $tpl["dst"]); - //var_dump($name);ob_flush();fgets(STDIN); +// var_dump($name);ob_flush();fgets(STDIN); } } - -// public static function providerDynamicExtends() { -// $tpls = array(); -// //foreach(self::templates() as $i => $tpl) { -// // $tpls[] = array($tpl[0], ); -// //} -// $data = self::providerExtends(); -// $data[2][1] = str_replace('"b2"', '"b{$two}"', $data[2][1]); -// return $data; -// } - -// public function setUp() { -// $this->cytro = Cytro::factory(CYTRO_RESOURCES.'/template', CYTRO_RESOURCES.'/compile'); -// } -// -// /** -// * @dataProvider providerExtends -// * @param $name -// * @param $code -// * @param $vars -// * @param $result -// */ -// public function testStaticExtends($name, $code, $vars, $result) { -// static $i = 0; -// $vars["iteration"] = $i++; -// $this->execTpl($name, $code, $vars, $result); -// } -// -// /** -// * @dataProvider providerDynamicExtends -// * @param $name -// * @param $code -// * @param $vars -// * @param $result -// */ -// public function testDynamicExtends($name, $code, $vars, $result) { -// static $i = 0; -// $vars["iteration"] = $i++; -// $this->execTpl($name, $code, $vars, $result, 0); -// } -// -// /** -// * @group extends -// */ -// public function _testParentLevel() { -// //echo($this->cytro->getTemplate("parent.tpl")->_body); exit; -// $this->assertSame($this->cytro->fetch("parent.tpl", array("a" => "a char")), "Parent template\nBlock1: Block2: Block3: default"); -// } -// -// /** -// * @group extends -// */ -// public function testChildLevel1() { -// //echo($this->cytro->fetch("child1.tpl", array("a" => "a char"))); exit; -// } -// -// /** -// * @group extends -// */ -// public function _testChildLevel3() { -// echo($this->cytro->getTemplate("child3.tpl")->getBody()); exit; -// } }