diff --git a/benchmark/scripts/bootstrap.php b/benchmark/scripts/bootstrap.php
index a3c8ebf..b366236 100644
--- a/benchmark/scripts/bootstrap.php
+++ b/benchmark/scripts/bootstrap.php
@@ -50,19 +50,23 @@ class Benchmark {
if($double) {
$aspect->fetch($tpl, $data);
}
- $start = microtime(true);
+ $_SERVER["t"] = $start = microtime(true);
$aspect->fetch($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 %s/run.php --engine '%s' --template '%s' --data '%s' --message '%s' %s", __DIR__, $engine, $template, $data, $message, $double ? '--double' : ''));
+ passthru(sprintf("php -dmemory_limit=512M %s/run.php --engine '%s' --template '%s' --data '%s' --message '%s' %s", __DIR__, $engine, $template, $data, $message, $double ? '--double' : ''));
}
public static function runs($engine, $template, $data) {
self::run($engine, $template, $data, false, '!compiled and !loaded');
- self::run($engine, $template, $data, false, 'compiled and !loaded');
- self::run($engine, $template, $data, true, 'compiled and loaded');
+ self::run($engine, $template, $data, false, ' compiled and !loaded');
+ self::run($engine, $template, $data, true, ' compiled and loaded');
echo "\n";
}
+}
+
+function t() {
+ if(isset($_SERVER["t"])) var_dump(round(microtime(1) - $_SERVER["t"], 4));
}
\ No newline at end of file
diff --git a/src/Aspect.php b/src/Aspect.php
index 16c6fa0..1f5ddce 100644
--- a/src/Aspect.php
+++ b/src/Aspect.php
@@ -526,7 +526,6 @@ class Aspect {
* @return Aspect\Template
*/
public function getTemplate($template) {
-
if(isset($this->_storage[ $template ])) {
/** @var Aspect\Template $tpl */
$tpl = $this->_storage[ $template ];
@@ -562,6 +561,7 @@ class Aspect {
if(!is_file($this->_compile_dir."/".$file_name)) {
return $this->compile($tpl);
} else {
+ $aspect = $this;
/** @var Aspect\Render $tpl */
return include($this->_compile_dir."/".$file_name);
}
diff --git a/src/Aspect/Compiler.php b/src/Aspect/Compiler.php
index 0469f4c..4fe2085 100644
--- a/src/Aspect/Compiler.php
+++ b/src/Aspect/Compiler.php
@@ -388,28 +388,18 @@ class Compiler {
public static function extendBody(&$body, $tpl) {
if(isset($tpl->_extends)) { // is child
if(is_object($tpl->_extends)) { // static extends
- $t = $tpl;
- while(isset($t->_extends)) {
+ $t = $tpl->_extends;
+ /* @var Template $t */
+ do {
+ $t->_blocks = &$tpl->_blocks;
$t->compile();
- $t->_blocks += (array)$t->_extends->_blocks;
- $t = $t->_extends;
-
- }
-
- if(empty($t->_blocks)) {
- $body = $t->getBody();
- } else {
- $b = $t->getBody();
- foreach($t->_blocks as $name => $pos) {
-
- }
- }
+ $tpl->addDepend($t);
+ } while(isset($t->_extends) && $t = $t->_extends);
+ $body = $t->_body;
} else { // dynamic extends
$body .= 'blocks = &$tpl->blocks; $parent->display((array)$tpl); unset($tpl->blocks, $parent->blocks); ?>';
- //return '$tpl->blocks['.$scope["name"].'] = ob_get_clean();';
}
}
- /*$body = 'blocks)) {$tpl->blocks = array();} ob_start(); ?>'.$body.'blocks = &$tpl->blocks; $parent->display((array)$tpl); unset($tpl->blocks, $parent->blocks); ?>';*/
}
@@ -425,25 +415,30 @@ class Compiler {
* @throws ImproperUseException
*/
public static function tagBlockOpen(Tokenizer $tokens, Scope $scope) {
- $p = $scope->tpl->parseParams($tokens);
- if (isset($p[0])) {
- $scope["name"] = $p[0];
+ $p = $scope->tpl->parseFirstArg($tokens, $name);
+ if ($name) {
+ $scope["static"] = true;
+ $scope["name"] = $name;
+ $scope["cname"] = '"'.addslashes($name).'"';
} else {
- throw new ImproperUseException("{block} must be named");
+ $scope["static"] = false;
+ $scope["name"] = $scope["cname"] = $p;
}
if(isset($scope->tpl->_extends)) { // is child
- if(is_object($scope->tpl->_extends)) { // static extends
+ if(is_object($scope->tpl->_extends) && $scope["static"]) { // static extends
$code = "";
} else { // dynamic extends
- $code = 'if(empty($tpl->blocks['.$scope["name"].'])) { ob_start();';
+ $code = 'if(empty($tpl->blocks['.$scope["cname"].'])) { $tpl->blocks['.$scope["cname"].'] = function($tpl) {';
}
} else { // is parent
- if(isset($scope->tpl->_blocks[ $scope["name"] ])) { // skip own block and insert child's block after
- $scope["body"] = $scope->tpl->_body;
- $scope->tpl->_body = "";
+ if(isset($scope->tpl->_blocks)) { // has blocks from child
+ if(isset($scope->tpl->_blocks[ $scope["name"] ])) { // skip own block and insert child's block after
+ $scope["body"] = $scope->tpl->_body;
+ $scope->tpl->_body = "";
+ } // else just put block content as is
return '';
} else {
- $code = 'if(isset($tpl->blocks['.$scope["name"].'])) { echo $tpl->blocks['.$scope["name"].']; } else {';
+ $code = 'if(isset($tpl->blocks['.$scope["cname"].'])) { echo $tpl->blocks['.$scope["cname"].']->__invoke($tpl); } else {';
}
}
$scope["offset"] = strlen($scope->tpl->getBody()) + strlen($code);
@@ -457,35 +452,26 @@ class Compiler {
* @return string
*/
public static function tagBlockClose($tokens, Scope $scope) {
- $scope->tpl->_blocks[ $scope["name"] ] = substr($scope->tpl->getBody(), $scope["offset"]);
+
if(isset($scope->tpl->_extends)) { // is child
- if(is_object($scope->tpl->_extends)) { // static extends
+ if(is_object($scope->tpl->_extends) && $scope["static"]) { // static extends
+ if(!isset($scope->tpl->_blocks[ $scope["name"] ])) {
+ $scope->tpl->_blocks[ $scope["name"] ] = substr($scope->tpl->getBody(), $scope["offset"]);
+ }
return "";
} else { // dynamic extends
- return '$tpl->blocks['.$scope["name"].'] = ob_get_clean(); }';
+ return '} }';
}
} else { // is parent
- if(isset($scope["body"])) {
- $scope->tpl->_body = $scope["body"].$scope->tpl->_blocks[ $scope["name"] ];
+ if(isset($scope->tpl->_blocks)) {
+ if(isset($scope["body"])) {
+ $scope->tpl->_body = $scope["body"].$scope->tpl->_blocks[ $scope["name"] ];
+ }
return "";
} else {
return '}';
}
}
- /* $scope->tpl->_blocks[ $scope["name"] ] = substr($scope->tpl->getBody(), $scope["offset"]);
- return '}';*/
- /*if(isset($scope->tpl->_extends) && is_object($scope->tpl->_extends)) {
-
- //var_dump("fetched block ".$scope->tpl->_blocks[ $scope["name"] ]);
- } else {
- return '}';
- }*/
- /*if(isset($scope->tpl->_extends)) {
- $var = '$i'.$scope->tpl->i++;
- return $var.' = ob_get_clean(); if('.$var.') $tpl->blocks['.$scope["name"].'] = '.$var.';';
- } else {
- return 'if(empty($tpl->blocks['.$scope["name"].'])) { ob_end_flush(); } else { print($tpl->blocks['.$scope["name"].']); ob_end_clean(); }';
- }*/
}
/**
diff --git a/src/Aspect/Misc.php b/src/Aspect/Misc.php
index 686daa0..4dfff2e 100644
--- a/src/Aspect/Misc.php
+++ b/src/Aspect/Misc.php
@@ -28,6 +28,11 @@ class Misc {
return $mask;
}
+ /**
+ * Clean directory from files
+ *
+ * @param string $path
+ */
public static function clean($path) {
if(is_file($path)) {
unlink($path);
@@ -46,6 +51,11 @@ class Misc {
}
}
+ /**
+ * Recursive remove directory
+ *
+ * @param string $path
+ */
public static function rm($path) {
self::clean($path);
if(is_dir($path)) {
diff --git a/src/Aspect/Render.php b/src/Aspect/Render.php
index 5e11694..4eebf2e 100644
--- a/src/Aspect/Render.php
+++ b/src/Aspect/Render.php
@@ -66,6 +66,10 @@ class Render extends \ArrayObject {
return $this->_aspect;
}
+ public function getDepends() {
+ return $this->_depends;
+ }
+
public function getScm() {
return $this->_scm;
}
@@ -93,7 +97,7 @@ class Render extends \ArrayObject {
return $this->_name;
}
- public function getCompileTime() {
+ public function getTime() {
return $this->_time;
}
@@ -108,7 +112,7 @@ class Render extends \ArrayObject {
return false;
}
foreach($this->_depends as $tpl => $time) {
- if($this->_aspect->getTemplate($tpl)->getCompileTime() !== $time) {
+ if($this->_aspect->getTemplate($tpl)->getTime() !== $time) {
return false;
}
}
diff --git a/src/Aspect/Template.php b/src/Aspect/Template.php
index 990726a..baaf156 100644
--- a/src/Aspect/Template.php
+++ b/src/Aspect/Template.php
@@ -137,16 +137,29 @@ class Template extends Render {
// $frag = ltrim($frag);
//}
- $this->_body .= str_replace("", '', $frag);
+ $this->_body .= str_replace("", '', $frag);
- $tag = $this->_tag($tag, $this->_trim); // dispatching tags
+ $this->_body .= $this->_tag($tag, $this->_trim); // dispatching tags
+ // duplicate new line http://docs.php.net/manual/en/language.basic-syntax.instruction-separation.php
+ /*if(isset($this->_src[ $end+1 ])) {
+ $c = $this->_src[ $end+1 ];
+ if($c === "\n") {
+ $this->_body .= "\n";
+ } elseif($c === "\r") {
+ if(isset($this->_src[ $end+2 ]) && $this->_src[ $end+2 ] == "\n") {
+ $this->_body .= "\r\n";
+ } else {
+ $this->_body .= "\r";
+ }
+ }
+ }*/
//if($this->_trim) { // if current tag has trim flag
// $frag = rtrim($frag);
//}
- $this->_body .= $tag;
+ //$this->_body .= $tag;
}
- $this->_body .= str_replace("", '', substr($this->_src, $this->_pos));
+ $this->_body .= str_replace("", '', substr($this->_src, $this->_pos));
if($this->_stack) {
$_names = array();
$_line = 0;
@@ -185,7 +198,7 @@ class Template extends Render {
public function getTemplateCode() {
return "_name."' compiled at ".date('Y-m-d H:i:s')." */\n".
- "return new Aspect\\Render(\$this, ".$this->_getClosureSource().", ".var_export(array(
+ "return new Aspect\\Render(\$aspect, ".$this->_getClosureSource().", ".var_export(array(
//"options" => $this->_options,
"provider" => $this->_scm,
"name" => $this->_name,
@@ -227,8 +240,7 @@ class Template extends Render {
* @param Render $tpl
*/
public function addDepend(Render $tpl) {
-
- $this->_depends[$tpl->getName()] = $tpl->getCompileTime();
+ $this->_depends[$tpl->getScm()][$tpl->getName()] = $tpl->getTime();
}
/**
diff --git a/tests/TestCase.php b/tests/TestCase.php
new file mode 100644
index 0000000..3895cff
--- /dev/null
+++ b/tests/TestCase.php
@@ -0,0 +1,57 @@
+aspect = Aspect::factory(ASPECT_RESOURCES.'/template', ASPECT_RESOURCES.'/compile');
+ }
+
+ /**
+ * Compile and execute template
+ *
+ * @param string $code source of template
+ * @param array $vars variables of template
+ * @param string $result expected result.
+ * @param bool $dump dump source and result code (for debug)
+ */
+ public function exec($code, $vars, $result, $dump = false) {
+ $tpl = $this->aspect->compileCode($code, "runtime.tpl");
+ 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");
+ }
+
+ /**
+ * Try to compile the invalid template
+ * @param string $code source of the template
+ * @param string $exception exception class
+ * @param string $message exception message
+ * @param int $options Aspect's options
+ */
+ public function execError($code, $exception, $message, $options = 0) {
+ $opt = $this->aspect->getOptions();
+ $this->aspect->setOptions($options);
+ try {
+ $this->aspect->compileCode($code, "inline.tpl");
+ } catch(\Exception $e) {
+ $this->assertSame($exception, get_class($e), "Exception $code");
+ $this->assertStringStartsWith($message, $e->getMessage());
+ $this->aspect->setOptions($opt);
+ return;
+ }
+ self::$aspect->setOptions($opt);
+ $this->fail("Code $code must be invalid");
+ }
+}
diff --git a/tests/autoload.php b/tests/autoload.php
index 3647243..fda7597 100644
--- a/tests/autoload.php
+++ b/tests/autoload.php
@@ -6,4 +6,12 @@ require_once __DIR__."/../vendor/autoload.php";
define('ASPECT_RESOURCES', __DIR__."/resources");
-require_once ASPECT_RESOURCES."/actions.php";
\ No newline at end of file
+require_once ASPECT_RESOURCES."/actions.php";
+require_once __DIR__."/TestCase.php";
+
+function drop() {
+ call_user_func_array("var_dump", func_get_args());
+ $e = new Exception();
+ echo "-------\nDump trace: \n".$e->getTraceAsString()."\n";
+ exit();
+}
\ No newline at end of file
diff --git a/tests/cases/Aspect/Template/ExtendsTest.php b/tests/cases/Aspect/Template/ExtendsTest.php
index 3e877f7..5c06aa3 100644
--- a/tests/cases/Aspect/Template/ExtendsTest.php
+++ b/tests/cases/Aspect/Template/ExtendsTest.php
@@ -1,22 +1,8 @@
compileCode($code, "inline.tpl");
- if($dump) {
- echo "\n===========================\n".$code.": ".$tpl->getBody();
- }
- $this->assertSame(Modifier::strip($result), Modifier::strip($tpl->fetch($vars), true), "Test $code");
- }
-
- public function execError($code, $exception, $message, $options) {
- self::$aspect->setOptions($options);
- try {
- self::$aspect->compileCode($code, "inline.tpl");
- } catch(\Exception $e) {
- $this->assertSame($exception, get_class($e), "Exception $code");
- $this->assertStringStartsWith($message, $e->getMessage());
- self::$aspect->setOptions(0);
- return;
- }
- self::$aspect->setOptions(0);
- $this->fail("Code $code must be invalid");
- }
-
/**
* @group extends
*/
- public function testParent() {
- //echo(self::$aspect->getTemplate("parent.tpl")->getBody()); exit;
+ public function testParentLevel() {
+ //echo($this->aspect->getTemplate("parent.tpl")->_body); exit;
+ $this->assertSame($this->aspect->fetch("parent.tpl", array("a" => "a char")), "Parent template\nBlock1: Block2: Block3: default");
}
/**
* @group extends
*/
- public function ___testChildLevel1() {
- echo(self::$aspect->getTemplate("child1.tpl")->getBody()); exit;
+ public function testChildLevel1() {
+ //echo($this->aspect->fetch("child1.tpl", array("a" => "a char"))); exit;
}
/**
* @group extends
*/
- public function __testExtends() {
- echo(self::$aspect->fetch("child1.tpl", array("a" => "value", "z" => ""))."\n"); exit;
+ public function _testChildLevel3() {
+ echo($this->aspect->getTemplate("child3.tpl")->getBody()); exit;
}
}
diff --git a/tests/cases/Aspect/TemplateTest.php b/tests/cases/Aspect/TemplateTest.php
index 620a6fc..3a97f65 100644
--- a/tests/cases/Aspect/TemplateTest.php
+++ b/tests/cases/Aspect/TemplateTest.php
@@ -4,20 +4,11 @@ use Aspect\Template,
Aspect,
Aspect\Render;
-class TemplateTest extends \PHPUnit_Framework_TestCase {
- /**
- * @var Aspect
- */
- public static $aspect;
+class TemplateTest extends TestCase {
public function setUp() {
- if(!file_exists(ASPECT_RESOURCES.'/compile')) {
- mkdir(ASPECT_RESOURCES.'/compile', 0777, true);
- } else {
- exec("rm -f ".ASPECT_RESOURCES.'/compile/*');
- }
- self::$aspect = Aspect::factory(ASPECT_RESOURCES.'/template', ASPECT_RESOURCES.'/compile');
- self::$aspect->addTemplate(new Render(self::$aspect, function ($tpl) {
+ parent::setUp();
+ $this->aspect->addTemplate(new Render($this->aspect, function ($tpl) {
echo "Welcome, ".$tpl["username"]." (".$tpl["email"].")";
}, array(
"name" => "welcome.tpl"
@@ -569,28 +560,6 @@ class TemplateTest extends \PHPUnit_Framework_TestCase {
);
}
- public function exec($code, $vars, $result, $dump = false) {
- $tpl = self::$aspect->compileCode($code, "inline.tpl");
- if($dump) {
- echo "\n===========================\n".$code.": ".$tpl->getBody();
- }
- $this->assertSame(Modifier::strip($result), Modifier::strip($tpl->fetch($vars), true), "Test $code");
- }
-
- public function execError($code, $exception, $message, $options) {
- self::$aspect->setOptions($options);
- try {
- self::$aspect->compileCode($code, "inline.tpl");
- } catch(\Exception $e) {
- $this->assertSame($exception, get_class($e), "Exception $code");
- $this->assertStringStartsWith($message, $e->getMessage());
- self::$aspect->setOptions(0);
- return;
- }
- self::$aspect->setOptions(0);
- $this->fail("Code $code must be invalid");
- }
-
/**
* @dataProvider providerVars
*/
diff --git a/tests/resources/template/child2.tpl b/tests/resources/template/child2.tpl
index 0095c8f..fab7d20 100644
--- a/tests/resources/template/child2.tpl
+++ b/tests/resources/template/child2.tpl
@@ -1,4 +1,4 @@
-{extends "parent.tpl"}
+{extends "child1.tpl"}
{block blk2}
blk2.{$a}
{/block}
\ No newline at end of file
diff --git a/tests/resources/template/child3.tpl b/tests/resources/template/child3.tpl
index fb75924..e3a7770 100644
--- a/tests/resources/template/child3.tpl
+++ b/tests/resources/template/child3.tpl
@@ -1,7 +1,7 @@
-{extends "parent.tpl"}
+{extends "child2.tpl"}
{block blk1}
-blk1.{$a} (rewrited)
+blk1.{$a} (overwritten)
{/block}
{block blk3}