mirror of
https://github.com/fenom-template/fenom.git
synced 2023-08-10 21:13:07 +03:00
Improve templates
This commit is contained in:
parent
0266f219b7
commit
9ba3ee68f8
@ -137,12 +137,12 @@ class Template extends Render {
|
||||
// $frag = ltrim($frag);
|
||||
//}
|
||||
|
||||
$this->_body .= str_replace("<?", '<?php echo "<?"; ?>', $frag);
|
||||
$frag = str_replace("<?", '<?php echo "<?"; ?>'."\n", $frag);
|
||||
|
||||
$this->_body .= $this->_tag($tag, $this->_trim); // dispatching tags
|
||||
$this->_body .= $this->_tag($tag, $frag); // dispatching tags
|
||||
|
||||
// duplicate new line http://docs.php.net/manual/en/language.basic-syntax.instruction-separation.php
|
||||
/*if(isset($this->_src[ $end+1 ])) {
|
||||
if(substr($this->_body, -2) === "?>" && isset($this->_src[ $end+1 ])) {
|
||||
$c = $this->_src[ $end+1 ];
|
||||
if($c === "\n") {
|
||||
$this->_body .= "\n";
|
||||
@ -153,7 +153,7 @@ class Template extends Render {
|
||||
$this->_body .= "\r";
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
//if($this->_trim) { // if current tag has trim flag
|
||||
// $frag = rtrim($frag);
|
||||
//}
|
||||
@ -262,20 +262,21 @@ class Template extends Render {
|
||||
/**
|
||||
* Internal tags router
|
||||
* @param string $src
|
||||
* @param bool $trim
|
||||
* @param string $frag
|
||||
* @throws UnexpectedException
|
||||
* @throws CompileException
|
||||
* @throws SecurityException
|
||||
* @return string
|
||||
*/
|
||||
private function _tag($src, &$trim = false) {
|
||||
private function _tag($src, &$frag) {
|
||||
if($src[strlen($src) - 2] === "-") {
|
||||
$token = substr($src, 1, -2);
|
||||
$trim = true;
|
||||
//$frag = ltrim($frag);
|
||||
} else {
|
||||
$token = substr($src, 1, -1);
|
||||
$trim = false;
|
||||
}
|
||||
|
||||
$this->_body .= $frag;
|
||||
$token = trim($token);
|
||||
if($this->_ignore) {
|
||||
if($token === '/ignore') {
|
||||
@ -298,10 +299,14 @@ class Template extends Render {
|
||||
$code = $this->_end($tokens);
|
||||
break;
|
||||
default:
|
||||
if($tokens->current() === "ignore") {
|
||||
$this->_ignore = true;
|
||||
$tokens->next();
|
||||
$code = '';
|
||||
} else {
|
||||
$code = $this->_parseAct($tokens);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
if($tokens->key()) { // if tokenizer still have tokens
|
||||
throw new UnexpectedException($tokens);
|
||||
}
|
||||
@ -344,6 +349,7 @@ class Template extends Render {
|
||||
*
|
||||
* @static
|
||||
* @param Tokenizer $tokens
|
||||
* @throws \LogicException
|
||||
* @throws TokenizeException
|
||||
* @return string
|
||||
*/
|
||||
@ -355,11 +361,6 @@ class Template extends Render {
|
||||
return 'echo '.$this->parseExp($tokens).';';
|
||||
}
|
||||
|
||||
if($action === "ignore") {
|
||||
$this->_ignore = true;
|
||||
$tokens->next();
|
||||
return '';
|
||||
}
|
||||
if($tokens->isNext("(")) {
|
||||
return "echo ".$this->parseExp($tokens).";";
|
||||
}
|
||||
@ -368,7 +369,7 @@ class Template extends Render {
|
||||
$tokens->next();
|
||||
switch($act["type"]) {
|
||||
case Aspect::BLOCK_COMPILER:
|
||||
$scope = new Scope($action, $this, $this->_line, $act);
|
||||
$scope = new Scope($action, $this, $this->_line, $act, count($this->_stack));
|
||||
array_push($this->_stack, $scope);
|
||||
return $scope->open($tokens);
|
||||
case Aspect::INLINE_COMPILER:
|
||||
@ -376,10 +377,12 @@ class Template extends Render {
|
||||
case Aspect::INLINE_FUNCTION:
|
||||
return call_user_func($act["parser"], $act["function"], $tokens, $this);
|
||||
case Aspect::BLOCK_FUNCTION:
|
||||
$scope = new Scope($action, $this, $this->_line, $act);
|
||||
$scope = new Scope($action, $this, $this->_line, $act, count($this->_stack));
|
||||
$scope->setFuncName($act["function"]);
|
||||
array_push($this->_stack, $scope);
|
||||
return $scope->open($tokens);
|
||||
default:
|
||||
throw new \LogicException("Unknown function type");
|
||||
}
|
||||
}
|
||||
|
||||
@ -520,6 +523,7 @@ class Template extends Render {
|
||||
* @param int $deny
|
||||
* @param bool $pure_var
|
||||
* @throws \LogicException
|
||||
* @throws UnexpectedException
|
||||
* @return string
|
||||
*/
|
||||
public function parseVar(Tokenizer $tokens, $deny = 0, &$pure_var = true) {
|
||||
@ -707,6 +711,10 @@ class Template extends Render {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $after
|
||||
* @return bool|string
|
||||
*/
|
||||
private function _getMoreSubstr($after) {
|
||||
$end = strpos($this->_src, $after, $this->_pos);
|
||||
$end = strpos($this->_src, "}", $end);
|
||||
@ -874,13 +882,25 @@ class Template extends Render {
|
||||
throw new TokenizeException("Unexpected token '".$tokens->current()."' in argument list");
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse first unnamed argument
|
||||
*
|
||||
* @param Tokenizer $tokens
|
||||
* @param string $static
|
||||
* @return mixed|string
|
||||
*/
|
||||
public function parseFirstArg(Tokenizer $tokens, &$static) {
|
||||
if($tokens->is(T_CONSTANT_ENCAPSED_STRING)) {
|
||||
if($tokens->isNext('|')) {
|
||||
return $this->parseExp($tokens, true);
|
||||
} else {
|
||||
$str = $tokens->getAndNext();
|
||||
$static = stripslashes(substr($str, 1, -1));
|
||||
return $str;
|
||||
}
|
||||
} elseif($tokens->is(Tokenizer::MACRO_STRING)) {
|
||||
return $static = $tokens->getAndNext();
|
||||
$static = $tokens->getAndNext();
|
||||
return '"'.addslashes($static).'"';
|
||||
} else {
|
||||
return $this->parseExp($tokens, true);
|
||||
}
|
||||
@ -925,8 +945,6 @@ class Template extends Render {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
class CompileException extends \ErrorException {}
|
||||
class SecurityException extends CompileException {}
|
||||
class ImproperUseException extends \LogicException {}
|
@ -48,7 +48,7 @@ class Tokenizer {
|
||||
*/
|
||||
const MACRO_INCDEC = 1005;
|
||||
/**
|
||||
* Boolean operations: &&, ||, or, xor
|
||||
* Boolean operations: &&, ||, or, xor, and
|
||||
*/
|
||||
const MACRO_BOOLEAN = 1006;
|
||||
/**
|
||||
@ -98,7 +98,7 @@ class Tokenizer {
|
||||
"+" => 1, "-" => 1, "*" => 1, "/" => 1, ">" => 1, "<" => 1, "^" => 1, "%" => 1, "&" => 1
|
||||
),
|
||||
self::MACRO_BOOLEAN => array(
|
||||
\T_LOGICAL_OR => 1, \T_LOGICAL_XOR => 1, \T_BOOLEAN_AND => 1, \T_BOOLEAN_OR => 1
|
||||
\T_LOGICAL_OR => 1, \T_LOGICAL_XOR => 1, \T_BOOLEAN_AND => 1, \T_BOOLEAN_OR => 1, \T_LOGICAL_AND => 1
|
||||
),
|
||||
self::MACRO_MATH => array(
|
||||
"+" => 1, "-" => 1, "*" => 1, "/" => 1, "^" => 1, "%" => 1, "&" => 1, "|" => 1
|
||||
@ -171,22 +171,6 @@ class Tokenizer {
|
||||
$this->_last_no = $this->tokens[$this->_max][3];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the filter callback. Token may be changed by reference or skipped if callback return false.
|
||||
*
|
||||
* @param $callback
|
||||
*/
|
||||
public function filter(\Closure $callback) {
|
||||
$tokens = array();
|
||||
foreach($this->tokens as $token) {
|
||||
if($callback($token) !== false) {
|
||||
$tokens[] = $token;
|
||||
}
|
||||
}
|
||||
$this->tokens = $tokens;
|
||||
$this->_max = count($this->tokens) - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current element
|
||||
*
|
||||
@ -270,20 +254,6 @@ class Tokenizer {
|
||||
return $this->current();
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatenate tokens from the current one to one of the specified and returns the string.
|
||||
* @param string|int $token
|
||||
* @param ...
|
||||
* @return string
|
||||
*/
|
||||
public function getStringUntil($token/*, $token2 */) {
|
||||
$str = '';
|
||||
while($this->valid() && !$this->_valid(func_get_args(), $this->curr[0])) {
|
||||
$str .= $this->curr[1].$this->curr[2];
|
||||
$this->next();
|
||||
}
|
||||
return $str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return substring. This method doesn't move pointer.
|
||||
@ -313,11 +283,10 @@ class Tokenizer {
|
||||
if($this->curr) {
|
||||
$cur = $this->curr[1];
|
||||
$this->next();
|
||||
return $cur;
|
||||
} else {
|
||||
throw new UnexpectedException($this, func_get_args());
|
||||
}
|
||||
|
||||
return $cur;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -396,7 +365,6 @@ class Tokenizer {
|
||||
|
||||
/**
|
||||
* Return the key of the current element
|
||||
* @link http://php.net/manual/en/iterator.key.php
|
||||
* @return mixed scalar on success, or null on failure.
|
||||
*/
|
||||
public function key() {
|
||||
@ -405,7 +373,6 @@ class Tokenizer {
|
||||
|
||||
/**
|
||||
* Checks if current position is valid
|
||||
* @link http://php.net/manual/en/iterator.valid.php
|
||||
* @return boolean The return value will be casted to boolean and then evaluated.
|
||||
* Returns true on success or false on failure.
|
||||
*/
|
||||
@ -413,13 +380,6 @@ class Tokenizer {
|
||||
return (bool)$this->curr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewind the Iterator to the first element. Disabled.
|
||||
* @link http://php.net/manual/en/iterator.rewind.php
|
||||
* @return void Any returned value is ignored.
|
||||
*/
|
||||
public function rewind() {}
|
||||
|
||||
/**
|
||||
* Get token name
|
||||
* @static
|
||||
@ -438,18 +398,6 @@ class Tokenizer {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whitespace of current token
|
||||
* @return null
|
||||
*/
|
||||
public function getWhiteSpace() {
|
||||
if($this->curr) {
|
||||
return $this->curr[2];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip specific token or throw an exception
|
||||
*
|
||||
@ -470,19 +418,6 @@ class Tokenizer {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip specific token or do nothing
|
||||
*
|
||||
* @param int|string $token1
|
||||
* @return Tokenizer
|
||||
*/
|
||||
public function skipIf($token1/*, $token2, ...*/) {
|
||||
if($this->_valid(func_get_args(), $this->curr[0])) {
|
||||
$this->next();
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check current token's type
|
||||
*
|
||||
@ -498,16 +433,6 @@ class Tokenizer {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Count elements of an object
|
||||
* @link http://php.net/manual/en/countable.count.php
|
||||
* @return int The custom count as an integer.
|
||||
* The return value is cast to an integer.
|
||||
*/
|
||||
public function count() {
|
||||
return $this->_max;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get tokens near current token
|
||||
* @param int $before count tokens before current token
|
||||
@ -636,8 +561,6 @@ class UnexpectedException extends TokenizeException {
|
||||
}
|
||||
if(!$tokens->curr) {
|
||||
$this->message = "Unexpected end of ".($where?:"expression")."$expect";
|
||||
} elseif($tokens->curr[1] === "\n") {
|
||||
$this->message = "Unexpected new line$expect";
|
||||
} elseif($tokens->curr[0] === T_WHITESPACE) {
|
||||
$this->message = "Unexpected whitespace$expect";
|
||||
} else {
|
||||
|
@ -17,6 +17,18 @@ class TestCase extends \PHPUnit_Framework_TestCase {
|
||||
$this->aspect = Aspect::factory(ASPECT_RESOURCES.'/template', ASPECT_RESOURCES.'/compile');
|
||||
}
|
||||
|
||||
public static function setUpBeforeClass() {
|
||||
if(!file_exists(ASPECT_RESOURCES.'/template')) {
|
||||
mkdir(ASPECT_RESOURCES.'/template', 0777, true);
|
||||
} else {
|
||||
Misc::clean(ASPECT_RESOURCES.'/template/');
|
||||
}
|
||||
}
|
||||
|
||||
public function tpl($name, $code) {
|
||||
file_put_contents(ASPECT_RESOURCES.'/template/'.$name, $code);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile and execute template
|
||||
*
|
||||
@ -33,6 +45,15 @@ class TestCase extends \PHPUnit_Framework_TestCase {
|
||||
$this->assertSame(Modifier::strip($result), Modifier::strip($tpl->fetch($vars), true), "Test $code");
|
||||
}
|
||||
|
||||
public function execTpl($name, $code, $vars, $result, $dump = false) {
|
||||
$this->tpl($name, $code);
|
||||
$tpl = $this->aspect->getTemplate($name);
|
||||
if($dump) {
|
||||
echo "\n========= DUMP BEGIN ===========\n".$code."\n--- to ---\n".$tpl->getBody()."\n========= DUMP END =============\n";
|
||||
}
|
||||
$this->assertSame(Modifier::strip($result, true), Modifier::strip($tpl->fetch($vars), true), "Test tpl $name");
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to compile the invalid template
|
||||
* @param string $code source of the template
|
||||
|
@ -207,25 +207,26 @@ class TemplateTest extends TestCase {
|
||||
$result3 = 'Include <b>Welcome, Master (flame@dev.null)</b> template';
|
||||
$result4 = 'Include <b>Welcome, Flame (flame@dev.null)</b> template';
|
||||
return array(
|
||||
array('Include {include file="welcome.tpl"} template', $a, $result),
|
||||
array('Include {include file=$tpl} template', $a, $result),
|
||||
array('Include {include file="$tpl"} template', $a, $result),
|
||||
array('Include {include file="{$tpl}"} template', $a, $result),
|
||||
array('Include {include file="$name.tpl"} template', $a, $result),
|
||||
array('Include {include file="{$name}.tpl"} template', $a, $result),
|
||||
array('Include {include file="{$pr_name|lower}.tpl"} template', $a, $result),
|
||||
array('Include {include file="wel{$fragment}.tpl"} template', $a, $result),
|
||||
array('Include {include file="wel{$pr_fragment|lower}.tpl"} template', $a, $result),
|
||||
array('Include {include file="welcome.tpl" username="Flame"} template', $a, $result2),
|
||||
array('Include {include file="welcome.tpl" email="flame@dev.null"} template', $a, $result3),
|
||||
array('Include {include file="welcome.tpl" username="Flame" email="flame@dev.null"} template',
|
||||
array('Include {include "welcome.tpl"} template', $a, $result),
|
||||
array('Include {include $tpl} template', $a, $result),
|
||||
array('Include {include "$tpl"} template', $a, $result),
|
||||
array('Include {include "{$tpl}"} template', $a, $result),
|
||||
array('Include {include "$name.tpl"} template', $a, $result),
|
||||
array('Include {include "{$name}.tpl"} template', $a, $result),
|
||||
array('Include {include "{$pr_name|lower}.tpl"} template', $a, $result),
|
||||
array('Include {include "wel{$fragment}.tpl"} template', $a, $result),
|
||||
array('Include {include "wel{$pr_fragment|lower}.tpl"} template', $a, $result),
|
||||
array('Include {include "welcome.tpl" username="Flame"} template', $a, $result2),
|
||||
array('Include {include "welcome.tpl" email="flame@dev.null"} template', $a, $result3),
|
||||
array('Include {include "welcome.tpl" username="Flame" email="flame@dev.null"} template',
|
||||
$a, $result4),
|
||||
);
|
||||
}
|
||||
|
||||
public static function providerIncludeInvalid() {
|
||||
return array(
|
||||
array('Include {include} template', 'Aspect\CompileException', "The tag {include} requires 'file' parameter"),
|
||||
array('Include {include} template', 'Aspect\CompileException', "Unexpected end of expression"),
|
||||
array('Include {include another="welcome.tpl"} template', 'Aspect\CompileException', "Unexpected token '=' in expression"),
|
||||
);
|
||||
}
|
||||
|
||||
@ -721,26 +722,5 @@ class TemplateTest extends TestCase {
|
||||
public function testLayersInvalid($code, $exception, $message, $options = 0) {
|
||||
$this->execError($code, $exception, $message, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group extends
|
||||
*/
|
||||
public function _testExtends() {
|
||||
echo(self::$aspect->getTemplate("parent.tpl")->getBody()); exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @group extends
|
||||
*/
|
||||
public function ___testExtends() {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -60,7 +60,6 @@ class TokenizerTest extends \PHPUnit_Framework_TestCase {
|
||||
$this->assertTrue($tokens->valid());
|
||||
$this->assertSame("3", $tokens->current());
|
||||
$this->assertSame(T_LNUMBER, $tokens->key());
|
||||
$this->assertSame(" ", $tokens->getWhiteSpace());
|
||||
$tokens->next();
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user