mirror of
https://github.com/fenom-template/fenom.git
synced 2023-08-10 21:13:07 +03:00
Improve compile method
This commit is contained in:
parent
00c7465e0a
commit
9fee4f2687
11
README.md
11
README.md
|
@ -7,10 +7,13 @@ Aspect PHP Template Engine
|
||||||
|
|
||||||
Features:
|
Features:
|
||||||
|
|
||||||
* Simple Smarty-like syntax
|
* Simple Smarty-like [syntax](./docs/syntax.md)
|
||||||
* Fast
|
* [Fast](./docs/benchmark.md)
|
||||||
* Secure
|
* [Secure](./docs/settings.md)
|
||||||
* No one regexp
|
* Without regexp
|
||||||
|
* [Flexible](./docs/main.md#extends)
|
||||||
|
* [Lightweight](./docs/benchmark.md#satistic)
|
||||||
|
* Easy to use
|
||||||
|
|
||||||
Primitive template
|
Primitive template
|
||||||
|
|
||||||
|
|
|
@ -102,18 +102,18 @@ class Aspect {
|
||||||
* @var array of modifiers [modifier_name => callable]
|
* @var array of modifiers [modifier_name => callable]
|
||||||
*/
|
*/
|
||||||
protected $_modifiers = array(
|
protected $_modifiers = array(
|
||||||
"upper" => 'strtoupper',
|
"upper" => 'strtoupper',
|
||||||
"up" => 'strtoupper',
|
"up" => 'strtoupper',
|
||||||
"lower" => 'strtolower',
|
"lower" => 'strtolower',
|
||||||
"low" => 'strtolower',
|
"low" => 'strtolower',
|
||||||
"date_format" => 'Aspect\Modifier::dateFormat',
|
"date_format" => 'Aspect\Modifier::dateFormat',
|
||||||
"date" => 'Aspect\Modifier::date',
|
"date" => 'Aspect\Modifier::date',
|
||||||
"truncate" => 'Aspect\Modifier::truncate',
|
"truncate" => 'Aspect\Modifier::truncate',
|
||||||
"escape" => 'Aspect\Modifier::escape',
|
"escape" => 'Aspect\Modifier::escape',
|
||||||
"e" => 'Aspect\Modifier::escape', // alias of escape
|
"e" => 'Aspect\Modifier::escape', // alias of escape
|
||||||
"unescape" => 'Aspect\Modifier::unescape',
|
"unescape" => 'Aspect\Modifier::unescape',
|
||||||
"strip" => 'Aspect\Modifier::strip',
|
"strip" => 'Aspect\Modifier::strip',
|
||||||
"default" => 'Aspect\Modifier::defaultValue'
|
"default" => 'Aspect\Modifier::defaultValue'
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -341,7 +341,7 @@ class Aspect {
|
||||||
/**
|
/**
|
||||||
* @param string $function
|
* @param string $function
|
||||||
* @param callable $callback
|
* @param callable $callback
|
||||||
* @param callable $parser
|
* @param callable|string $parser
|
||||||
* @return Aspect
|
* @return Aspect
|
||||||
*/
|
*/
|
||||||
public function addFunction($function, $callback, $parser = self::DEFAULT_FUNC_PARSER) {
|
public function addFunction($function, $callback, $parser = self::DEFAULT_FUNC_PARSER) {
|
||||||
|
@ -436,7 +436,10 @@ class Aspect {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $tag
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
public function getTagOwners($tag) {
|
public function getTagOwners($tag) {
|
||||||
$tags = array();
|
$tags = array();
|
||||||
foreach($this->_actions as $owner => $params) {
|
foreach($this->_actions as $owner => $params) {
|
||||||
|
|
|
@ -377,13 +377,12 @@ class Compiler {
|
||||||
$tpl_name = $tpl->parseFirstArg($tokens, $name);
|
$tpl_name = $tpl->parseFirstArg($tokens, $name);
|
||||||
$tpl->addPostCompile(__CLASS__."::extendBody");
|
$tpl->addPostCompile(__CLASS__."::extendBody");
|
||||||
if($name) { // static extends
|
if($name) { // static extends
|
||||||
//$tpl->_static = true;
|
|
||||||
$tpl->_extends = $tpl->getStorage()->getRawTemplate()->load($name, false);
|
$tpl->_extends = $tpl->getStorage()->getRawTemplate()->load($name, false);
|
||||||
|
$tpl->_compatible = &$tpl->_extends->_compatible;
|
||||||
$tpl->addDepend($tpl->_extends); // for valid compile-time need take template from storage
|
$tpl->addDepend($tpl->_extends); // for valid compile-time need take template from storage
|
||||||
return "";
|
return "";
|
||||||
} else { // dynamic extends
|
} else { // dynamic extends
|
||||||
$tpl->_extends = $tpl_name;
|
$tpl->_extends = $tpl_name;
|
||||||
//$tpl->_static = false;
|
|
||||||
return '$parent = $tpl->getStorage()->getTemplate('.$tpl_name.');';
|
return '$parent = $tpl->getStorage()->getTemplate('.$tpl_name.');';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -397,7 +396,14 @@ class Compiler {
|
||||||
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
|
||||||
/* @var Template $t */
|
/* @var Template $t */
|
||||||
$t = $tpl->_extends;
|
$tpl->_extends->_extended = true;
|
||||||
|
$tpl->_extends->blocks = &$tpl->blocks;
|
||||||
|
$tpl->_extends->compile();
|
||||||
|
if($tpl->_compatible) {
|
||||||
|
$body .= $tpl->_extends->_body;
|
||||||
|
} else {
|
||||||
|
$body = $tpl->_extends->_body;
|
||||||
|
}
|
||||||
if(empty($tpl->_dynamic)) {
|
if(empty($tpl->_dynamic)) {
|
||||||
do {
|
do {
|
||||||
$t->_blocks = &$tpl->_blocks;
|
$t->_blocks = &$tpl->_blocks;
|
||||||
|
@ -419,7 +425,7 @@ class Compiler {
|
||||||
$body = '<?php ob_start(); ?>'.$body.'<?php ob_end_clean(); ?>'.$t->_body;
|
$body = '<?php ob_start(); ?>'.$body.'<?php ob_end_clean(); ?>'.$t->_body;
|
||||||
}
|
}
|
||||||
} else { // dynamic extends
|
} else { // dynamic extends
|
||||||
$body .= '<?php $parent->blocks = &$tpl->blocks; $parent->display((array)$tpl); unset($tpl->blocks, $parent->blocks); ?>';
|
$body = '<?php ob_start(); ?>'.$body.'<?php ob_end_clean(); $parent->b = &$tpl->b; $parent->display((array)$tpl); unset($tpl->b, $parent->b); ?>';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -444,29 +450,8 @@ class Compiler {
|
||||||
*/
|
*/
|
||||||
public static function tagBlockOpen(Tokenizer $tokens, Scope $scope) {
|
public static function tagBlockOpen(Tokenizer $tokens, Scope $scope) {
|
||||||
$p = $scope->tpl->parseFirstArg($tokens, $name);
|
$p = $scope->tpl->parseFirstArg($tokens, $name);
|
||||||
if ($name) {
|
$scope["name"] = false;
|
||||||
$scope["name"] = $name;
|
$scope["cname"] = $p;
|
||||||
$scope["cname"] = $p;
|
|
||||||
} else {
|
|
||||||
$scope->tpl->_dynamic = true;
|
|
||||||
$scope["name"] = $scope["cname"] = $p;
|
|
||||||
}
|
|
||||||
/*if($scope->level) {
|
|
||||||
$scope->tpl->_static = false;
|
|
||||||
}*/
|
|
||||||
if(isset($scope->tpl->_extends)) { // is child
|
|
||||||
return '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 {
|
|
||||||
return 'if(isset($tpl->blocks['.$scope["cname"].'])) { echo $tpl->blocks['.$scope["cname"].']->__invoke($tpl); } else {';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -476,25 +461,59 @@ class Compiler {
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public static function tagBlockClose($tokens, Scope $scope) {
|
public static function tagBlockClose($tokens, Scope $scope) {
|
||||||
|
$tpl = $scope->tpl;
|
||||||
if(isset($scope->tpl->_extends)) { // is child
|
if(isset($tpl->_extends)) { // is child
|
||||||
if(!isset($scope->tpl->_blocks[ $scope["name"] ])) {
|
if($scope["name"]) { // is scalar name
|
||||||
$scope->tpl->_blocks[ $scope["name"] ] = $scope->getContent();
|
if(!isset($tpl->blocks[ $scope["name"] ])) { // is block still doesn't preset
|
||||||
} // dynamic extends
|
if($tpl->_compatible) { // is compatible mode
|
||||||
return '}; }';
|
$scope->replace(
|
||||||
} else { // is parent
|
'if(empty($tpl->blocks['.$scope["cname"].'])) { '.
|
||||||
if(isset($scope->tpl->_blocks)) {
|
'$tpl->b['.$scope["cname"].'] = function($tpl) {'.
|
||||||
if(isset($scope["body"])) {
|
$scope->getContent().
|
||||||
$scope->tpl->_body = $scope["body"].
|
"};".
|
||||||
'<?php if(isset($tpl->blocks['.$scope["cname"].'])) { echo $tpl->blocks['.$scope["cname"].']->__invoke($tpl); } else {?>'.
|
"}\n"
|
||||||
$scope->tpl->_blocks[ $scope["name"] ].'}';
|
);
|
||||||
return "";
|
} else {
|
||||||
|
$tpl->blocks[ $scope["name"] ] = $scope->getContent();
|
||||||
|
$scope->replace(
|
||||||
|
'$tpl->b['.$scope["cname"].'] = function($tpl) {'.
|
||||||
|
$scope->getContent().
|
||||||
|
"};\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else { // dynamic name
|
||||||
|
$tpl->_compatible = true; // go to compatible mode
|
||||||
|
$scope->replace(
|
||||||
|
'if(empty($tpl->b['.$scope["cname"].'])) { '.
|
||||||
|
'$tpl->b['.$scope["cname"].'] = function($tpl) {'.
|
||||||
|
$scope->getContent().
|
||||||
|
"};".
|
||||||
|
"}\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else { // is parent
|
||||||
|
if(isset($tpl->blocks[ $scope["name"] ])) { // has block
|
||||||
|
if($tpl->_compatible) {
|
||||||
|
$scope->tpl->replace(
|
||||||
|
'<?php if(isset($tpl->blocks['.$scope["cname"].'])) { echo $tpl->blocks['.$scope["cname"].']->__invoke($tpl); } else {?>'.
|
||||||
|
$tpl->blocks[ $scope["body"] ].
|
||||||
|
'}'
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$tpl->replace($tpl->blocks[ $scope["name"] ]);
|
||||||
}
|
}
|
||||||
return "";
|
|
||||||
} else {
|
} else {
|
||||||
return '}';
|
if(isset($tpl->_extended)) {
|
||||||
|
$scope->tpl->replace(
|
||||||
|
'<?php if(isset($tpl->blocks['.$scope["cname"].'])) { echo $tpl->blocks['.$scope["cname"].']->__invoke($tpl); } else {?>'.
|
||||||
|
$scope->getContent().
|
||||||
|
'}'
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function tagParent($tokens, Scope $scope) {
|
public static function tagParent($tokens, Scope $scope) {
|
||||||
|
|
|
@ -14,8 +14,6 @@ class Scope extends \ArrayObject {
|
||||||
* @var Template
|
* @var Template
|
||||||
*/
|
*/
|
||||||
public $tpl;
|
public $tpl;
|
||||||
public $closed = false;
|
|
||||||
public $is_next_close = false;
|
|
||||||
public $is_compiler = true;
|
public $is_compiler = true;
|
||||||
private $_action;
|
private $_action;
|
||||||
private static $count = 0;
|
private static $count = 0;
|
||||||
|
@ -108,4 +106,19 @@ class Scope extends \ArrayObject {
|
||||||
throw new \LogicException("Trying get content of non-block scope");
|
throw new \LogicException("Trying get content of non-block scope");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
* @throws \LogicException
|
||||||
|
*/
|
||||||
|
public function cutContent() {
|
||||||
|
if($pos = strpos($this->tpl->_body, "/*#{$this->id}#*/")) {
|
||||||
|
$begin = strpos($this->tpl->_body, "?>", $pos);
|
||||||
|
$content = substr($this->tpl->_body, $begin + 2);
|
||||||
|
$this->tpl->_body = substr($this->tpl->_body, 0, $begin + 1);
|
||||||
|
return $content;
|
||||||
|
} else {
|
||||||
|
throw new \LogicException("Trying cut content of non-block scope");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -134,6 +134,7 @@ class Template extends Render {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$pos = 0;
|
$pos = 0;
|
||||||
|
$frag = "";
|
||||||
while(($start = strpos($this->_src, '{', $pos)) !== false) { // search open-char of tags
|
while(($start = strpos($this->_src, '{', $pos)) !== false) { // search open-char of tags
|
||||||
switch($this->_src[$start + 1]) { // check next char
|
switch($this->_src[$start + 1]) { // check next char
|
||||||
case "\n": case "\r": case "\t": case " ": case "}": // ignore the tag
|
case "\n": case "\r": case "\t": case " ": case "}": // ignore the tag
|
||||||
|
@ -141,8 +142,8 @@ class Template extends Render {
|
||||||
continue 2;
|
continue 2;
|
||||||
case "*": // if comment block
|
case "*": // if comment block
|
||||||
$end = strpos($this->_src, '*}', $start); // finding end of the comment block
|
$end = strpos($this->_src, '*}', $start); // finding end of the comment block
|
||||||
$frag = substr($this->_src, $this->_pos, $start - $end); // read the comment block for precessing
|
$_frag = substr($this->_src, $this->_pos, $start - $end); // read the comment block for precessing
|
||||||
$this->_line += substr_count($frag, "\n"); // count skipped lines
|
$this->_line += substr_count($_frag, "\n"); // count skipped lines
|
||||||
$pos = $end + 1; // trying finding tags after the comment block
|
$pos = $end + 1; // trying finding tags after the comment block
|
||||||
continue 2;
|
continue 2;
|
||||||
}
|
}
|
||||||
|
@ -150,37 +151,33 @@ class Template extends Render {
|
||||||
if(!$end) { // if unexpected end of template
|
if(!$end) { // if unexpected end of template
|
||||||
throw new CompileException("Unclosed tag in line {$this->_line}", 0, 1, $this->_name, $this->_line);
|
throw new CompileException("Unclosed tag in line {$this->_line}", 0, 1, $this->_name, $this->_line);
|
||||||
}
|
}
|
||||||
$frag = substr($this->_src, $this->_pos, $start - $this->_pos); // variable $frag contains chars after last '}' and next '{'
|
$frag .= substr($this->_src, $this->_pos, $start - $this->_pos); // variable $frag contains chars after last '}' and next '{'
|
||||||
$tag = substr($this->_src, $start, $end - $start + 1); // variable $tag contains aspect tag '{...}'
|
$tag = substr($this->_src, $start, $end - $start + 1); // variable $tag contains aspect tag '{...}'
|
||||||
$this->_line += substr_count($this->_src, "\n", $this->_pos, $end - $start + 1); // count lines in $frag and $tag (using original text $code)
|
$this->_line += substr_count($this->_src, "\n", $this->_pos, $end - $start + 1); // count lines in $frag and $tag (using original text $code)
|
||||||
$pos = $this->_pos = $end + 1; // move search-pointer to end of the tag
|
$pos = $this->_pos = $end + 1; // move search-pointer to end of the tag
|
||||||
//if($this->_trim) { // if previous tag has trim flag
|
|
||||||
// $frag = ltrim($frag);
|
|
||||||
//}
|
|
||||||
|
|
||||||
$frag = str_replace("<?", '<?php echo "<?"; ?>'."\n", $frag);
|
if($tag[strlen($tag) - 2] === "-") {
|
||||||
|
$_tag = substr($tag, 1, -2);
|
||||||
$this->_body .= $this->_tag($tag, $frag); // dispatching tags
|
$_frag = rtrim($frag);
|
||||||
|
} else {
|
||||||
// duplicate new line http://docs.php.net/manual/en/language.basic-syntax.instruction-separation.php
|
$_tag = substr($tag, 1, -1);
|
||||||
if(substr($this->_body, -2) === "?>" && isset($this->_src[ $end+1 ])) {
|
$_frag = $frag;
|
||||||
$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->_ignore) {
|
||||||
// $frag = rtrim($frag);
|
if($_tag === '/ignore') {
|
||||||
//}
|
$this->_ignore = false;
|
||||||
//$this->_body .= $tag;
|
$this->_appendText($_frag);
|
||||||
|
} else {
|
||||||
|
$frag .= $tag;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->_appendText($_frag);
|
||||||
|
$this->_appendCode($this->_tag($_tag));
|
||||||
|
}
|
||||||
|
$frag = "";
|
||||||
}
|
}
|
||||||
$this->_body .= str_replace("<?", '<?php echo "<?"; ?>', substr($this->_src, $this->_pos));
|
$this->_appendText(substr($this->_src, $this->_pos));
|
||||||
if($this->_stack) {
|
if($this->_stack) {
|
||||||
$_names = array();
|
$_names = array();
|
||||||
$_line = 0;
|
$_line = 0;
|
||||||
|
@ -200,7 +197,28 @@ class Template extends Render {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addPostCompile($cb) {
|
/**
|
||||||
|
* Append plain text to template body
|
||||||
|
*
|
||||||
|
* @param string $text
|
||||||
|
*/
|
||||||
|
private function _appendText($text) {
|
||||||
|
$this->_body .= str_replace("<?", '<?php echo "<?"; ?>'.PHP_EOL, $text);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append PHP code to template body
|
||||||
|
*
|
||||||
|
* @param string $code
|
||||||
|
*/
|
||||||
|
private function _appendCode($code) {
|
||||||
|
$this->_body .= $code;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param callable[] $cb
|
||||||
|
*/
|
||||||
|
public function addPostCompile(array $cb) {
|
||||||
$this->_post[] = $cb;
|
$this->_post[] = $cb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -283,34 +301,15 @@ class Template extends Render {
|
||||||
/**
|
/**
|
||||||
* Internal tags router
|
* Internal tags router
|
||||||
* @param string $src
|
* @param string $src
|
||||||
* @param string $frag
|
|
||||||
* @throws UnexpectedException
|
* @throws UnexpectedException
|
||||||
* @throws CompileException
|
* @throws CompileException
|
||||||
* @throws SecurityException
|
* @throws SecurityException
|
||||||
* @return string
|
* @return string executable PHP code
|
||||||
*/
|
*/
|
||||||
private function _tag($src, &$frag) {
|
private function _tag($src) {
|
||||||
if($src[strlen($src) - 2] === "-") {
|
$tokens = new Tokenizer($src);
|
||||||
$token = substr($src, 1, -2);
|
|
||||||
//$frag = ltrim($frag);
|
|
||||||
} else {
|
|
||||||
$token = substr($src, 1, -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->_body .= $frag;
|
|
||||||
$token = trim($token);
|
|
||||||
if($this->_ignore) {
|
|
||||||
if($token === '/ignore') {
|
|
||||||
$this->_ignore = false;
|
|
||||||
return '';
|
|
||||||
} else {
|
|
||||||
return $src;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$tokens = new Tokenizer($token);
|
|
||||||
try {
|
try {
|
||||||
switch($token[0]) {
|
switch($src[0]) {
|
||||||
case '"':
|
case '"':
|
||||||
case '\'':
|
case '\'':
|
||||||
case '$':
|
case '$':
|
||||||
|
@ -428,19 +427,6 @@ class Template extends Render {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Tokenizer $tokens
|
|
||||||
*/
|
|
||||||
private function _parseMacros(Tokenizer $tokens) {
|
|
||||||
$tokens->get('.');
|
|
||||||
$name = $tokens->get(Tokenizer::MACRO_STRING);
|
|
||||||
if($tokens->is('(')) {
|
|
||||||
$tokens->skip();
|
|
||||||
} else {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse expressions. The mix of math operations, boolean operations, scalars, arrays and variables.
|
* Parse expressions. The mix of math operations, boolean operations, scalars, arrays and variables.
|
||||||
*
|
*
|
||||||
|
@ -459,7 +445,6 @@ class Template extends Render {
|
||||||
$cond = false;
|
$cond = false;
|
||||||
while($tokens->valid()) {
|
while($tokens->valid()) {
|
||||||
if(!$term && $tokens->is(Tokenizer::MACRO_SCALAR, '"', '`', T_ENCAPSED_AND_WHITESPACE)) {
|
if(!$term && $tokens->is(Tokenizer::MACRO_SCALAR, '"', '`', T_ENCAPSED_AND_WHITESPACE)) {
|
||||||
|
|
||||||
$_exp .= $this->parseScalar($tokens, true);
|
$_exp .= $this->parseScalar($tokens, true);
|
||||||
$term = 1;
|
$term = 1;
|
||||||
} elseif(!$term && $tokens->is(T_VARIABLE)) {
|
} elseif(!$term && $tokens->is(T_VARIABLE)) {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user