Done static inheritance

Add TestCase
This commit is contained in:
bzick
2013-02-15 01:49:26 +04:00
parent 8c618c2b34
commit ad2e7316b1
12 changed files with 157 additions and 142 deletions

View File

@ -50,19 +50,23 @@ class Benchmark {
if($double) { if($double) {
$aspect->fetch($tpl, $data); $aspect->fetch($tpl, $data);
} }
$start = microtime(true); $_SERVER["t"] = $start = microtime(true);
$aspect->fetch($tpl, $data); $aspect->fetch($tpl, $data);
printf(self::$t, __FUNCTION__, $message, round(microtime(true)-$start, 4), round(memory_get_peak_usage()/1024/1024, 2)); 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) { 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) { 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, 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, true, ' compiled and loaded');
echo "\n"; echo "\n";
} }
} }
function t() {
if(isset($_SERVER["t"])) var_dump(round(microtime(1) - $_SERVER["t"], 4));
}

View File

@ -526,7 +526,6 @@ class Aspect {
* @return Aspect\Template * @return Aspect\Template
*/ */
public function getTemplate($template) { public function getTemplate($template) {
if(isset($this->_storage[ $template ])) { if(isset($this->_storage[ $template ])) {
/** @var Aspect\Template $tpl */ /** @var Aspect\Template $tpl */
$tpl = $this->_storage[ $template ]; $tpl = $this->_storage[ $template ];
@ -562,6 +561,7 @@ class Aspect {
if(!is_file($this->_compile_dir."/".$file_name)) { if(!is_file($this->_compile_dir."/".$file_name)) {
return $this->compile($tpl); return $this->compile($tpl);
} else { } else {
$aspect = $this;
/** @var Aspect\Render $tpl */ /** @var Aspect\Render $tpl */
return include($this->_compile_dir."/".$file_name); return include($this->_compile_dir."/".$file_name);
} }

View File

@ -388,28 +388,18 @@ class Compiler {
public static function extendBody(&$body, $tpl) { public static function extendBody(&$body, $tpl) {
if(isset($tpl->_extends)) { // is child if(isset($tpl->_extends)) { // is child
if(is_object($tpl->_extends)) { // static extends if(is_object($tpl->_extends)) { // static extends
$t = $tpl; $t = $tpl->_extends;
while(isset($t->_extends)) { /* @var Template $t */
do {
$t->_blocks = &$tpl->_blocks;
$t->compile(); $t->compile();
$t->_blocks += (array)$t->_extends->_blocks; $tpl->addDepend($t);
$t = $t->_extends; } while(isset($t->_extends) && $t = $t->_extends);
$body = $t->_body;
}
if(empty($t->_blocks)) {
$body = $t->getBody();
} else {
$b = $t->getBody();
foreach($t->_blocks as $name => $pos) {
}
}
} else { // dynamic extends } else { // dynamic extends
$body .= '<?php $parent->blocks = &$tpl->blocks; $parent->display((array)$tpl); unset($tpl->blocks, $parent->blocks); ?>'; $body .= '<?php $parent->blocks = &$tpl->blocks; $parent->display((array)$tpl); unset($tpl->blocks, $parent->blocks); ?>';
//return '$tpl->blocks['.$scope["name"].'] = ob_get_clean();';
} }
} }
/*$body = '<?php if(!isset($tpl->blocks)) {$tpl->blocks = array();} ob_start(); ?>'.$body.'<?php ob_end_clean(); $parent->blocks = &$tpl->blocks; $parent->display((array)$tpl); unset($tpl->blocks, $parent->blocks); ?>';*/
} }
@ -425,25 +415,30 @@ class Compiler {
* @throws ImproperUseException * @throws ImproperUseException
*/ */
public static function tagBlockOpen(Tokenizer $tokens, Scope $scope) { public static function tagBlockOpen(Tokenizer $tokens, Scope $scope) {
$p = $scope->tpl->parseParams($tokens); $p = $scope->tpl->parseFirstArg($tokens, $name);
if (isset($p[0])) { if ($name) {
$scope["name"] = $p[0]; $scope["static"] = true;
$scope["name"] = $name;
$scope["cname"] = '"'.addslashes($name).'"';
} else { } else {
throw new ImproperUseException("{block} must be named"); $scope["static"] = false;
$scope["name"] = $scope["cname"] = $p;
} }
if(isset($scope->tpl->_extends)) { // is child 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 = ""; $code = "";
} else { // dynamic extends } 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 } else { // is parent
if(isset($scope->tpl->_blocks[ $scope["name"] ])) { // skip own block and insert child's block after if(isset($scope->tpl->_blocks)) { // has blocks from child
$scope["body"] = $scope->tpl->_body; if(isset($scope->tpl->_blocks[ $scope["name"] ])) { // skip own block and insert child's block after
$scope->tpl->_body = ""; $scope["body"] = $scope->tpl->_body;
$scope->tpl->_body = "";
} // else just put block content as is
return ''; return '';
} else { } 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); $scope["offset"] = strlen($scope->tpl->getBody()) + strlen($code);
@ -457,35 +452,26 @@ class Compiler {
* @return string * @return string
*/ */
public static function tagBlockClose($tokens, Scope $scope) { 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(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 ""; return "";
} else { // dynamic extends } else { // dynamic extends
return '$tpl->blocks['.$scope["name"].'] = ob_get_clean(); }'; return '} }';
} }
} else { // is parent } else { // is parent
if(isset($scope["body"])) { if(isset($scope->tpl->_blocks)) {
$scope->tpl->_body = $scope["body"].$scope->tpl->_blocks[ $scope["name"] ]; if(isset($scope["body"])) {
$scope->tpl->_body = $scope["body"].$scope->tpl->_blocks[ $scope["name"] ];
}
return ""; return "";
} else { } else {
return '}'; 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(); }';
}*/
} }
/** /**

View File

@ -28,6 +28,11 @@ class Misc {
return $mask; return $mask;
} }
/**
* Clean directory from files
*
* @param string $path
*/
public static function clean($path) { public static function clean($path) {
if(is_file($path)) { if(is_file($path)) {
unlink($path); unlink($path);
@ -46,6 +51,11 @@ class Misc {
} }
} }
/**
* Recursive remove directory
*
* @param string $path
*/
public static function rm($path) { public static function rm($path) {
self::clean($path); self::clean($path);
if(is_dir($path)) { if(is_dir($path)) {

View File

@ -66,6 +66,10 @@ class Render extends \ArrayObject {
return $this->_aspect; return $this->_aspect;
} }
public function getDepends() {
return $this->_depends;
}
public function getScm() { public function getScm() {
return $this->_scm; return $this->_scm;
} }
@ -93,7 +97,7 @@ class Render extends \ArrayObject {
return $this->_name; return $this->_name;
} }
public function getCompileTime() { public function getTime() {
return $this->_time; return $this->_time;
} }
@ -108,7 +112,7 @@ class Render extends \ArrayObject {
return false; return false;
} }
foreach($this->_depends as $tpl => $time) { foreach($this->_depends as $tpl => $time) {
if($this->_aspect->getTemplate($tpl)->getCompileTime() !== $time) { if($this->_aspect->getTemplate($tpl)->getTime() !== $time) {
return false; return false;
} }
} }

View File

@ -137,16 +137,29 @@ class Template extends Render {
// $frag = ltrim($frag); // $frag = ltrim($frag);
//} //}
$this->_body .= str_replace("<?", '<?php echo "<?" ?>', $frag); $this->_body .= str_replace("<?", '<?php echo "<?"; ?>', $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 //if($this->_trim) { // if current tag has trim flag
// $frag = rtrim($frag); // $frag = rtrim($frag);
//} //}
$this->_body .= $tag; //$this->_body .= $tag;
} }
$this->_body .= str_replace("<?", '<?php echo "<?" ?>', substr($this->_src, $this->_pos)); $this->_body .= str_replace("<?", '<?php echo "<?"; ?>', substr($this->_src, $this->_pos));
if($this->_stack) { if($this->_stack) {
$_names = array(); $_names = array();
$_line = 0; $_line = 0;
@ -185,7 +198,7 @@ class Template extends Render {
public function getTemplateCode() { public function getTemplateCode() {
return "<?php \n". return "<?php \n".
"/** Aspect template '".$this->_name."' compiled at ".date('Y-m-d H:i:s')." */\n". "/** Aspect template '".$this->_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, //"options" => $this->_options,
"provider" => $this->_scm, "provider" => $this->_scm,
"name" => $this->_name, "name" => $this->_name,
@ -227,8 +240,7 @@ class Template extends Render {
* @param Render $tpl * @param Render $tpl
*/ */
public function addDepend(Render $tpl) { public function addDepend(Render $tpl) {
$this->_depends[$tpl->getScm()][$tpl->getName()] = $tpl->getTime();
$this->_depends[$tpl->getName()] = $tpl->getCompileTime();
} }
/** /**

57
tests/TestCase.php Normal file
View File

@ -0,0 +1,57 @@
<?php
namespace Aspect;
use Aspect;
class TestCase extends \PHPUnit_Framework_TestCase {
/**
* @var Aspect
*/
public $aspect;
public function setUp() {
if(!file_exists(ASPECT_RESOURCES.'/compile')) {
mkdir(ASPECT_RESOURCES.'/compile', 0777, true);
} else {
Misc::clean(ASPECT_RESOURCES.'/compile/');
}
$this->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");
}
}

View File

@ -7,3 +7,11 @@ require_once __DIR__."/../vendor/autoload.php";
define('ASPECT_RESOURCES', __DIR__."/resources"); define('ASPECT_RESOURCES', __DIR__."/resources");
require_once ASPECT_RESOURCES."/actions.php"; 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();
}

View File

@ -1,22 +1,8 @@
<?php <?php
namespace Aspect\Template; namespace Aspect\Template;
use Aspect, Aspect\Modifier; use Aspect, Aspect\Modifier, Aspect\TestCase;
class ExtendsTest extends \PHPUnit_Framework_TestCase {
/**
* @var Aspect
*/
public static $aspect;
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');
}
class ExtendsTest extends TestCase {
public static function providerExtends() { public static function providerExtends() {
return array( return array(
array('{extends "parent.tpl"}{block "bk1"} block1 {/block}', "Template extended by block1"), array('{extends "parent.tpl"}{block "bk1"} block1 {/block}', "Template extended by block1"),
@ -26,47 +12,26 @@ class ExtendsTest 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");
}
/** /**
* @group extends * @group extends
*/ */
public function testParent() { public function testParentLevel() {
//echo(self::$aspect->getTemplate("parent.tpl")->getBody()); exit; //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 * @group extends
*/ */
public function ___testChildLevel1() { public function testChildLevel1() {
echo(self::$aspect->getTemplate("child1.tpl")->getBody()); exit; //echo($this->aspect->fetch("child1.tpl", array("a" => "a char"))); exit;
} }
/** /**
* @group extends * @group extends
*/ */
public function __testExtends() { public function _testChildLevel3() {
echo(self::$aspect->fetch("child1.tpl", array("a" => "value", "z" => ""))."\n"); exit; echo($this->aspect->getTemplate("child3.tpl")->getBody()); exit;
} }
} }

View File

@ -4,20 +4,11 @@ use Aspect\Template,
Aspect, Aspect,
Aspect\Render; Aspect\Render;
class TemplateTest extends \PHPUnit_Framework_TestCase { class TemplateTest extends TestCase {
/**
* @var Aspect
*/
public static $aspect;
public function setUp() { public function setUp() {
if(!file_exists(ASPECT_RESOURCES.'/compile')) { parent::setUp();
mkdir(ASPECT_RESOURCES.'/compile', 0777, true); $this->aspect->addTemplate(new Render($this->aspect, function ($tpl) {
} 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) {
echo "<b>Welcome, ".$tpl["username"]." (".$tpl["email"].")</b>"; echo "<b>Welcome, ".$tpl["username"]." (".$tpl["email"].")</b>";
}, array( }, array(
"name" => "welcome.tpl" "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 * @dataProvider providerVars
*/ */

View File

@ -1,4 +1,4 @@
{extends "parent.tpl"} {extends "child1.tpl"}
{block blk2} {block blk2}
<b>blk2.{$a}</b> <b>blk2.{$a}</b>
{/block} {/block}

View File

@ -1,7 +1,7 @@
{extends "parent.tpl"} {extends "child2.tpl"}
{block blk1} {block blk1}
<b>blk1.{$a} (rewrited)</b> <b>blk1.{$a} (overwritten)</b>
{/block} {/block}
{block blk3} {block blk3}