summaryrefslogtreecommitdiff
path: root/stupid_template_engine.php
diff options
context:
space:
mode:
authorKevin Chabowski <kevin@kch42.de>2013-10-25 21:44:02 +0200
committerKevin Chabowski <kevin@kch42.de>2013-10-25 21:44:02 +0200
commitdd03af69265ad686ae8daff6ecbd9df763e6d19f (patch)
treea86dae876afae2dcea9a3d70e3b513e305b38415 /stupid_template_engine.php
parent91b965181f907b169aeeff63a6c15f9b8df9d9a9 (diff)
parenteebf5cb885f266104333ac145355ef6e2599e5f6 (diff)
downloadste-dd03af69265ad686ae8daff6ecbd9df763e6d19f.tar.gz
ste-dd03af69265ad686ae8daff6ecbd9df763e6d19f.tar.bz2
ste-dd03af69265ad686ae8daff6ecbd9df763e6d19f.zip
Merge branch 'parser2'
STE gets a new, more robust parser. The previous parser was very fragile and relied on some ugly regular expressions. Other advantages of the new parser: * No precompile phase to implement short tags (?{}, ~{}), that means short tags can now be nested in any order. * The parses now uses the mb_* functions, so it should handle non-ascii text correctly. There were also some improvements to the compiler: * Various small bugfixes. * The compiler accepts and compiles tag parameters that have tags in them. The parser still doesn't accept this (and probably never will), this is mainly used to allow arbitrary code inside of short cmp tags (~{..}). There are now (finally!) tests for the language to maintain a certain quality and consistency (which STE lacked in the past, to be honest). And finally the code was reformatted. Now 1TBS is used instead of Allman.
Diffstat (limited to 'stupid_template_engine.php')
-rw-r--r--stupid_template_engine.php1497
1 files changed, 827 insertions, 670 deletions
diff --git a/stupid_template_engine.php b/stupid_template_engine.php
index 7153a99..dd90a89 100644
--- a/stupid_template_engine.php
+++ b/stupid_template_engine.php
@@ -15,76 +15,71 @@
*/
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);
+ $this->text = $text;
+ }
}
-class TagNode extends ASTNode
-{
+class TagNode extends ASTNode {
public $name;
- public $params;
- public $sub;
+ public $params = array();
+ public $sub = array();
+ public function __construct($tpl, $off, $name = "") {
+ parent::__construct($tpl, $off);
+ $this->name = $name;
+ }
}
-class VariableNode extends ASTNode
-{
+class VariableNode extends ASTNode {
public $name;
- public $arrayfields;
- public function transcompile()
- {
+ public $arrayfields = array();
+ 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->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;
}
}
@@ -102,288 +97,463 @@ class RuntimeError extends \Exception {}
*/
class FatalRuntimeError extends \Exception {}
-/* $text must start after the first opening bracket */
-function find_closing_bracket($text, $opening, $closing)
-{
- $counter = 1;
- $len = strlen($text);
- for($i = 0; $i < $len; ++$i)
- {
- switch($text[$i])
- {
- case $opening:
- ++$counter;
- break;
- case $closing:
- --$counter;
- break;
- }
- if($counter == 0)
- break;
- }
-
- if($counter > 0)
- throw new \Exception("Missing closing \"$closing\". Stop.");
+/*
+ * Class: Parser
+ * The class, where the parser lives in. Can not be constructed manually.
+ * Use the static method parse.
+ */
+class Parser {
+ private $text;
+ private $name;
+ private $off;
+ private $len;
- return $i;
-}
-
-function instance_in_array($classname, $a)
-{
- foreach($a as $v)
- {
- if($v instanceof $classname)
- return True;
- }
- return False;
-}
-
-function unescape_text($text)
-{
- return stripcslashes($text);
-}
-
-function tokenize_text($text, $tpl, $off)
-{
- $tokens = array();
- /* Find next non-escaped $-char */
- if(preg_match("/(?:(?<!\\\\)\\$)/s", $text, $match, PREG_OFFSET_CAPTURE) == 0)
- {
- $node = new TextNode($tpl, $off);
- $node->text = preg_replace("/^(?:\\n|\\r\\n|\\r)\\s*/s", "", unescape_text($text));
- return (strlen($node->text) == 0) ? array() : array($node);
- }
+ const PARSE_SHORT = 1;
+ const PARSE_TAG = 2;
- if($match[0][1] > 0)
- {
- $node = new TextNode($tpl, $off);
- $node->text = unescape_text(substr($text, 0, $match[0][1]));
- $tokens[] = $node;
- }
+ const ESCAPES_DEFAULT = '$?~{}|\\';
- if($text[$match[0][1] + 1] == "{")
- {
- try
- {
- $varend = find_closing_bracket(substr($text, $match[0][1] + 2), "{", "}") + $match[0][1] + 2;
- }
- catch(\Exception $e)
- {
- throw new ParseCompileError("Parse Error: Missing closing '}'", $tpl, $off + $match[0][1] + 1);
- }
- return array_merge(
- $tokens,
- tokenize_text("\$" . substr($text, $match[0][1] + 2, ($varend - 1) - ($match[0][1] + 1)), $tpl, $off + $match[0][1] + 2),
- tokenize_text(substr($text, $varend + 1), $tpl, $off + $varend + 1)
- );
+ private function __construct($text, $name) {
+ $this->text = $text;
+ $this->name = $name;
+ $this->off = 0;
+ $this->len = mb_strlen($text);
}
- $text = substr($text, $match[0][1] + 1);
- $off += $match[0][1] + 1;
- if(preg_match("/^[a-zA-Z0-9_]+/s", $text, $match, PREG_OFFSET_CAPTURE) == 0)
- {
- $nexttokens = tokenize_text($text, $tpl, $off);
- if($nexttokens[0] instanceof TextNode)
- $nexttokens[0]->text = "\$" . $nexttokens[0]->text;
- else
- {
- $node = new TextNode($tpl, $off);
- $node->text = "\$";
- $tokens[] = $node;
- }
- return array_merge($tokens, $nexttokens);
+ private function next($n = 1) {
+ if($n <= 0) {
+ throw new \InvalidArgumentException("\$n must be > 0");
+ }
+ $c = mb_substr($this->text, $this->off, $n);
+ $this->off = min($this->off + $n, $this->len);
+ return $c;
}
- $node = new VariableNode($tpl, $off + $match[0][1]);
- $node->name = $match[0][0];
- $node->arrayfields = array();
- $text = substr($text, $match[0][1] + strlen($match[0][0]));
- $off += $match[0][1] + strlen($match[0][0]);
- while(@$text[0] == "[")
- {
- $text = substr($text, 1);
- $off += 1;
- try
- {
- $fieldend = find_closing_bracket($text, "[", "]");
- }
- catch(\Exception $e)
- {
- throw new ParseCompileError("Parse Error: Missing closing ']'", $tpl, $off - 1);
- }
- $node->arrayfields[] = tokenize_text(substr($text, 0, $fieldend), $tpl, $off);
- $text = substr($text, $fieldend + 1);
- $off += $fieldend + 1;
+ private function back($n = 1) {
+ if($n <= 0) {
+ throw new \InvalidArgumentException("\$n must be > 0");
+ }
+ $this->off = max($this->off - $n, 0);
}
- $tokens[] = $node;
+ private function search_off($needle) {
+ return mb_strpos($this->text, $needle, $this->off);
+ }
- return strlen($text) > 0 ? array_merge($tokens, tokenize_text($text, $tpl, $off)) : $tokens;
-}
+ private function search_multi($needles) {
+ $oldoff = $this->off;
+
+ $minoff = $this->len;
+ $which = NULL;
+
+ foreach($needles as $key => $needle) {
+ if(($off = $this->search_off($needle)) === false) {
+ continue;
+ }
-function mk_ast($code, $tpl, $err_off)
-{
- $ast = array();
-
- if(preg_match("/\\<\\s*ste:([a-zA-Z0-9_]*)/s", $code, $matches, PREG_OFFSET_CAPTURE) == 0)
- return tokenize_text($code, $tpl, $err_off);
-
- $ast = tokenize_text(substr($code, 0, $matches[0][1]), $tpl, $err_off);
- $tag = new TagNode($tpl, $err_off + $matches[0][1]);
- $tag->name = $matches[1][0];
-
- $code = substr($code, $matches[0][1] + strlen($matches[0][0]));
- $err_off += $matches[0][1] + strlen($matches[0][0]);
+ if($off < $minoff) {
+ $minoff = $off;
+ $which = $key;
+ }
+ }
+
+ $this->off = $minoff + (($which === NULL) ? 0 : mb_strlen((string) $needles[$which]));
+
+ return array($which, $minoff, mb_substr($this->text, $oldoff, $minoff - $oldoff), $oldoff);
+ }
- $tag->params = array();
+ private function search($needle) {
+ $oldoff = $this->off;
+
+ $off = $this->search_off($needle);
+ if($off === false) {
+ $this->off = $this->len;
+ return array(false, mb_substr($this->text, $oldoff), $oldoff);
+ }
+
+ $this->off = $off + mb_strlen($needle);
+ return array($off, mb_substr($this->text, $oldoff, $off - $oldoff), $oldoff);
+ }
- while(preg_match("/^\\s+([a-zA-Z0-9_]+)=((?:\"(?:.*?)(?<!\\\\)\")|(?:'(?:.*?)(?<!\\\\)'))/s", $code, $matches, PREG_OFFSET_CAPTURE) > 0)
- {
- $paramval = substr($code, $matches[2][1] + 1, strlen($matches[2][0]) - 2);
- $paramval = str_replace("\\\"", "\"", $paramval);
- $paramval = str_replace("\\'", "'", $paramval);
- $tag->params[$matches[1][0]] = tokenize_text($paramval, $tpl, $err_off + $matches[2][1] + 1);
- $code = substr($code, strlen($matches[0][0]));
- $err_off += strlen($matches[0][0]);
+ private function take_while($cb) {
+ $s = "";
+ while($c = $this->next()) {
+ if(!call_user_func($cb, $c)) {
+ $this->back();
+ return $s;
+ }
+ $s .= $c;
+ }
+ return $s;
}
- if(preg_match("/^\\s*([\\/]?)\\s*\\>/s", $code, $matches) == 0)
- throw new ParseCompileError("Parse Error: Missing closing '>' in \"" . $tag->name . "\"-Tag.", $tpl, $tag->offset);
+ private function skip_ws() {
+ $this->take_while("ctype_space");
+ }
- $code = substr($code, strlen($matches[0]));
- $err_off += strlen($matches[0]);
+ private function get_name() {
+ $off = $this->off;
+ $name = $this->take_while(function($c) { return ctype_alnum($c) || ($c == "_"); });
+ if(mb_strlen($name) == 0) {
+ throw new ParseCompileError("Expected a name (alphanumeric chars + '_', at least one char)", $this->name, $off);
+ }
+ return $name;
+ }
- $tag->sub = array();
+ /*
+ * Function: parse
+ * Parses the input into an AST.
+ *
+ * You only need this function, if you want to manually trnascompile a template.
+ *
+ * Parameters:
+ * $text - The input code.
+ * $name - The name of the template.
+ *
+ * Returns:
+ * An array of <ASTNode> objects.
+ *
+ * Throws:
+ * <ParseCompileError>
+ */
+ public static function parse($text, $name) {
+ $obj = new self($text, $name);
+ $res = $obj->parse_text(
+ self::ESCAPES_DEFAULT, /* Escapes */
+ self::PARSE_SHORT | self::PARSE_TAG /* Flags */
+ );
+ return self::tidyup_ast($res[0]);
+ }
- if($matches[1][0] != "/")
- {
- /* Handling ste:comment pseudotag */
- if($tag->name == "comment")
- {
- if(preg_match("/\\<\\s*\\/\\s*ste:comment\\s*\\>/s", $code, $matches, PREG_OFFSET_CAPTURE) == 0)
- return $ast; /* Treat the whole code as comment */
- $comment_end = $matches[0][1] + strlen($matches[0][0]);
- return array_merge($ast, mk_ast(substr($code, $comment_end), $tpl, $err_off + $comment_end));
- }
-
- /* Handling ste:rawtext pseudotag */
- if($tag->name == "rawtext")
- {
- $tag = new TextNode($tpl, $tag->offset);
- if(preg_match("/\\<\\s*\\/\\s*ste:rawtext\\s*\\>/s", $code, $matches, PREG_OFFSET_CAPTURE) == 0)
- {
- /* Treat the rest of the code as rawtext */
- $tag->text = $code;
- $ast[] = $tag;
- return $ast;
- }
- $tag->text = strpos($code, 0, $matches[0][1]);
- $ast[] = $tag;
- $rawtext_end = $matches[0][1] + strlen($matches[0][0]);
- return array_merge($ast, mk_ast(substr($code, $rawtext_end), $tpl, $err_off + $rawtext_end));
- }
-
- $off = 0;
- $last_tag_start = 0;
- $tagstack = array(array($tag->name, $tag->offset - $err_off));
- while(preg_match("/\\<((?:\\s*)|(?:\\s*\\/\\s*))ste:([a-zA-Z0-9_]*)(?:\\s+(?:[a-zA-Z0-9_]+)=(?:(?:\"(?:.*?)(?<!\\\\)\")|(?:'(?:.*?)(?<!\\\\)')))*((?:\\s*)|(?:\\s*\\/\\s*))\\>/s", $code, $matches, PREG_OFFSET_CAPTURE, $off) > 0) /* RegEx from hell! Matches all <ste:> Tags. Opening, closing and self-closing ones. */
- {
- if(trim($matches[3][0]) != "/")
- {
- $closingtag = trim($matches[1][0]);
- if($closingtag[0] == "/")
- {
- list($matching_opentag, $mo_off) = array_pop($tagstack);
- if($matching_opentag != $matches[2][0])
- throw new ParseCompileError("Parse Error: Missing closing \"ste:$matching_opentag\"-Tag.", $tpl, $mo_off + $err_off);
+ private static function tidyup_ast($ast) {
+ $out = array();
+
+ $prevtext = NULL;
+ $first = true;
+
+ foreach($ast as $node) {
+ if($node instanceof TextNode) {
+ if($prevtext === NULL) {
+ $prevtext = $node;
+ } else {
+ $prevtext->text .= $node->text;
+ }
+ } else {
+ if($prevtext !== NULL) {
+ if($first) {
+ $prevtext->text = ltrim($prevtext->text);
+ }
+ if($prevtext->text != "") {
+ $out[] = $prevtext;
+ }
}
- else
- $tagstack[] = array($matches[2][0], $matches[0][1]);
+ $prevtext = NULL;
+ $first = false;
+
+ if($node instanceof TagNode) {
+ $node->sub = self::tidyup_ast($node->sub);
+ foreach($node->params as $k => &$v) {
+ $v = self::tidyup_ast($v);
+ }
+ unset($v);
+ } else { /* VariableNode */
+ foreach($node->arrayfields as &$v) {
+ $v = self::tidyup_ast($v);
+ }
+ unset($v);
+ }
+
+ $out[] = $node;
}
- $last_tag_start = $matches[0][1];
- $off = $last_tag_start + strlen($matches[0][0]);
- if(empty($tagstack))
+ }
+
+ if($prevtext !== NULL) {
+ if($first) {
+ $prevtext->text = ltrim($prevtext->text);
+ }
+ if($prevtext->text != "") {
+ $out[] = $prevtext;
+ }
+ }
+
+ return $out;
+ }
+
+ private function parse_text($escapes, $flags, $breakon = NULL, $separator = NULL, $nullaction = NULL, $opentag = NULL, $openedat = -1) {
+ $elems = array();
+ $astlist = array();
+
+ $needles = array(
+ "commentopen" => "<ste:comment>",
+ "rawopen" => "<ste:rawtext>",
+ "escape" => '\\',
+ "varcurlyopen" => '${',
+ "var" => '$',
+ );
+
+ if($flags & self::PARSE_TAG) {
+ $needles["tagopen"] = '<ste:';
+ $needles["closetagopen"] = '</ste:';
+ }
+ if($flags & self::PARSE_SHORT) {
+ $needles["shortifopen"] = '?{';
+ $needles["shortcompopen"] = '~{';
+ }
+
+ if($separator !== NULL) {
+ $needles["sep"] = $separator;
+ }
+ if($breakon !== NULL) {
+ $needles["break"] = $breakon;
+ }
+
+ for(;;) {
+ list($which, $off, $before, $offbefore) = $this->search_multi($needles);
+
+ $astlist[] = new TextNode($this->name, $offbefore, $before);
+
+ switch($which) {
+ case NULL:
+ if($nullaction === NULL) {
+ $elems[] = $astlist;
+ return $elems;
+ } else {
+ call_user_func($nullaction);
+ }
+ break;
+ case "commentopen":
+ list($off, $before, $offbefore) = $this->search("</ste:comment>");
+ if($off === false) {
+ throw new ParseCompileError("ste:comment was not closed", $this->name, $offbefore);
+ }
+ break;
+ case "rawopen":
+ $off_start = $off;
+ list($off, $before, $offbefore) = $this->search("</ste:rawtext>");
+ if($off === false) {
+ throw new ParseCompileError("ste:rawtext was not closed", $this->name, $off_start);
+ }
+ $astlist[] = new TextNode($this->name, $off_start, $before);
+ break;
+ case "tagopen":
+ $astlist[] = $this->parse_tag($off);
+ break;
+ case "closetagopen":
+ $off_start = $off;
+ $name = $this->get_name();
+ $this->skip_ws();
+ $off = $this->off;
+ if($this->next() != ">") {
+ throw new ParseCompileError("Expected '>' in closing ste-Tag", $this->name, $off);
+ }
+
+ if($opentag === NULL) {
+ throw new ParseCompileError("Found closing ste:$name tag, but no tag was opened", $this->name, $off_start);
+ }
+ if($opentag != $name) {
+ throw new ParseCompileError("Open ste:$opentag was not closed", $this->name, $openedat);
+ }
+
+ $elems[] = $astlist;
+ return $elems;
+ case "escape":
+ $c = $this->next();
+ if(mb_strpos($escapes, $c) !== false) {
+ $astlist[] = new TextNode($this->name, $off, $c);
+ } else {
+ $astlist[] = new TextNode($this->name, $off, '\\');
+ $this->back();
+ }
+ break;
+ case "shortifopen":
+ $shortelems = $this->parse_short("?{", $off);
+ if(count($shortelems) != 3) {
+ throw new ParseCompileError("A short if tag must have the form ?{..|..|..}", $this->name, $off);
+ }
+
+ list($cond, $then, $else) = $shortelems;
+ $thentag = new TagNode($this->name, $off);
+ $thentag->name = "then";
+ $thentag->sub = $then;
+
+ $elsetag = new TagNode($this->name, $off);
+ $elsetag->name = "else";
+ $elsetag->sub = $else;
+
+ $iftag = new TagNode($this->name, $off);
+ $iftag->name = "if";
+ $iftag->sub = $cond;
+ $iftag->sub[] = $thentag;
+ $iftag->sub[] = $elsetag;
+
+ $astlist[] = $iftag;
+ break;
+ case "shortcompopen":
+ $shortelems = $this->parse_short("~{", $off);
+ if(count($shortelems) != 3) {
+ throw new ParseCompileError("A short comparasion tag must have the form ~{..|..|..}", $this->name, $off);
+ }
+
+ // TODO: What will happen, if a tag was in one of the elements?
+ list($a, $op, $b) = $shortelems;
+ $cmptag = new TagNode($this->name, $off);
+ $cmptag->name = "cmp";
+ $cmptag->params["text_a"] = $a;
+ $cmptag->params["op"] = $op;
+ $cmptag->params["text_b"] = $b;
+
+ $astlist[] = $cmptag;
+ break;
+ case "sep":
+ $elems[] = $astlist;
+ $astlist = array();
+ break;
+ case "varcurlyopen":
+ $astlist[] = $this->parse_var($off, true);
+ break;
+ case "var":
+ $astlist[] = $this->parse_var($off, false);
break;
+ case "break":
+ $elems[] = $astlist;
+ return $elems;
+ }
}
- if((!empty($tagstack)) or ($tag->name != $matches[2][0]))
- throw new ParseCompileError("Parse Error: Missing closing \"ste:" . $tag->name . "\"-Tag.", $tpl, $tag->offset);
+ $elems[] = $astlist;
+ return $elems;
+ }
+
+ private function parse_short($shortname, $openedat) {
+ $tplname = $this->name;
- $tag->sub = mk_ast(substr($code, 0, $last_tag_start), $tpl, $err_off);
- $code = substr($code, $off);
- $err_off += $off;
+ return $this->parse_text(
+ self::ESCAPES_DEFAULT, /* Escapes */
+ self::PARSE_SHORT | self::PARSE_TAG, /* Flags */
+ '}', /* Break on */
+ '|', /* Separator */
+ function() use ($shortname, $tplname, $openedat) { /* NULL action */
+ throw new ParseCompileError("Unclosed $shortname", $tplname, $openedat);
+ },
+ NULL, /* Open tag */
+ $openedat /* Opened at */
+ );
}
- if($tag !== NULL)
- $ast[] = $tag;
- return array_merge($ast, strlen($code) > 0 ? mk_ast($code, $tpl, $err_off) : array());
-}
-
-/*
- * Function: precompile
- * Precompiling STE T/PL templates.
- * You only need this function, if you want to manually transcompile a template.
- *
- * Parameters:
- * $code - The input code
- *
- * Returns:
- * The precompiled code.
- */
-function precompile($code)
-{
- $code = preg_replace( /* Transform short form of comparison (~{a|op|b}) to long form */
- "/(?:(?<!\\\\)~)(?:(?<!\\\\)\\{)(.*?)(?:(?<!\\\\)\\|)(.*?)(?:(?<!\\\\)\\|)(.*?)(?:(?<!\\\\)\\})/s",
- "<ste:cmp text_a=\"\$1\" op=\"\$2\" text_b=\"\$3\" />",
- $code
- );
- $code = preg_replace( /* Transform short form of if-clause (?{cond|then|else}) to long form */
- "/(?:(?<!\\\\)\\?)(?:(?<!\\\\)\\{)(.*?)(?:(?<!\\\\)\\|)(.*?)(?:(?<!\\\\)\\|)(.*?)(?:(?<!\\\\)\\})/s",
- "<ste:if>\$1<ste:then>\$2</ste:then><ste:else>\$3</ste:else></ste:if>",
- $code
- );
- /* Unescape \? \~ \{ \} \| */
- $code = preg_replace("/(?:(?<!\\\\)\\\\\\?)/s", "?", $code);
- $code = preg_replace("/(?:(?<!\\\\)\\\\~)/s", "~", $code);
- $code = preg_replace("/(?:(?<!\\\\)\\\\\\{)/s", "{", $code);
- $code = preg_replace("/(?:(?<!\\\\)\\\\\\})/s", "}", $code);
- $code = preg_replace("/(?:(?<!\\\\)\\\\\\|)/s", "|", $code);
+ private function parse_var($openedat, $curly) {
+ $varnode = new VariableNode($this->name, $openedat);
+ $varnode->name = $this->get_name();
+ $varnode->arrayfields = $this->parse_array();
+
+ if(($curly) && ($this->next() != "}")) {
+ throw new ParseCompileError("Unclosed '\${'", $this->name, $openedat);
+ }
+ return $varnode;
+ }
- return $code;
-}
-
-/*
- * Function: parse
- * Parsing a STE T/PL template.
- * You only need this function, if you want to manually transcompile a template.
- *
- * Parameters:
- * $code - The STE T/PL code.
- * $tpl - The name of the template.
- *
- * Returns:
- * An abstract syntax tree, which can be used with <transcompile>.
- */
-function parse($code, $tpl)
-{
- return mk_ast($code, $tpl, 0);
+ private function parse_array() {
+ $tplname = $this->name;
+
+ $arrayfields = array();
+
+ while($this->next() == "[") {
+ $openedat = $this->off - 1;
+ $res = $this->parse_text(
+ self::ESCAPES_DEFAULT, /* Escapes */
+ 0, /* Flags */
+ ']', /* Break on */
+ NULL, /* Separator */
+ function() use ($tplname, $openedat) { /* NULL action */
+ throw new ParseCompileError("Unclosed array access '[...]'", $tplname, $openedat);
+ },
+ NULL, /* Open tag */
+ $openedat /* Opened at */
+ );
+ $arrayfields[] = $res[0];
+ }
+
+ $this->back();
+ return $arrayfields;
+ }
+
+ private function parse_tag($openedat) {
+ $tplname = $this->name;
+
+ $this->skip_ws();
+ $tag = new TagNode($this->name, $openedat);
+ $name = $tag->name = $this->get_name();
+ $tag->params = array();
+ $tag->sub = array();
+
+ for(;;) {
+ $this->skip_ws();
+
+ switch($this->next()) {
+ case '/': /* Self-closing tag */
+ $this->skip_ws();
+ if($this->next() != '>') {
+ throw new ParseCompileError("Unclosed opening <ste: tag (expected >)", $this->name, $openedat);
+ }
+
+ return $tag;
+ case '>':
+ $sub = $this->parse_text(
+ self::ESCAPES_DEFAULT, /* Escapes */
+ self::PARSE_SHORT | self::PARSE_TAG, /* Flags */
+ NULL, /* Break on */
+ NULL, /* Separator */
+ function() use ($name, $tplname, $openedat) { /* NULL action */
+ throw new ParseCompileError("Open ste:$name tag was not closed", $tplname, $openedat);
+ },
+ $tag->name, /* Open tag */
+ $openedat /* Opened at */
+ );
+ $tag->sub = $sub[0];
+ return $tag;
+ default:
+ $this->back();
+
+ $param = $this->get_name();
+
+ $this->skip_ws();
+ if($this->next() != '=') {
+ throw new ParseCompileError("Expected '=' after tag parameter name", $this->name, $this->off - 1);
+ }
+ $this->skip_ws();
+
+ $quot = $this->next();
+ if(($quot != '"') && ($quot != "'")) {
+ throw new ParseCompileError("Expected ' or \" after '=' of tag parameter", $this->name, $this->off - 1);
+ }
+
+ $off = $this->off - 1;
+ $paramval = $this->parse_text(
+ self::ESCAPES_DEFAULT . $quot, /* Escapes */
+ 0, /* Flags */
+ $quot, /* Break on */
+ NULL, /* Separator */
+ function() use ($quot, $tplname, $off) { /* NULL action */
+ throw new ParseCompileError("Open tag parameter value ($quot) was not closed", $tplname, $off);
+ },
+ NULL, /* Open tag */
+ $off /* Opened at */
+ );
+ $tag->params[$param] = $paramval[0];
+ }
+ }
+ }
}
-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),
@@ -396,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', '!='),
@@ -566,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)
{
@@ -613,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";
@@ -664,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";
+ }
+ list($val, $pre) = _transcompile($ast->params["value"], true);
+ $code .= $pre;
+ $code .= "\$${loopname}_valuevar = " . $val . ";\n";
- if(!empty($ast->params["key"]))
- $code .= "\$${loopname}_keyvar = " . _transcompile($ast->params["key"], True) . ";\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";
@@ -713,55 +899,58 @@ $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)
- {
- if(empty($ast->params["name"]))
+ "mktag" => function($ast) {
+ $code = "";
+
+ 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";
- $fxbody = "\$outputstack = array(); \$outputstack_i = 0;\$ste->vars['_tag_parameters'] = \$params;\n";
+ list($tagname, $tagname_pre) = _transcompile($ast->params["name"], true);
- if(!empty($ast->params["mandatory"]))
- {
+ $usemandatory = "";
+ if(!empty($ast->params["mandatory"])) {
+ $usemandatory = " use (\$mandatory_params)";
$code .= "\$outputstack[] = '';\n\$outputstack_i++;\n";
$code .= _transcompile($ast->params["mandatory"]);
$code .= "\$outputstack_i--;\n\$mandatory_params = explode('|', array_pop(\$outputstack));\n";
@@ -772,37 +961,40 @@ $ste_builtins = array(
$fxbody .= _transcompile($ast->sub);
$fxbody .= "return array_pop(\$outputstack);";
- $code .= "\$tag_fx = function(\$ste, \$params, \$sub) use (\$mandatory_params)\n{\n" . indent_code($fxbody) . "\n};\n";
+ $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";
@@ -811,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);
@@ -853,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;
@@ -883,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}";
}
@@ -918,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.
@@ -957,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;
@@ -969,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.");
-
+ }
}
}
@@ -1035,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;
@@ -1047,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
@@ -1062,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);
@@ -1083,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;
}
@@ -1107,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));
}
@@ -1148,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;
}
@@ -1175,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 \"]\".");
}
}
@@ -1233,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;
}
@@ -1253,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;
}
@@ -1276,44 +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)
- {
- $content = precompile($content);
- try
- {
- $ast = parse($content, $tpl);
+ if($mode == MODE_SOURCE) {
+ try {
+ $ast = Parser::parse($content, $tpl);
$transc = transcompile($ast);
- }
- catch(ParseCompileError $e)
- {
+ } catch(ParseCompileError $e) {
$e->rewrite($content);
throw $e;
}
@@ -1325,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;
+ }
}
/*
@@ -1344,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);
}