summaryrefslogtreecommitdiff
path: root/STECore.php
diff options
context:
space:
mode:
Diffstat (limited to 'STECore.php')
-rw-r--r--STECore.php301
1 files changed, 301 insertions, 0 deletions
diff --git a/STECore.php b/STECore.php
new file mode 100644
index 0000000..0f81611
--- /dev/null
+++ b/STECore.php
@@ -0,0 +1,301 @@
+<?php
+
+namespace kch42\ste;
+
+/*
+ * Class: STECore
+ * The Core of STE
+ */
+class STECore {
+ private $tags;
+ private $storage_access;
+ private $cur_tpl_dir;
+
+ /*
+ * Variables: Public variables
+ *
+ * $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>).
+ */
+ public $blocks;
+ public $blockorder;
+ public $vars;
+ public $mute_runtime_errors = true;
+ public $fatal_error_on_missing_tag = false;
+
+ /*
+ * Constructor: __construct
+ *
+ * Parameters:
+ * $storage_access - An Instance of a <StorageAccess> implementation.
+ */
+ public function __construct($storage_access) {
+ $this->storage_access = $storage_access;
+ $this->cur_tpl_dir = "/";
+ STEStandardLibrary::_register_lib($this);
+ $this->vars = array();
+ $this->blockorder = array();
+ $this->blocks = array();
+ }
+
+ /*
+ * Function: register_tag
+ * Register a custom tag.
+ *
+ * Parameters:
+ * $name - The name of the tag.
+ * $callback - A callable function (This must take three parameters: The <STECore> instance, an associative array of parameters, and a function representing the tags content(This expects the <STECore> instance as its only parameter and returns its text result, i.e to get the text, you neeed to call this function with the <STECore> instance as a parameter)).
+ *
+ * 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)) {
+ throw new \Exception("Can not register tag \"$name\", not callable.");
+ }
+ if(empty($name)) {
+ throw new \Exception("Can not register tag, empty name.");
+ }
+ $this->tags[$name] = $callback;
+ }
+
+ /*
+ * Function: call_tag
+ * Calling a custom tag (builtin ones can not be called)
+ *
+ * Parameters:
+ * $name - The Tag's name
+ * $params - Associative array of parameters
+ * $sub - A callable function (expecting an <STECore> instance as it's parameter) that represents the tag's content.
+ *
+ * Throws:
+ * Might throw a <FatalRuntimeError> (see <$fatal_error_on_missing_tag>.
+ *
+ * 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) {
+ throw new FatalRuntimeError("Can not call tag \"$name\": Does not exist.");
+ } 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) {
+ return "RuntimeError occurred on tag '$name': " . $e->getMessage();
+ }
+ }
+ }
+
+ public function calc($expression) {
+ return Calc::calc($expression);
+ }
+
+ /*
+ * Function: exectemplate
+ * Executes a template and returns the result. The huge difference to <load> is that this function will also output all blocks.
+ *
+ * Parameters:
+ * $tpl - The name of the template to execute.
+ *
+ * Throws:
+ * * A <CantLoadTemplate> exception if the template could not be loaded.
+ * * A <ParseCompileError> if the template could not be parsed or transcompiled.
+ * * A <FatalRuntimeError> if a tag threw it or if a tag was not found and <$fatal_error_on_missing_tag> is true.
+ * * Might also throw different exceptions, if a external tag threw it (but they should use <RuntimeError> or <FatalRuntimeError> to make it possible for STE to handle them correctly).
+ *
+ * Returns:
+ * The output of the template.
+ */
+ public function exectemplate($tpl) {
+ $output = "";
+ $lastblock = $this->load($tpl);
+
+ foreach($this->blockorder as $blockname) {
+ $output .= $this->blocks[$blockname];
+ }
+
+ return $output . $lastblock;
+ }
+
+ /*
+ * Function: get_var_reference
+ * Get a reference to a template variable using a variable name.
+ * This can be used,if your custom tag takes a variable name as a parameter.
+ *
+ * Parameters:
+ * $name - The variables name.
+ * $create_if_not_exist - Should the variable be created, if it does not exist? Otherwise NULL will be returned, if the variable does not exist.
+ *
+ * Throws:
+ * <RuntimeError> if the variable name can not be parsed (e.g. unbalanced brackets).
+ *
+ * Returns:
+ * A Reference to the variable.
+ */
+
+ 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) {
+ $nullref = NULL;
+
+ $bracket_open = strpos($name, "[");
+ if($bracket_open === false) {
+ if(isset($from[$name]) or $create_if_not_exist) {
+ $ref = &$from[$name];
+ return $ref;
+ } else {
+ return $nullref;
+ }
+ } else {
+ $old_varname = $varname;
+ $bracket_close = strpos($name, "]", $bracket_open);
+ 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) {
+ $from[$varname] = array();
+ } else {
+ return $nullref;
+ }
+ }
+ try {
+ $ref = &$this->_get_var_reference($from[$varname], $name, $create_if_not_exist);
+ return $ref;
+ } catch(Exception $e) {
+ throw new RuntimeError("Invalid varname \"$old_varname\". Missing closing \"]\".");
+ }
+ }
+ }
+
+ /*
+ * Function: set_var_by_name
+ * Set a template variable by its name.
+ * This can be used,if your custom tag takes a variable name as a parameter.
+ *
+ * Parameters:
+ * $name - The variables name.
+ * $val - The new value.
+ *
+ * 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);
+ $ref = $val;
+ }
+
+ /*
+ * Function: get_var_by_name
+ * Get a template variable by its name.
+ * This can be used,if your custom tag takes a variable name as a parameter.
+ *
+ * Parameters:
+ * $name - The variables name.
+ *
+ * Throws:
+ * <RuntimeError> if the variable name can not be parsed (e.g. unbalanced brackets).
+ *
+ * Returns:
+ * The variables value.
+ */
+ public function get_var_by_name($name) {
+ $ref = $this->_get_var_reference($this->vars, $name, false);
+ return $ref === NULL ? "" : $ref;
+ }
+
+ /*
+ * Function: load
+ * Load a template and return its result (blocks not included, use <exectemplate> for this).
+ *
+ * Parameters:
+ * $tpl - The name of the template to be loaded.
+ * $quiet - If true, do not output anything and do not modify the blocks. This can be useful to load custom tags that are programmed in the STE Template Language. Default: false.
+ *
+ * Throws:
+ * * A <CantLoadTemplate> exception if the template could not be loaded.
+ * * A <ParseCompileError> if the template could not be parsed or transcompiled.
+ * * A <FatalRuntimeError> if a tag threw it or if a tag was not found and <$fatal_error_on_missing_tag> is true.
+ * * Might also throw different exceptions, if a external tag threw it (but they should use <RuntimeError> or <FatalRuntimeError> to make it possible for STE to handle them correctly).
+ *
+ * Returns:
+ * The result of the template (if $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] != "/") {
+ $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) {
+ $pathex = array_slice($pathex, 1);
+ } 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) {
+ $blocks_back = clone $this->blocks;
+ $blockorder_back = clone $this->blockorder;
+ }
+
+ $mode = StorageAccess::MODE_TRANSCOMPILED;
+ $content = $this->storage_access->load($tpl, $mode);
+ if($mode == StorageAccess::MODE_SOURCE) {
+ try {
+ $ast = Parser::parse($content, $tpl);
+ $transc = Transcompiler::transcompile($ast);
+ } catch(ParseCompileError $e) {
+ $e->rewrite($content);
+ throw $e;
+ }
+ $this->storage_access->save($tpl, $transc, StorageAccess::MODE_TRANSCOMPILED);
+ eval("\$content = $transc;");
+ }
+
+ $output = $content($this);
+
+ $this->cur_tpl_dir = $tpldir_b4;
+
+ if($quiet) {
+ $this->blocks = $blocks_back;
+ $this->blockorder = $blockorder_back;
+ } else {
+ return $output;
+ }
+ }
+
+ /*
+ * Function: evalbool
+ * Test, if a text represents false (an empty / only whitespace text) or true (everything else).
+ *
+ * Parameters:
+ * $txt - The text to test.
+ *
+ * Returns:
+ * true/false.
+ */
+ public function evalbool($txt) {
+ return trim($txt . "") != "";
+ }
+}