diff options
author | Kevin Chabowski <kevin@kch42.de> | 2013-10-25 21:10:32 +0200 |
---|---|---|
committer | Kevin Chabowski <kevin@kch42.de> | 2013-10-25 21:10:32 +0200 |
commit | 6dc747c097e096075b4726eb4aebf1ce0ed71276 (patch) | |
tree | 83725a6b18429da79f13ddc732d2b172310c155e /stupid_template_engine.php | |
parent | c3a72422e2b9754b60467387fd3cb660e0979e18 (diff) | |
download | ste-6dc747c097e096075b4726eb4aebf1ce0ed71276.tar.gz ste-6dc747c097e096075b4726eb4aebf1ce0ed71276.tar.bz2 ste-6dc747c097e096075b4726eb4aebf1ce0ed71276.zip |
short cmp tags can now contain tags.
To accomplish this, tag parameters can now be compiled, even if they
contain tags (the parser still doesn't allow this, but the compiler can now handle
this situation).
Also reformatted the code.
Diffstat (limited to 'stupid_template_engine.php')
-rw-r--r-- | stupid_template_engine.php | 820 |
1 files changed, 393 insertions, 427 deletions
diff --git a/stupid_template_engine.php b/stupid_template_engine.php index 2cfb9d0..cfb5882 100644 --- a/stupid_template_engine.php +++ b/stupid_template_engine.php @@ -15,19 +15,16 @@ */ namespace ste; -abstract class ASTNode -{ +abstract class ASTNode { public $tpl; public $offset; - public function __construct($tpl, $off) - { + public function __construct($tpl, $off) { $this->tpl = $tpl; $this->offset = $off; } } -class TextNode extends ASTNode -{ +class TextNode extends ASTNode { public $text; public function __construct($tpl, $off, $text = "") { parent::__construct($tpl, $off); @@ -35,8 +32,7 @@ class TextNode extends ASTNode } } -class TagNode extends ASTNode -{ +class TagNode extends ASTNode { public $name; public $params = array(); public $sub = array(); @@ -46,53 +42,44 @@ class TagNode extends ASTNode } } -class VariableNode extends ASTNode -{ +class VariableNode extends ASTNode { public $name; public $arrayfields = array(); - public function transcompile() - { + public function transcompile() { $varaccess = '@$ste->vars[' . (is_numeric($this->name) ? $this->name : '"' . escape_text($this->name) . '"'). ']'; - foreach($this->arrayfields as $af) - { - if((count($af) == 1) and ($af[0] instanceof TextNode) and is_numeric($af[0]->text)) + foreach($this->arrayfields as $af) { + if((count($af) == 1) and ($af[0] instanceof TextNode) and is_numeric($af[0]->text)) { $varaccess .= '[' . $af->text . ']'; - else - $varaccess .= '[' . implode(".", - array_map( - function($node) - { - if($node instanceof TextNode) - return "\"" . escape_text($node->text) . "\""; - else if($node instanceof VariableNode) - return $node->transcompile(); - }, $af - ) - ). ']'; + } else { + $varaccess .= '[' . implode(".", array_map(function($node) { + if($node instanceof TextNode) { + return "\"" . escape_text($node->text) . "\""; + } else if($node instanceof VariableNode) { + return $node->transcompile(); + } + }, $af)). ']'; + } } return $varaccess; } } -class ParseCompileError extends \Exception -{ +class ParseCompileError extends \Exception { public $msg; public $tpl; public $off; - public function __construct($msg, $tpl, $offset, $code = 0, $previous = NULL) - { + public function __construct($msg, $tpl, $offset, $code = 0, $previous = NULL) { $this->msg = $msg; $this->tpl = $tpl; $this->off = $offset; $this->message = "$msg (Template $tpl, Offset $offset)"; } - public function rewrite($code) - { + public function rewrite($code) { $line = substr_count(str_replace("\r\n", "\n", substr($code, 0, $this->off)), "\n") + 1; $this->message = "{$this->msg} (Template {$this->tpl}, Line $line)"; - $this->is_rewritten = True; + $this->is_rewritten = true; } } @@ -558,20 +545,15 @@ class Parser { } } -function indent_code($code) -{ +function indent_code($code) { return implode( "\n", - array_map( - function($line) { return "\t$line"; }, - explode("\n", $code) - ) + array_map(function($line) { return "\t$line"; }, explode("\n", $code)) ); } /* We could also just eval() the $infix_math code, but this is much cooler :-D (Parser inception) */ -function shunting_yard($infix_math) -{ +function shunting_yard($infix_math) { $operators = array( "+" => array("l", 2), "-" => array("l", 2), @@ -584,165 +566,156 @@ function shunting_yard($infix_math) ); preg_match_all("/\s*(?:(?:[+\\-\\*\\/\\^\\(\\)])|(\\d*[\\.]?\\d*))\\s*/s", $infix_math, $tokens, PREG_PATTERN_ORDER); - $tokens_raw = array_filter(array_map('trim', $tokens[0]), function($x) { return ($x === "0") or (!empty($x)); }); + $tokens_raw = array_filter(array_map('trim', $tokens[0]), function($x) { return ($x === "0") || (!empty($x)); }); $output_queue = array(); $op_stack = array(); $lastpriority = NULL; /* Make - unary, if neccessary */ $tokens = array(); - foreach($tokens_raw as $token) - { + foreach($tokens_raw as $token) { $priority = isset($operators[$token]) ? $operators[$token][1] : -1; - if(($token == "-") and (($lastpriority === NULL) or ($lastpriority >= 0))) - { + if(($token == "-") && (($lastpriority === NULL) || ($lastpriority >= 0))) { $priority = $operators["_"][1]; $tokens[] = "_"; - } - else + } else { $tokens[] = $token; + } $lastpriority = $priority; } - while(!empty($tokens)) - { + while(!empty($tokens)) { $token = array_shift($tokens); - if(is_numeric($token)) + if(is_numeric($token)) { $output_queue[] = $token; - else if($token == "(") + } else if($token == "(") { $op_stack[] = $token; - else if($token == ")") - { - $lbr_found = False; - while(!empty($op_stack)) - { + } else if($token == ")") { + $lbr_found = false; + while(!empty($op_stack)) { $op = array_pop($op_stack); - if($op == "(") - { - $lbr_found = True; + if($op == "(") { + $lbr_found = true; break; } $output_queue[] = $op; } - if(!$lbr_found) + if(!$lbr_found) { throw new RuntimeError("Bracket mismatch."); - } - else if(!isset($operators[$token])) + } + } else if(!isset($operators[$token])) { throw new RuntimeError("Invalid token ($token): Not a number, bracket or operator. Stop."); - else - { + } else { $priority = $operators[$token][1]; - if($operators[$token][0] == "l") - while((!empty($op_stack)) and ($priority <= $operators[$op_stack[count($op_stack)-1]][1])) + if($operators[$token][0] == "l") { + while((!empty($op_stack)) and ($priority <= $operators[$op_stack[count($op_stack)-1]][1])) { $output_queue[] = array_pop($op_stack); - else - while((!empty($op_stack)) and ($priority < $operators[$op_stack[count($op_stack)-1]][1])) + } + } else { + while((!empty($op_stack)) and ($priority < $operators[$op_stack[count($op_stack)-1]][1])) { $output_queue[] = array_pop($op_stack); + } + } $op_stack[] = $token; } } - while(!empty($op_stack)) - { + while(!empty($op_stack)) { $op = array_pop($op_stack); - if($op == "(") + if($op == "(") { throw new RuntimeError("Bracket mismatch..."); + } $output_queue[] = $op; } return $output_queue; } -function pop2(&$array) -{ +function pop2(&$array) { $rv = array(array_pop($array), array_pop($array)); - if(array_search(NULL, $rv, True) !== False) + if(array_search(NULL, $rv, true) !== false) { throw new RuntimeError("Not enough numbers on stack. Invalid formula."); + } return $rv; } -function calc_rpn($rpn) -{ +function calc_rpn($rpn) { $stack = array(); - foreach($rpn as $token) - { - switch($token) - { - case "+": - list($b, $a) = pop2($stack); - $stack[] = $a + $b; - break; - case "-": - list($b, $a) = pop2($stack); - $stack[] = $a - $b; - break; - case "*": - list($b, $a) = pop2($stack); - $stack[] = $a * $b; - break; - case "/": - list($b, $a) = pop2($stack); - $stack[] = $a / $b; - break; - case "^": - list($b, $a) = pop2($stack); - $stack[] = pow($a, $b); - break; - case "_": - $a = array_pop($stack); - if($a === NULL) - throw new RuntimeError("Not enough numbers on stack. Invalid formula."); - $stack[] = -$a; - break; - default: - $stack[] = $token; - break; + foreach($rpn as $token) { + switch($token) { + case "+": + list($b, $a) = pop2($stack); + $stack[] = $a + $b; + break; + case "-": + list($b, $a) = pop2($stack); + $stack[] = $a - $b; + break; + case "*": + list($b, $a) = pop2($stack); + $stack[] = $a * $b; + break; + case "/": + list($b, $a) = pop2($stack); + $stack[] = $a / $b; + break; + case "^": + list($b, $a) = pop2($stack); + $stack[] = pow($a, $b); + break; + case "_": + $a = array_pop($stack); + if($a === NULL) { + throw new RuntimeError("Not enough numbers on stack. Invalid formula."); + } + $stack[] = -$a; + break; + default: + $stack[] = $token; + break; } } return array_pop($stack); } -function loopbody($code) -{ +function loopbody($code) { return "try\n{\n" . indent_code($code) . "\n}\ncatch(\ste\BreakException \$e) { break; }\ncatch(\ste\ContinueException \$e) { continue; }\n"; } $ste_builtins = array( - "if" => function($ast) - { + "if" => function($ast) { $output = ""; $condition = array(); $then = NULL; $else = NULL; - foreach($ast->sub as $node) - { - if(($node instanceof TagNode) and ($node->name == "then")) + foreach($ast->sub as $node) { + if(($node instanceof TagNode) and ($node->name == "then")) { $then = $node->sub; - else if(($node instanceof TagNode) and ($node->name == "else")) + } else if(($node instanceof TagNode) and ($node->name == "else")) { $else = $node->sub; - else + } else { $condition[] = $node; + } } - if($then === NULL) + if($then === NULL) { throw new ParseCompileError("Transcompile error: Missing <ste:then> in <ste:if>.", $ast->tpl, $ast->offset); + } $output .= "\$outputstack[] = \"\";\n\$outputstack_i++;\n"; $output .= _transcompile($condition); $output .= "\$outputstack_i--;\nif(\$ste->evalbool(array_pop(\$outputstack)))\n{\n"; $output .= indent_code(_transcompile($then)); $output .= "\n}\n"; - if($else !== NULL) - { + if($else !== NULL) { $output .= "else\n{\n"; $output .= indent_code(_transcompile($else)); $output .= "\n}\n"; } return $output; }, - "cmp" => function($ast) - { + "cmp" => function($ast) { $operators = array( array('eq', '=='), array('neq', '!='), @@ -754,42 +727,48 @@ $ste_builtins = array( $code = ""; - if(isset($ast->params["var_b"])) - $b = '$ste->get_var_by_name(' . _transcompile($ast->params["var_b"], True) . ')'; - else if(isset($ast->params["text_b"])) - $b = _transcompile($ast->params["text_b"], True); - else + if(isset($ast->params["var_b"])) { + list($val, $pre) = _transcompile($ast->params["var_b"], true); + $code .= $pre; + $b = '$ste->get_var_by_name(' . $val . ')'; + } else if(isset($ast->params["text_b"])) { + list($b, $pre) = _transcompile($ast->params["text_b"], true); + $code .= $pre; + } else { throw new ParseCompileError("Transcompile error: neiter var_b nor text_b set in <ste:cmp>.", $ast->tpl, $ast->offset); + } - if(isset($ast->params["var_a"])) - $a = '$ste->get_var_by_name(' . _transcompile($ast->params["var_a"], True) . ')'; - else if(isset($ast->params["text_a"])) - $a = _transcompile($ast->params["text_a"], True); - else + if(isset($ast->params["var_a"])) { + list($val, $pre) = _transcompile($ast->params["var_a"], true); + $code .= $pre; + $a = '$ste->get_var_by_name(' . $val . ')'; + } else if(isset($ast->params["text_a"])) { + list($a, $pre) = _transcompile($ast->params["text_a"], true); + $code .= $pre; + } else { throw new ParseCompileError("Transcompile error: neiter var_a nor text_a set in <ste:cmp>.", $ast->tpl, $ast->offset); + } - if(!isset($ast->params["op"])) + if(!isset($ast->params["op"])) { throw new ParseCompileError("Transcompile error: op not given in <ste:cmp>.", $ast->tpl, $ast->offset); - if((count($ast->params["op"]) == 1) and ($ast->params["op"][0] instanceof TextNode)) - { + } + if((count($ast->params["op"]) == 1) and ($ast->params["op"][0] instanceof TextNode)) { /* Operator is known at compile time, this saves *a lot* of output code! */ $op = trim($ast->params["op"][0]->text); $op_php = NULL; - foreach($operators as $v) - { - if($v[0] == $op) - { + foreach($operators as $v) { + if($v[0] == $op) { $op_php = $v[1]; break; } } - if($op_php === NULL) + if($op_php === NULL) { throw new ParseCompileError("Transcompile Error: Unknown operator in <ste:cmp>", $ast->tpl, $ast->offset); + } $code .= "\$outputstack[\$outputstack_i] .= (($a) $op_php ($b)) ? 'yes' : '';\n"; - } - else - { - $code .= "switch(trim(" . _transcompile($ast->params["op"], True) . "))\n{\n\t"; + } else { + list($val, $pre) = _transcompile($ast->params["op"], true); + $code .= $pre . "switch(trim(" . $val . "))\n{\n\t"; $code .= implode("", array_map( function($op) use ($a,$b) { @@ -801,49 +780,57 @@ $ste_builtins = array( } return $code; }, - "not" => function($ast) - { + "not" => function($ast) { $code = "\$outputstack[] = '';\n\$outputstack_i++;\n"; $code .= _transcompile($ast->sub); $code .= "\$outputstack_i--;\n\$outputstack[\$outputstack_i] .= (!\$ste->evalbool(array_pop(\$outputstack))) ? 'yes' : '';\n"; return $code; }, - "even" => function($ast) - { + "even" => function($ast) { $code = "\$outputstack[] = '';\n\$outputstack_i++;\n"; $code .= _transcompile($ast->sub); $code .= "\$outputstack_i--;\n\$tmp_even = array_pop(\$outputstack);\n\$outputstack[\$outputstack_i] .= (is_numeric(\$tmp_even) and (\$tmp_even % 2 == 0)) ? 'yes' : '';\n"; return $code; }, - "for" => function($ast) - { + "for" => function($ast) { $code = ""; - $loopname = "forloop_" . str_replace(".", "_", uniqid("",True)); - if(empty($ast->params["start"])) + $loopname = "forloop_" . str_replace(".", "_", uniqid("",true)); + if(empty($ast->params["start"])) { throw new ParseCompileError("Transcompile error: Missing 'start' parameter in <ste:for>.", $ast->tpl, $ast->offset); - $code .= "\$${loopname}_start = " . _transcompile($ast->params["start"], True) . ";\n"; + } + list($val, $pre) = _transcompile($ast->params["start"], true); + $code .= $pre; + $code .= "\$${loopname}_start = " . $val . ";\n"; - if(empty($ast->params["stop"])) + if(empty($ast->params["stop"])) { throw new ParseCompileError("Transcompile error: Missing 'end' parameter in <ste:for>.", $ast->tpl, $ast->offset); - $code .= "\$${loopname}_stop = " . _transcompile($ast->params["stop"], True) . ";\n"; + } + list($val, $pre) = _transcompile($ast->params["stop"], true); + $code .= $pre; + $code .= "\$${loopname}_stop = " . $val . ";\n"; $step = NULL; /* i.e. not known at compilation time */ - if(empty($ast->params["step"])) + if(empty($ast->params["step"])) { $step = 1; - else if((count($ast->params["step"]) == 1) and ($ast->params["step"][0] instanceof TextNode)) + } else if((count($ast->params["step"]) == 1) and ($ast->params["step"][0] instanceof TextNode)) { $step = $ast->params["step"][0]->text + 0; - else - $code .= "\$${loopname}_step = " . _transcompile($ast->params["step"], True) . ";\n"; + } else { + list($val, $pre) = _transcompile($ast->params["step"], true); + $code .= $pre; + $code .= "\$${loopname}_step = " . $val . ";\n"; + } - if(!empty($ast->params["counter"])) - $code .= "\$${loopname}_countername = " . _transcompile($ast->params["counter"], True) . ";\n"; + if(!empty($ast->params["counter"])) { + list($val, $pre) = _transcompile($ast->params["counter"], true); + $code .= $pre; + $code .= "\$${loopname}_countername = " . $val . ";\n"; + } $loopbody = empty($ast->params["counter"]) ? "" : "\$ste->set_var_by_name(\$${loopname}_countername, \$${loopname}_counter);\n"; $loopbody .= _transcompile($ast->sub); $loopbody = indent_code("{\n" . loopbody(indent_code($loopbody)) . "\n}\n"); - if($step === NULL) - { + if($step === NULL) { $code .= "if(\$${loopname}_step == 0)\n\tthrow new \\ste\\RuntimeError('step can not be 0 in <ste:for>.');\n"; $code .= "if(\$${loopname}_step > 0)\n{\n"; $code .= "\tfor(\$${loopname}_counter = \$${loopname}_start; \$${loopname}_counter <= \$${loopname}_stop; \$${loopname}_counter += \$${loopname}_step)\n"; @@ -852,47 +839,58 @@ $ste_builtins = array( $code .= "\tfor(\$${loopname}_counter = \$${loopname}_start; \$${loopname}_counter >= \$${loopname}_stop; \$${loopname}_counter += \$${loopname}_step)\n"; $code .= $loopbody; $code .= "\n}\n"; - } - else if($step == 0) + } else if($step == 0) { throw new ParseCompileError("Transcompile Error: step can not be 0 in <ste:for>.", $ast->tpl, $ast->offset); - else if($step > 0) + } else if($step > 0) { $code .= "for(\$${loopname}_counter = \$${loopname}_start; \$${loopname}_counter <= \$${loopname}_stop; \$${loopname}_counter += $step)\n$loopbody\n"; - else + } else { $code .= "for(\$${loopname}_counter = \$${loopname}_start; \$${loopname}_counter >= \$${loopname}_stop; \$${loopname}_counter += $step)\n$loopbody\n"; + } return $code; }, - "foreach" => function($ast) - { - $loopname = "foreachloop_" . str_replace(".", "_", uniqid("",True)); + "foreach" => function($ast) { + $loopname = "foreachloop_" . str_replace(".", "_", uniqid("",true)); $code = ""; - if(empty($ast->params["array"])) + if(empty($ast->params["array"])) { throw new ParseCompileError("Transcompile Error: array not given in <ste:foreach>.", $ast->tpl, $ast->offset); - $code .= "\$${loopname}_arrayvar = " . _transcompile($ast->params["array"], True) . ";\n"; + } + list($val, $pre) = _transcompile($ast->params["array"], true); + $code .= $pre; + $code .= "\$${loopname}_arrayvar = " . $val . ";\n"; - if(empty($ast->params["value"])) + if(empty($ast->params["value"])) { throw new ParseCompileError("Transcompile Error: value not given in <ste:foreach>.", $ast->tpl, $ast->offset); - $code .= "\$${loopname}_valuevar = " . _transcompile($ast->params["value"], True) . ";\n"; - - if(!empty($ast->params["key"])) - $code .= "\$${loopname}_keyvar = " . _transcompile($ast->params["key"], True) . ";\n"; + } + list($val, $pre) = _transcompile($ast->params["value"], true); + $code .= $pre; + $code .= "\$${loopname}_valuevar = " . $val . ";\n"; + + if(!empty($ast->params["key"])) { + list($val, $pre) = _transcompile($ast->params["key"], true); + $code .= $pre; + $code .= "\$${loopname}_keyvar = " . $val . ";\n"; + } - if(!empty($ast->params["counter"])) - $code .= "\$${loopname}_countervar = " . _transcompile($ast->params["counter"], True) . ";\n"; + if(!empty($ast->params["counter"])) { + list($val, $pre) = _transcompile($ast->params["counter"], true); + $code .= $pre; + $code .= "\$${loopname}_countervar = " . $val . ";\n"; + } $loopbody = ""; $code .= "\$${loopname}_array = \$ste->get_var_by_name(\$${loopname}_arrayvar);\n"; $code .= "if(!is_array(\$${loopname}_array))\n\t\$${loopname}_array = array();\n"; - if(!empty($ast->params["counter"])) - { + if(!empty($ast->params["counter"])) { $code .= "\$${loopname}_counter = -1;\n"; $loopbody .= "\$${loopname}_counter++;\n\$ste->set_var_by_name(\$${loopname}_countervar, \$${loopname}_counter);\n"; } $loopbody .= "\$ste->set_var_by_name(\$${loopname}_valuevar, \$${loopname}_value);\n"; - if(!empty($ast->params["key"])) + if(!empty($ast->params["key"])) { $loopbody .= "\$ste->set_var_by_name(\$${loopname}_keyvar, \$${loopname}_key);\n"; + } $loopbody .= "\n"; $loopbody .= _transcompile($ast->sub); $loopbody = "{\n" . loopbody(indent_code($loopbody)) . "\n}\n"; @@ -901,58 +899,57 @@ $ste_builtins = array( return $code; }, - "infloop" => function($ast) - { - return "while(True)\n{\n" . indent_code(loopbody(indent_code(_transcompile($ast->sub)))) . "\n}\n"; + "infloop" => function($ast) { + return "while(true)\n{\n" . indent_code(loopbody(indent_code(_transcompile($ast->sub)))) . "\n}\n"; }, - "break" => function($ast) - { + "break" => function($ast) { return "throw new \\ste\\BreakException();\n"; }, - "continue" => function($ast) - { + "continue" => function($ast) { return "throw new \\ste\\ContinueException();\n"; }, - "block" => function($ast) - { - if(empty($ast->params["name"])) + "block" => function($ast) { + if(empty($ast->params["name"])) { throw new ParseCompileError("Transcompile Error: name missing in <ste:block>.", $ast->tpl, $ast->offset); + } - $blknamevar = "blockname_" . str_replace(".", "_", uniqid("", True)); + $blknamevar = "blockname_" . str_replace(".", "_", uniqid("", true)); - $code = "\$${blknamevar} = " . _transcompile($ast->params["name"], True) . ";\n"; + list($val, $code) = _transcompile($ast->params["name"], true); + $code .= "\$${blknamevar} = " . $val . ";\n"; - $tmpblk = uniqid("", True); + $tmpblk = uniqid("", true); $code .= "\$ste->blocks['$tmpblk'] = array_pop(\$outputstack);\n\$ste->blockorder[] = '$tmpblk';\n\$outputstack = array('');\n\$outputstack_i = 0;\n"; $code .= _transcompile($ast->sub); $code .= "\$ste->blocks[\$${blknamevar}] = array_pop(\$outputstack);\n"; - $code .= "if(array_search(\$${blknamevar}, \$ste->blockorder) === FALSE)\n\t\$ste->blockorder[] = \$${blknamevar};\n\$outputstack = array('');\n\$outputstack_i = 0;\n"; + $code .= "if(array_search(\$${blknamevar}, \$ste->blockorder) === false)\n\t\$ste->blockorder[] = \$${blknamevar};\n\$outputstack = array('');\n\$outputstack_i = 0;\n"; return $code; }, - "load" => function($ast) - { - if(empty($ast->params["name"])) + "load" => function($ast) { + if(empty($ast->params["name"])) { throw new ParseCompileError("Transcompile Error: name missing in <ste:load>.", $ast->tpl, $ast->offset); + } - return "\$outputstack[\$outputstack_i] .= \$ste->load(" . _transcompile($ast->params["name"], True) . ");\n"; + list($val, $code) = _transcompile($ast->params["name"], true); + $code .= "\$outputstack[\$outputstack_i] .= \$ste->load(" . $val . ");\n"; + return $code; }, - "mktag" => function($ast) - { + "mktag" => function($ast) { $code = ""; - if(empty($ast->params["name"])) + if(empty($ast->params["name"])) { throw new ParseCompileError("Transcompile Error: name missing in <ste:mktag>.", $ast->tpl, $ast->offset); - - $tagname = _transcompile($ast->params["name"], True); + } $fxbody = "\$outputstack = array(''); \$outputstack_i = 0;\$ste->vars['_tag_parameters'] = \$params;\n"; + list($tagname, $tagname_pre) = _transcompile($ast->params["name"], true); + $usemandatory = ""; - if(!empty($ast->params["mandatory"])) - { + if(!empty($ast->params["mandatory"])) { $usemandatory = " use (\$mandatory_params)"; $code .= "\$outputstack[] = '';\n\$outputstack_i++;\n"; $code .= _transcompile($ast->params["mandatory"]); @@ -965,36 +962,39 @@ $ste_builtins = array( $fxbody .= "return array_pop(\$outputstack);"; $code .= "\$tag_fx = function(\$ste, \$params, \$sub)" . $usemandatory . "\n{\n" . indent_code($fxbody) . "\n};\n"; + $code .= $tagname_pre; $code .= "\$ste->register_tag($tagname, \$tag_fx);\n"; return $code; }, - "tagcontent" => function($ast) - { + "tagcontent" => function($ast) { return "\$outputstack[\$outputstack_i] .= \$sub(\$ste);"; }, - "set" => function($ast) - { - if(empty($ast->params["var"])) + "set" => function($ast) { + if(empty($ast->params["var"])) { throw new ParseCompileError("Transcompile Error: var missing in <ste:set>.", $ast->tpl, $ast->offset); + } $code = "\$outputstack[] = '';\n\$outputstack_i++;\n"; $code .= _transcompile($ast->sub); $code .= "\$outputstack_i--;\n"; - $code .= "\$ste->set_var_by_name(" . _transcompile($ast->params["var"], True) . ", array_pop(\$outputstack));\n"; + list($val, $pre) = _transcompile($ast->params["var"], true); + $code .= $pre; + $code .= "\$ste->set_var_by_name(" . $val . ", array_pop(\$outputstack));\n"; return $code; }, - "get" => function($ast) - { - if(empty($ast->params["var"])) + "get" => function($ast) { + if(empty($ast->params["var"])) { throw new ParseCompileError("Transcompile Error: var missing in <ste:get>.", $ast->tpl, $ast->offset); + } - return "\$outputstack[\$outputstack_i] .= \$ste->get_var_by_name(" . _transcompile($ast->params["var"], True) . ");"; + list($val, $pre) = _transcompile($ast->params["var"], true); + $code .= $pre; + return "\$outputstack[\$outputstack_i] .= \$ste->get_var_by_name(" . $val . ");"; }, - "calc" => function($ast) - { + "calc" => function($ast) { $code = "\$outputstack[] = '';\n\$outputstack_i++;\n"; $code .= _transcompile($ast->sub); $code .= "\$outputstack_i--;\n\$outputstack[\$outputstack_i] .= \$ste->calc(array_pop(\$outputstack));\n"; @@ -1003,40 +1003,36 @@ $ste_builtins = array( } ); -function escape_text($text) -{ +function escape_text($text) { return addcslashes($text, "\r\n\t\$\0..\x1f\\\"\x7f..\xff"); } -function _transcompile($ast, $no_outputstack = False) /* The real transcompile function, does not add boilerplate code. */ -{ +function _transcompile($ast, $avoid_outputstack = false) { /* The real transcompile function, does not add boilerplate code. */ $code = ""; global $ste_builtins; $text_and_var_buffer = array(); - foreach($ast as $node) - { - if($node instanceof TextNode) + foreach($ast as $node) { + if($node instanceof TextNode) { $text_and_var_buffer[] = '"' . escape_text($node->text) . '"'; - else if($node instanceof VariableNode) + } else if($node instanceof VariableNode) { $text_and_var_buffer[] = $node->transcompile(); - else if($node instanceof TagNode) - { - if(!empty($text_and_var_buffer)) - { + } else if($node instanceof TagNode) { + if(!empty($text_and_var_buffer)) { $code .= "\$outputstack[\$outputstack_i] .= " . implode (" . ", $text_and_var_buffer) . ";\n"; $text_and_var_buffer = array(); } - if(isset($ste_builtins[$node->name])) + if(isset($ste_builtins[$node->name])) { $code .= $ste_builtins[$node->name]($node); - else - { - $paramarray = "parameters_" . str_replace(".", "_", uniqid("", True)); + } else { + $paramarray = "parameters_" . str_replace(".", "_", uniqid("", true)); $code .= "\$$paramarray = array();\n"; - foreach($node->params as $pname => $pcontent) - $code .= "\$${paramarray}['" . escape_text($pname) . "'] = " . _transcompile($pcontent, True) . ";\n"; + foreach($node->params as $pname => $pcontent) { + list($pval, $pre) = _transcompile($pcontent, true); + $code .= $pre . "\$${paramarray}['" . escape_text($pname) . "'] = " . $pval . ";\n"; + } $code .= "\$outputstack[\$outputstack_i] .= \$ste->call_tag('" . escape_text($node->name) . "', \$${paramarray}, "; $code .= empty($node->sub) ? "function(\$ste) { return ''; }" : transcompile($node->sub); @@ -1045,18 +1041,19 @@ function _transcompile($ast, $no_outputstack = False) /* The real transcompile f } } - if(!empty($text_and_var_buffer)) - { - if(!$no_outputstack) - $code .= "\$outputstack[\$outputstack_i] .= "; - $code .= implode (" . ", $text_and_var_buffer); - if(!$no_outputstack) - $code .= ";\n"; - $text_and_var_buffer = array(); + if($avoid_outputstack && ($code == "")) { + return array(implode (" . ", $text_and_var_buffer), ""); } - else if($no_outputstack) - { - $code = "\"\""; + + if(!empty($text_and_var_buffer)) { + $code .= "\$outputstack[\$outputstack_i] .= ". implode (" . ", $text_and_var_buffer) . ";\n"; + } + + if($avoid_outputstack) { + $tmpvar = "tmp_" . str_replace(".", "_", uniqid("",true)); + $code = "\$outputstack[] = '';\n\$outputstack_i++;" . $code; + $code .= "\$$tmpvar = array_pop(\$outputstack);\n\$outputstack_i--;\n"; + return array("\$$tmpvar", $code); } return $code; @@ -1075,8 +1072,7 @@ $ste_transc_boilerplate = "\$outputstack = array('');\n\$outputstack_i = 0;\n"; * Returns: * PHP code. The PHP code is an anonymous function expecting a <STECore> instance as its parameter and returns a string (everything that was not pached into a section). */ -function transcompile($ast) /* Transcompile and add some boilerplate code. */ -{ +function transcompile($ast) { /* Transcompile and add some boilerplate code. */ global $ste_transc_boilerplate; return "function(\$ste)\n{\n" . indent_code($ste_transc_boilerplate . _transcompile($ast) . "return array_pop(\$outputstack);") . "\n}"; } @@ -1110,8 +1106,7 @@ class CantSaveTemplate extends StorageAccessFailure { } * A StorageAccess implementation is used to access the templates from any storage. * This means, that you are not limited to store the Templates inside directories, you can also use a database or something else. */ -interface StorageAccess -{ +interface StorageAccess { /* * Function: load * Loading a template. @@ -1149,8 +1144,7 @@ interface StorageAccess * Class: FilesystemStorageAccess * The default <StorageAccess> implementation for loading / saving templates into a directory structure. */ -class FilesystemStorageAccess implements StorageAccess -{ +class FilesystemStorageAccess implements StorageAccess { protected $sourcedir; protected $transcompileddir; @@ -1161,62 +1155,51 @@ class FilesystemStorageAccess implements StorageAccess * $src - The directory with the sources (Writing permissions are not mandatory, because STE does not save template sources). * $transc - The directory with the transcompiled templates (the PHP instance / the HTTP Server needs writing permissions to this directory). */ - public function __construct($src, $transc) - { + public function __construct($src, $transc) { $this->sourcedir = $src; $this->transcompileddir = $transc; } - public function load($tpl, &$mode) - { + public function load($tpl, &$mode) { $src_fn = $this->sourcedir . "/" . $tpl; $transc_fn = $this->transcompileddir . "/" . $tpl . ".php"; - if($mode == MODE_SOURCE) - { + if($mode == MODE_SOURCE) { $content = @file_get_contents($src_fn); - if($content === False) + if($content === false) { throw new CantLoadTemplate("Template not found."); + } return $content; } $src_stat = @stat($src_fn); $transc_stat = @stat($transc_fn); - if(($src_stat === False) and ($transc_stat === False)) + if(($src_stat === false) and ($transc_stat === false)) { throw new CantLoadTemplate("Template not found."); - else if($transc_stat === False) - { + } else if($transc_stat === false) { $mode = MODE_SOURCE; return file_get_contents($src_fn); - } - else if($src_stat === False) - { + } else if($src_stat === false) { include($transc_fn); return $transcompile_fx; - } - else - { - if($src_stat["mtime"] > $transc_stat["mtime"]) - { + } else { + if($src_stat["mtime"] > $transc_stat["mtime"]) { $mode = MODE_SOURCE; return file_get_contents($src_fn); - } - else - { + } else { include($transc_fn); return $transcompile_fx; } } } - public function save($tpl, $data, $mode) - { + public function save($tpl, $data, $mode) { $fn = (($mode == MODE_SOURCE) ? $this->sourcedir : $this->transcompileddir) . "/" . $tpl . (($mode == MODE_TRANSCOMPILED) ? ".php" : ""); - @mkdir(dirname($fn), 0777, True); - if(file_put_contents($fn, "<?php \$transcompile_fx = $data; ?>") === False) + @mkdir(dirname($fn), 0777, true); + if(file_put_contents($fn, "<?php \$transcompile_fx = $data; ?>") === false) { throw new CantSaveTemplate("Unable to save template."); - + } } } @@ -1227,8 +1210,7 @@ class ContinueException extends \Exception { } * Class: STECore * The Core of STE */ -class STECore -{ +class STECore { private $tags; private $storage_access; private $cur_tpl_dir; @@ -1239,14 +1221,14 @@ class STECore * $blocks - Associative array of blocks (see the language definition). * $blockorder - The order of the blocks (an array) * $vars - Associative array of all template variables. Use this to pass data to your templates. - * $mute_runtime_errors - If True (default) a <RuntimeError> exception will result in no output from the tag, if False a error message will be written to output. - * $fatal_error_on_missing_tag - If True, STE will throw a <FatalRuntimeError> if a tag was called that was not registered, otherwise (default) a regular <RuntimeError> will be thrown and automatically handled by STE (see <$mute_runtime_errors>). + * $mute_runtime_errors - If true (default) a <RuntimeError> exception will result in no output from the tag, if false a error message will be written to output. + * $fatal_error_on_missing_tag - If true, STE will throw a <FatalRuntimeError> if a tag was called that was not registered, otherwise (default) a regular <RuntimeError> will be thrown and automatically handled by STE (see <$mute_runtime_errors>). */ public $blocks; public $blockorder; public $vars; - public $mute_runtime_errors = True; - public $fatal_error_on_missing_tag = False; + public $mute_runtime_errors = true; + public $fatal_error_on_missing_tag = false; /* * Constructor: __construct @@ -1254,8 +1236,7 @@ class STECore * Parameters: * $storage_access - An Instance of a <StorageAccess> implementation. */ - public function __construct($storage_access) - { + public function __construct($storage_access) { $this->storage_access = $storage_access; $this->cur_tpl_dir = "/"; STEStandardLibrary::_register_lib($this); @@ -1275,12 +1256,13 @@ class STECore * Throws: * An Exception if the tag could not be registered (if $callback is not callable or if $name is empty) */ - public function register_tag($name, $callback) - { - if(!is_callable($callback)) + public function register_tag($name, $callback) { + if(!is_callable($callback)) { throw new \Exception("Can not register tag \"$name\", not callable."); - if(empty($name)) + } + if(empty($name)) { throw new \Exception("Can not register tag, empty name."); + } $this->tags[$name] = $callback; } @@ -1299,28 +1281,24 @@ class STECore * Returns: * The output of the tag or, if a <RuntimeError> was thrown, the appropiate result (see <$mute_runtime_errors>). */ - public function call_tag($name, $params, $sub) - { - try - { - if(!isset($this->tags[$name])) - { - if($this->fatal_error_on_missing_tag) + public function call_tag($name, $params, $sub) { + try { + if(!isset($this->tags[$name])) { + if($this->fatal_error_on_missing_tag) { throw new FatalRuntimeError("Can not call tag \"$name\": Does not exist."); - else + } else { throw new RuntimeError("Can not call tag \"$name\": Does not exist."); + } } return call_user_func($this->tags[$name], $this, $params, $sub); - } - catch(RuntimeError $e) - { - if(!$this->mute_runtime_errors) + } catch(RuntimeError $e) { + if(!$this->mute_runtime_errors) { return "RuntimeError occurred on tag '$name': " . $e->getMessage(); + } } } - public function calc($expression) - { + public function calc($expression) { return calc_rpn(shunting_yard($expression)); } @@ -1340,13 +1318,13 @@ class STECore * Returns: * The output of the template. */ - public function exectemplate($tpl) - { + public function exectemplate($tpl) { $output = ""; $lastblock = $this->load($tpl); - foreach($this->blockorder as $blockname) + foreach($this->blockorder as $blockname) { $output .= $this->blocks[$blockname]; + } return $output . $lastblock; } @@ -1367,47 +1345,39 @@ class STECore * A Reference to the variable. */ - public function &get_var_reference($name, $create_if_not_exist) - { + public function &get_var_reference($name, $create_if_not_exist) { $ref = &$this->_get_var_reference($this->vars, $name, $create_if_not_exist); return $ref; } - private function &_get_var_reference(&$from, $name, $create_if_not_exist) - { + private function &_get_var_reference(&$from, $name, $create_if_not_exist) { $bracket_open = strpos($name, "["); - if($bracket_open === False) - { - if(isset($from[$name]) or $create_if_not_exist) - { + if($bracket_open === false) { + if(isset($from[$name]) or $create_if_not_exist) { $ref = &$from[$name]; return $ref; - } - else + } else { return NULL; - } - else - { + } + } else { $old_varname = $varname; $bracket_close = strpos($name, "]", $bracket_open); - if($bracket_close === FALSE) + if($bracket_close === false) { throw new RuntimeError("Invalid varname \"$varname\". Missing closing \"]\"."); + } $varname = substr($name, 0, $bracket_open); $name = substr($name, $bracket_open + 1, $bracket_close - $bracket_open - 1) . substr($name, $bracket_close + 1); - if(!is_array($from[$varname])) - { - if($create_if_not_exist) + if(!is_array($from[$varname])) { + if($create_if_not_exist) { $from[$varname] = array(); - else + } else { return NULL; + } } - try - { + try { $ref = &$this->_get_var_reference($from[$varname], $name, $create_if_not_exist); return $ref; - } - catch(Exception $e) - { + } catch(Exception $e) { throw new RuntimeError("Invalid varname \"$old_varname\". Missing closing \"]\"."); } } @@ -1425,9 +1395,8 @@ class STECore * Throws: * <RuntimeError> if the variable name can not be parsed (e.g. unbalanced brackets). */ - public function set_var_by_name($name, $val) - { - $ref = &$this->_get_var_reference($this->vars, $name, True); + public function set_var_by_name($name, $val) { + $ref = &$this->_get_var_reference($this->vars, $name, true); $ref = $val; } @@ -1445,9 +1414,8 @@ class STECore * Returns: * The variables value. */ - public function get_var_by_name($name) - { - $ref = $this->_get_var_reference($this->vars, $name, False); + public function get_var_by_name($name) { + $ref = $this->_get_var_reference($this->vars, $name, false); return $ref === NULL ? "" : $ref; } @@ -1468,43 +1436,38 @@ class STECore * Returns: * The result of the template (if $quiet == false). */ - public function load($tpl, $quiet=False) - { + public function load($tpl, $quiet = false) { $tpldir_b4 = $this->cur_tpl_dir; /* Resolve ".", ".." and protect from possible LFI */ $tpl = str_replace("\\", "/", $tpl); - if($tpl[0] != "/") + if($tpl[0] != "/") { $tpl = $this->cur_tpl_dir . "/" . $tpl; + } $pathex = array_filter(explode("/", $tpl), function($s) { return ($s != ".") and (!empty($s)); }); $pathex = array_merge($pathex); - while(($i = array_search("..", $pathex)) !== False) - { - if($i == 0) + while(($i = array_search("..", $pathex)) !== false) { + if($i == 0) { $pathex = array_slice($pathex, 1); - else + } else { $pathex = array_merge(array_slice($pathex, 0, $i), array_slice($pathex, $i + 2)); + } } $tpl = implode("/", $pathex); $this->cur_tpl_dir = dirname($tpl); - if($quiet) - { + if($quiet) { $blocks_back = clone $this->blocks; $blockorder_back = clone $this->blockorder; } $mode = MODE_TRANSCOMPILED; $content = $this->storage_access->load($tpl, $mode); - if($mode == MODE_SOURCE) - { - try - { + if($mode == MODE_SOURCE) { + try { $ast = Parser::parse($content, $tpl); $transc = transcompile($ast); - } - catch(ParseCompileError $e) - { + } catch(ParseCompileError $e) { $e->rewrite($content); throw $e; } @@ -1516,13 +1479,12 @@ class STECore $this->cur_tpl_dir = $tpldir_b4; - if($quiet) - { + if($quiet) { $this->blocks = $blocks_back; $this->blockorder = $blockorder_back; - } - else + } else { return $output; + } } /* @@ -1535,145 +1497,149 @@ class STECore * Returns: * true/false. */ - public function evalbool($txt) - { + public function evalbool($txt) { return trim($txt . "") != ""; } } -class STEStandardLibrary -{ - static public function _register_lib($ste) - { - foreach(get_class_methods(__CLASS__) as $method) - if($method[0] != "_") +class STEStandardLibrary { + static public function _register_lib($ste) { + foreach(get_class_methods(__CLASS__) as $method) { + if($method[0] != "_") { $ste->register_tag($method, array(__CLASS__, $method)); + } + } } - static public function escape($ste, $params, $sub) - { - if($ste->evalbool($params["lines"])) + static public function escape($ste, $params, $sub) { + if($ste->evalbool($params["lines"])) { return nl2br(htmlspecialchars(str_replace("\r\n", "\n", $sub($ste)))); - else + } else { return htmlspecialchars($sub($ste)); + } } - static public function strlen($ste, $params, $sub) - { + static public function strlen($ste, $params, $sub) { return strlen($sub($ste)); } - static public function arraylen($ste, $params, $sub) - { - if(empty($params["array"])) + static public function arraylen($ste, $params, $sub) { + if(empty($params["array"])) { throw new RuntimeError("Missing array parameter in <ste:arraylen>."); - $a = $ste->get_var_by_name($params["array"], False); + } + $a = $ste->get_var_by_name($params["array"], false); return (is_array($a)) ? count($a) : ""; } - static public function inc($ste, $params, $sub) - { - if(empty($params["var"])) + static public function inc($ste, $params, $sub) { + if(empty($params["var"])) { throw new RuntimeError("Missing var parameter in <ste:inc>."); - $ref = &$ste->get_var_reference($params["var"], True); + } + $ref = &$ste->get_var_reference($params["var"], true); $ref++; } - static public function dec($ste, $params, $sub) - { - if(empty($params["var"])) + static public function dec($ste, $params, $sub) { + if(empty($params["var"])) { throw new RuntimeError("Missing var parameter in <ste:dec>."); - $ref = &$ste->get_var_reference($params["var"], True); + } + $ref = &$ste->get_var_reference($params["var"], true); $ref--; } - static public function date($ste, $params, $sub) - { + static public function date($ste, $params, $sub) { return @strftime($sub($ste), empty($params["timestamp"]) ? @time() : (int) $params["timestamp"]); } - static public function in_array($ste, $params, $sub) - { - if(empty($params["array"])) + static public function in_array($ste, $params, $sub) { + if(empty($params["array"])) { throw new RuntimeError("Missing array parameter in <ste:in_array>."); - $ar = &$ste->get_var_reference($params["array"], False); - if(!is_array($ar)) + } + $ar = &$ste->get_var_reference($params["array"], false); + if(!is_array($ar)) { return ""; + } return in_array($sub($ste), $ar) ? "y" : ""; } - static public function join($ste, $params, $sub) - { - if(empty($params["array"])) + static public function join($ste, $params, $sub) { + if(empty($params["array"])) { throw new RuntimeError("Missing array parameter in <ste:join>."); + } return implode($sub($ste), $ste->get_var_by_name($params["array"])); } - static public function split($ste, $params, $sub) - { - if(empty($params["array"])) + static public function split($ste, $params, $sub) { + if(empty($params["array"])) { throw new RuntimeError("Missing array parameter in <ste:split>."); - if(empty($params["delim"])) + } + if(empty($params["delim"])) { throw new RuntimeError("Missing delim parameter in <ste:split>."); + } $ste->set_var_by_name($params["array"], explode($params["delim"], $sub($ste))); } - static public function array_add($ste, $params, $sub) - { - if(empty($params["array"])) + static public function array_add($ste, $params, $sub) { + if(empty($params["array"])) { throw new RuntimeError("Missing array parameter in <ste:array_add>."); + } - $ar = &$ste->get_var_reference($params["array"], True); - if(empty($params["key"])) + $ar = &$ste->get_var_reference($params["array"], true); + if(empty($params["key"])) { $ar[] = $sub($ste); - else + } else { $ar[$params["key"]] = $sub($ste); + } } static public function array_filter($ste, $params, $sub) { - if(empty($params["array"])) + if(empty($params["array"])) { throw new RuntimeError("Missing array parameter in <ste:array_filter>."); + } $ar = $ste->get_var_by_name($params["array"]); - if(!is_array($ar)) + if(!is_array($ar)) { throw new RuntimeError("Variable at 'array' is not an array."); + } $keys = array_keys($ar); - if(!empty($params["keep_by_keys"])) - { - $keep_by_keys = &$ste->get_var_reference($params["keep_by_keys"], False); - if(!is_array($keep_by_keys)) + if(!empty($params["keep_by_keys"])) { + $keep_by_keys = &$ste->get_var_reference($params["keep_by_keys"], false); + if(!is_array($keep_by_keys)) { throw new RuntimeError("Variable at 'keep_by_keys' is not an array."); + } $delkeys = array_filter($keys, function($k) use ($keep_by_keys) { return !in_array($k, $keep_by_keys); }); - foreach($delkeys as $dk) + foreach($delkeys as $dk) { unset($ar[$dk]); + } $keys = array_keys($ar); } - if(!empty($params["keep_by_values"])) - { - $keep_by_values = &$ste->get_var_reference($params["keep_by_values"], False); - if(!is_array($keep_by_values)) + if(!empty($params["keep_by_values"])) { + $keep_by_values = &$ste->get_var_reference($params["keep_by_values"], false); + if(!is_array($keep_by_values)) { throw new RuntimeError("Variable at 'keep_by_values' is not an array."); + } $ar = array_filter($ar, function($v) use ($keep_by_values) { return in_array($v, $keep_by_values); }); $keys = array_keys($ar); } - if(!empty($params["delete_by_keys"])) - { - $delete_by_keys = &$ste->get_var_reference($params["delete_by_keys"], False); - if(!is_array($delete_by_keys)) + if(!empty($params["delete_by_keys"])) { + $delete_by_keys = &$ste->get_var_reference($params["delete_by_keys"], false); + if(!is_array($delete_by_keys)) { throw new RuntimeError("Variable at 'delete_by_keys' is not an array."); + } $delkeys = array_filter($keys, function($k) use ($delete_by_keys) { return in_array($k, $delete_by_keys); }); - foreach($delkeys as $dk) + foreach($delkeys as $dk) { unset($ar[$dk]); + } $keys = array_keys($ar); } - if(!empty($params["delete_by_values"])) - { - $delete_by_values = &$ste->get_var_reference($params["delete_by_values"], False); - if(!is_array($delete_by_values)) + if(!empty($params["delete_by_values"])) { + $delete_by_values = &$ste->get_var_reference($params["delete_by_values"], false); + if(!is_array($delete_by_values)) { throw new RuntimeError("Variable at 'delete_by_values' is not an array."); + } $ar = array_filter($ar, function($v) use ($delete_by_values) { return !in_array($v, $delete_by_values); }); $keys = array_keys($ar); } |