diff options
Diffstat (limited to 'STECore.php')
-rw-r--r-- | STECore.php | 301 |
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 . "") != ""; + } +} |