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) {
$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));
}

View File

@ -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);
}

View File

@ -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 .= '<?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
*/
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)) { // 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->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(); }';
}*/
}
/**

View File

@ -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)) {

View File

@ -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;
}
}

View File

@ -137,16 +137,29 @@ class Template extends Render {
// $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
// $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) {
$_names = array();
$_line = 0;
@ -185,7 +198,7 @@ class Template extends Render {
public function getTemplateCode() {
return "<?php \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,
"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();
}
/**

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");
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
namespace Aspect\Template;
use Aspect, Aspect\Modifier;
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');
}
use Aspect, Aspect\Modifier, Aspect\TestCase;
class ExtendsTest extends TestCase {
public static function providerExtends() {
return array(
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
*/
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 testParent() {
//echo(self::$aspect->getTemplate("parent.tpl")->getBody()); exit;
public function testChildLevel1() {
//echo($this->aspect->fetch("child1.tpl", array("a" => "a char"))); exit;
}
/**
* @group extends
*/
public function ___testChildLevel1() {
echo(self::$aspect->getTemplate("child1.tpl")->getBody()); 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;
}
}

View File

@ -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 "<b>Welcome, ".$tpl["username"]." (".$tpl["email"].")</b>";
}, 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
*/

View File

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

View File

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