This commit is contained in:
Ivan Shalganov 2013-01-28 16:34:34 +04:00
parent 2b0bf5e23b
commit 439533f5ad
2 changed files with 145 additions and 145 deletions

View File

@ -63,10 +63,10 @@ class Aspect {
); );
public $blocks = array(); public $blocks = array();
/** /**
* @var array Templates storage * @var array Templates storage
*/ */
protected $_storage = array(); protected $_storage = array();
/** /**
* @var array template directory * @var array template directory
*/ */
@ -426,8 +426,8 @@ class Aspect {
if(!$_dir) { if(!$_dir) {
throw new \InvalidArgumentException("Invalid template dir: $dir"); throw new \InvalidArgumentException("Invalid template dir: $dir");
} }
$this->_tpl_path[] = $_dir; $this->_tpl_path[] = $_dir;
} }
/** /**
* Set options. May be bitwise mask of constants DENY_METHODS, DENY_INLINE_FUNCS, DENY_SET_VARS, INCLUDE_SOURCES, * Set options. May be bitwise mask of constants DENY_METHODS, DENY_INLINE_FUNCS, DENY_SET_VARS, INCLUDE_SOURCES,
@ -464,9 +464,9 @@ class Aspect {
* @param array $vars * @param array $vars
* @return Aspect\Render * @return Aspect\Render
*/ */
public function display($template, array $vars = array()) { public function display($template, array $vars = array()) {
return $this->getTemplate($template)->display($vars); return $this->getTemplate($template)->display($vars);
} }
/** /**
* *
@ -475,9 +475,9 @@ class Aspect {
* @internal param int $options * @internal param int $options
* @return mixed * @return mixed
*/ */
public function fetch($template, array $vars = array()) { public function fetch($template, array $vars = array()) {
return $this->getTemplate($template)->fetch($vars); return $this->getTemplate($template)->fetch($vars);
} }
/** /**
* Return template by name * Return template by name
@ -485,19 +485,19 @@ class Aspect {
* @param string $template * @param string $template
* @return Aspect\Template * @return Aspect\Template
*/ */
public function getTemplate($template) { public function getTemplate($template) {
if(isset($this->_storage[ $template ])) { if(isset($this->_storage[ $template ])) {
if(($this->_options & self::CHECK_MTIME) && !$this->_check($template)) { if(($this->_options & self::CHECK_MTIME) && !$this->_check($template)) {
return $this->_storage[ $template ] = $this->compile($template); return $this->_storage[ $template ] = $this->compile($template);
} else { } else {
return $this->_storage[ $template ]; return $this->_storage[ $template ];
} }
} elseif($this->_options & self::FORCE_COMPILE) { } elseif($this->_options & self::FORCE_COMPILE) {
return $this->compile($template); return $this->compile($template);
} else { } else {
return $this->_storage[ $template ] = $this->_load($template); return $this->_storage[ $template ] = $this->_load($template);
} }
} }
/** /**
* Add custom template into storage * Add custom template into storage
@ -516,16 +516,16 @@ class Aspect {
* @return Aspect\Template|mixed * @return Aspect\Template|mixed
*/ */
protected function _load($tpl) { protected function _load($tpl) {
$file_name = $this->_getHash($tpl); $file_name = $this->_getHash($tpl);
if(!is_file($this->_compile_dir."/".$file_name) || ($this->_options & self::CHECK_MTIME) && !$this->_check($tpl)) { if(!is_file($this->_compile_dir."/".$file_name) || ($this->_options & self::CHECK_MTIME) && !$this->_check($tpl)) {
return $this->compile($tpl); return $this->compile($tpl);
} else { } else {
/** @var Aspect\Render $tpl */ /** @var Aspect\Render $tpl */
$tpl = include($this->_compile_dir."/".$file_name); $tpl = include($this->_compile_dir."/".$file_name);
$tpl->setStorage($this); $tpl->setStorage($this);
return $tpl; return $tpl;
} }
} }
/** /**
* @param string $template * @param string $template

View File

@ -60,26 +60,26 @@ class Template extends Render {
* @throws CompileException * @throws CompileException
*/ */
public function __construct(Aspect $aspect, $code, $name = "runtime template") { public function __construct(Aspect $aspect, $code, $name = "runtime template") {
$this->_src = $code; $this->_src = $code;
$this->_name = $name; $this->_name = $name;
$this->_aspect = $aspect; $this->_aspect = $aspect;
$this->_options = $aspect->getOptions(); $this->_options = $aspect->getOptions();
$pos = 0; $pos = 0;
while(($start = strpos($code, '{', $pos)) !== false) { // search open-char of tags while(($start = strpos($code, '{', $pos)) !== false) { // search open-char of tags
switch($code[$start + 1]) { // check next char switch($code[$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
$pos = $start + 1; // trying finding tags after the current char $pos = $start + 1; // trying finding tags after the current char
continue 2; continue 2;
case "*": // if comment block case "*": // if comment block
$end = strpos($code, '*}', $start); // finding end of the comment block $end = strpos($code, '*}', $start); // finding end of the comment block
$frag = substr($code, $this->_pos, $start - $end); // read the comment block for precessing $frag = substr($code, $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;
} }
$end = strpos($code, '}', $start); // search close-char of the tag $end = strpos($code, '}', $start); // search close-char of the tag
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($code, $this->_pos, $start - $this->_pos); // variable $frag contains chars after last '}' and new '{' $frag = substr($code, $this->_pos, $start - $this->_pos); // variable $frag contains chars after last '}' and new '{'
$tag = substr($code, $start, $end - $start + 1); // variable $tag contains aspect tag '{...}' $tag = substr($code, $start, $end - $start + 1); // variable $tag contains aspect tag '{...}'
@ -103,7 +103,7 @@ class Template extends Render {
if(!$_line) { if(!$_line) {
$_line = $scope->line; $_line = $scope->line;
} }
$_names[] = $scope->name.' (line '.$scope->line.')'; $_names[] = $scope->name.' defined on line '.$scope->line;
} }
throw new CompileException("Unclosed tags: ".implode(", ", $_names), 0, 1, $this->_name, $_line); throw new CompileException("Unclosed tags: ".implode(", ", $_names), 0, 1, $this->_name, $_line);
} }
@ -113,7 +113,7 @@ class Template extends Render {
call_user_func_array($cb, array(&$this->_body, $this)); call_user_func_array($cb, array(&$this->_body, $this));
} }
} }
} }
public function addPostCompile($cb) { public function addPostCompile($cb) {
$this->_post[] = $cb; $this->_post[] = $cb;
@ -124,8 +124,8 @@ class Template extends Render {
* @return string * @return string
*/ */
public function getBody() { public function getBody() {
return $this->_body; return $this->_body;
} }
/** /**
* Return PHP code of PHP file of template * Return PHP code of PHP file of template
@ -133,8 +133,8 @@ 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->_name}', ".$this->_getClosureCode().", ".$this->_options.");\n"; "return new Aspect\\Render('{$this->_name}', ".$this->_getClosureCode().", ".$this->_options.");\n";
} }
/** /**
@ -208,23 +208,23 @@ class Template extends Render {
} }
$tokens = new Tokenizer($token); $tokens = new Tokenizer($token);
try { try {
switch($token[0]) { switch($token[0]) {
case '"': case '"':
case '\'': case '\'':
case '$': case '$':
$code = "echo ".$this->parseExp($tokens).";"; $code = "echo ".$this->parseExp($tokens).";";
break; break;
case '/': case '/':
$code = $this->_end($tokens); $code = $this->_end($tokens);
break; break;
default: default:
$code = $this->_parseAct($tokens); $code = $this->_parseAct($tokens);
if($code === null) { if($code === null) {
} }
break; break;
} }
if($tokens->key()) { // if tokenizer still have tokens if($tokens->key()) { // if tokenizer still have tokens
throw new UnexpectedException($tokens); throw new UnexpectedException($tokens);
@ -236,10 +236,10 @@ class Template extends Render {
} }
} catch (\LogicException $e) { } catch (\LogicException $e) {
throw new SecurityException($e->getMessage()." in {$this} line {$this->_line}, near '{".$tokens->getSnippetAsString(0,0)."' <- there", 0, 1, $this->_name, $this->_line, $e); throw new SecurityException($e->getMessage()." in {$this} line {$this->_line}, near '{".$tokens->getSnippetAsString(0,0)."' <- there", 0, 1, $this->_name, $this->_line, $e);
} catch (\Exception $e) { } catch (\Exception $e) {
throw new CompileException($e->getMessage()." in {$this} line {$this->_line}, near '{".$tokens->getSnippetAsString(0,0)."' <- there", 0, 1, $this->_name, $this->_line, $e); throw new CompileException($e->getMessage()." in {$this} line {$this->_line}, near '{".$tokens->getSnippetAsString(0,0)."' <- there", 0, 1, $this->_name, $this->_line, $e);
} }
} }
/** /**
* Close tag handler * Close tag handler
@ -248,18 +248,18 @@ class Template extends Render {
* @throws TokenizeException * @throws TokenizeException
*/ */
private function _end(Tokenizer $tokens) { private function _end(Tokenizer $tokens) {
$name = $tokens->getNext(Tokenizer::MACRO_STRING); $name = $tokens->getNext(Tokenizer::MACRO_STRING);
$tokens->next(); $tokens->next();
if(!$this->_stack) { if(!$this->_stack) {
throw new TokenizeException("Unexpected closing of the tag '$name', the tag hasn't been opened"); throw new TokenizeException("Unexpected closing of the tag '$name', the tag hasn't been opened");
} }
/** @var Scope $scope */ /** @var Scope $scope */
$scope = array_pop($this->_stack); $scope = array_pop($this->_stack);
if($scope->name !== $name) { if($scope->name !== $name) {
throw new TokenizeException("Unexpected closing of the tag '$name' (expecting closing of the tag {$scope->name}, opened on line {$scope->line})"); throw new TokenizeException("Unexpected closing of the tag '$name' (expecting closing of the tag {$scope->name}, opened on line {$scope->line})");
} }
return $scope->close($tokens); return $scope->close($tokens);
} }
/** /**
* Parse action {action ...} or {action(...) ...} * Parse action {action ...} or {action(...) ...}
@ -269,10 +269,10 @@ class Template extends Render {
* @throws TokenizeException * @throws TokenizeException
* @return string * @return string
*/ */
private function _parseAct(Tokenizer $tokens) { private function _parseAct(Tokenizer $tokens) {
if($tokens->is(Tokenizer::MACRO_STRING)) { if($tokens->is(Tokenizer::MACRO_STRING)) {
$action = $tokens->current(); $action = $tokens->current();
} else { } else {
return 'echo '.$this->parseExp($tokens).';'; return 'echo '.$this->parseExp($tokens).';';
} }
@ -286,7 +286,7 @@ class Template extends Render {
return "echo ".$this->parseExp($tokens).";"; return "echo ".$this->parseExp($tokens).";";
} }
if($act = $this->_aspect->getFunction($action)) { if($act = $this->_aspect->getFunction($action)) {
$tokens->next(); $tokens->next();
switch($act["type"]) { switch($act["type"]) {
case Aspect::BLOCK_COMPILER: case Aspect::BLOCK_COMPILER:
@ -303,20 +303,20 @@ class Template extends Render {
array_push($this->_stack, $scope); array_push($this->_stack, $scope);
return $scope->open($tokens); return $scope->open($tokens);
} }
} }
for($j = $i = count($this->_stack)-1; $i>=0; $i--) { for($j = $i = count($this->_stack)-1; $i>=0; $i--) {
if($this->_stack[$i]->hasTag($action, $j - $i)) { if($this->_stack[$i]->hasTag($action, $j - $i)) {
$tokens->next(); $tokens->next();
return $this->_stack[$i]->tag($action, $tokens); return $this->_stack[$i]->tag($action, $tokens);
} }
} }
if($tags = $this->_aspect->getTagOwners($action)) { if($tags = $this->_aspect->getTagOwners($action)) {
throw new TokenizeException("Unexpected tag '$action' (this tag can be used with '".implode("', '", $tags)."')"); throw new TokenizeException("Unexpected tag '$action' (this tag can be used with '".implode("', '", $tags)."')");
} else { } else {
throw new TokenizeException("Unexpected tag $action"); throw new TokenizeException("Unexpected tag $action");
} }
} }
/** /**
* 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.
@ -329,21 +329,21 @@ class Template extends Render {
* @throws TokenizeException * @throws TokenizeException
* @return string * @return string
*/ */
public function parseExp(Tokenizer $tokens, $required = false) { public function parseExp(Tokenizer $tokens, $required = false) {
$_exp = ""; $_exp = "";
$brackets = 0; $brackets = 0;
$term = false; $term = false;
$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)) {
$pp = $tokens->isPrev(Tokenizer::MACRO_INCDEC); $pp = $tokens->isPrev(Tokenizer::MACRO_INCDEC);
$_exp .= $this->parseVar($tokens, 0, $only_var); $_exp .= $this->parseVar($tokens, 0, $only_var);
if($only_var && !$pp) { if($only_var && !$pp) {
$term = 2; $term = 2;
} else { } else {
$term = 1; $term = 1;
} }
@ -352,23 +352,23 @@ class Template extends Render {
$brackets++; $brackets++;
$term = false; $term = false;
} elseif($term && $tokens->is(")")) { } elseif($term && $tokens->is(")")) {
if(!$brackets) { if(!$brackets) {
break; break;
} }
$brackets--; $brackets--;
$_exp .= $tokens->getAndNext(); $_exp .= $tokens->getAndNext();
$term = 1; $term = 1;
} elseif(!$term && $tokens->is(T_STRING)) { } elseif(!$term && $tokens->is(T_STRING)) {
if($tokens->isSpecialVal()) { if($tokens->isSpecialVal()) {
$_exp .= $tokens->getAndNext(); $_exp .= $tokens->getAndNext();
} elseif($tokens->isNext("(")) { } elseif($tokens->isNext("(")) {
$func = $this->_aspect->getModifier($tokens->current()); $func = $this->_aspect->getModifier($tokens->current());
$tokens->next(); $tokens->next();
$_exp .= $func.$this->parseArgs($tokens); $_exp .= $func.$this->parseArgs($tokens);
} else { } else {
break; break;
} }
$term = 1; $term = 1;
} elseif(!$term && $tokens->is(T_ISSET, T_EMPTY)) { } elseif(!$term && $tokens->is(T_ISSET, T_EMPTY)) {
$_exp .= $tokens->getAndNext(); $_exp .= $tokens->getAndNext();
if($tokens->is("(") && $tokens->isNext(T_VARIABLE)) { if($tokens->is("(") && $tokens->isNext(T_VARIABLE)) {
@ -378,28 +378,28 @@ class Template extends Render {
} }
$term = 1; $term = 1;
} elseif(!$term && $tokens->is(Tokenizer::MACRO_UNARY)) { } elseif(!$term && $tokens->is(Tokenizer::MACRO_UNARY)) {
if(!$tokens->isNext(T_VARIABLE, T_DNUMBER, T_LNUMBER, T_STRING, T_ISSET, T_EMPTY)) { if(!$tokens->isNext(T_VARIABLE, T_DNUMBER, T_LNUMBER, T_STRING, T_ISSET, T_EMPTY)) {
break; break;
} }
$_exp .= $tokens->getAndNext(); $_exp .= $tokens->getAndNext();
$term = 0; $term = 0;
} elseif($tokens->is(Tokenizer::MACRO_BINARY)) { } elseif($tokens->is(Tokenizer::MACRO_BINARY)) {
if(!$term) { if(!$term) {
throw new UnexpectedException($tokens); throw new UnexpectedException($tokens);
} }
if($tokens->isLast()) { if($tokens->isLast()) {
break; break;
} }
if($tokens->is(Tokenizer::MACRO_COND)) { if($tokens->is(Tokenizer::MACRO_COND)) {
if($cond) { if($cond) {
break; break;
} }
$cond = true; $cond = true;
} elseif ($tokens->is(Tokenizer::MACRO_BOOLEAN)) { } elseif ($tokens->is(Tokenizer::MACRO_BOOLEAN)) {
$cond = false; $cond = false;
} }
$_exp .= " ".$tokens->getAndNext()." "; $_exp .= " ".$tokens->getAndNext()." ";
$term = 0; $term = 0;
} elseif($tokens->is(Tokenizer::MACRO_INCDEC)) { } elseif($tokens->is(Tokenizer::MACRO_INCDEC)) {
if($term === 2) { if($term === 2) {
$term = 1; $term = 1;
@ -408,17 +408,17 @@ class Template extends Render {
} }
$_exp .= $tokens->getAndNext(); $_exp .= $tokens->getAndNext();
} elseif($term && !$cond && !$tokens->isLast()) { } elseif($term && !$cond && !$tokens->isLast()) {
if($tokens->is(Tokenizer::MACRO_EQUALS) && $term === 2) { if($tokens->is(Tokenizer::MACRO_EQUALS) && $term === 2) {
if($this->_options & Aspect::DENY_SET_VARS) { if($this->_options & Aspect::DENY_SET_VARS) {
throw new \LogicException("Forbidden to set a variable"); throw new \LogicException("Forbidden to set a variable");
} }
$_exp .= ' '.$tokens->getAndNext().' '; $_exp .= ' '.$tokens->getAndNext().' ';
$term = 0; $term = 0;
} else { } else {
break; break;
} }
} else { } else {
break; break;
} }
} }
@ -431,8 +431,8 @@ class Template extends Render {
if($required && $_exp === "") { if($required && $_exp === "") {
throw new UnexpectedException($tokens); throw new UnexpectedException($tokens);
} }
return $_exp; return $_exp;
} }
/** /**
@ -447,7 +447,7 @@ class Template extends Render {
* @throws \LogicException * @throws \LogicException
* @return string * @return string
*/ */
public function parseVar(Tokenizer $tokens, $deny = 0, &$pure_var = true) { public function parseVar(Tokenizer $tokens, $deny = 0, &$pure_var = true) {
$var = $tokens->get(T_VARIABLE); $var = $tokens->get(T_VARIABLE);
$pure_var = true; $pure_var = true;
if(isset(self::$sysvar[ $var ])) { if(isset(self::$sysvar[ $var ])) {
@ -524,8 +524,8 @@ class Template extends Render {
break; break;
} }
} }
return $_var; return $_var;
} }
/** /**
* Parse scalar values * Parse scalar values
@ -751,33 +751,33 @@ class Template extends Render {
* @throws TokenizeException * @throws TokenizeException
* @return string * @return string
*/ */
public function parseArgs(Tokenizer $tokens) { public function parseArgs(Tokenizer $tokens) {
$_args = "("; $_args = "(";
$tokens->next(); $tokens->next();
$arg = $colon = false; $arg = $colon = false;
while($tokens->valid()) { while($tokens->valid()) {
if(!$arg && $tokens->is(T_VARIABLE, T_STRING, "(", Tokenizer::MACRO_SCALAR, '"', Tokenizer::MACRO_UNARY, Tokenizer::MACRO_INCDEC)) { if(!$arg && $tokens->is(T_VARIABLE, T_STRING, "(", Tokenizer::MACRO_SCALAR, '"', Tokenizer::MACRO_UNARY, Tokenizer::MACRO_INCDEC)) {
$_args .= $this->parseExp($tokens, true); $_args .= $this->parseExp($tokens, true);
$arg = true; $arg = true;
$colon = false; $colon = false;
} elseif(!$arg && $tokens->is('[')) { } elseif(!$arg && $tokens->is('[')) {
$_args .= $this->parseArray($tokens); $_args .= $this->parseArray($tokens);
$arg = true; $arg = true;
$colon = false; $colon = false;
} elseif($arg && $tokens->is(',')) { } elseif($arg && $tokens->is(',')) {
$_args .= $tokens->getAndNext().' '; $_args .= $tokens->getAndNext().' ';
$arg = false; $arg = false;
$colon = true; $colon = true;
} elseif(!$colon && $tokens->is(')')) { } elseif(!$colon && $tokens->is(')')) {
$tokens->next(); $tokens->next();
return $_args.')'; return $_args.')';
} else { } else {
break; break;
} }
} }
throw new TokenizeException("Unexpected token '".$tokens->current()."' in argument list"); throw new TokenizeException("Unexpected token '".$tokens->current()."' in argument list");
} }
/** /**
* Parse parameters as $key=$value * Parse parameters as $key=$value
@ -789,8 +789,8 @@ class Template extends Render {
* @throws \Exception * @throws \Exception
* @return array * @return array
*/ */
public function parseParams(Tokenizer $tokens, array $defaults = null) { public function parseParams(Tokenizer $tokens, array $defaults = null) {
$params = array(); $params = array();
while($tokens->valid()) { while($tokens->valid()) {
if($tokens->is(Tokenizer::MACRO_STRING)) { if($tokens->is(Tokenizer::MACRO_STRING)) {
$key = $tokens->getAndNext(); $key = $tokens->getAndNext();
@ -815,7 +815,7 @@ class Template extends Render {
} }
return $params; return $params;
} }
} }