diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/ste/ASTNode.php | 8 | ||||
-rw-r--r-- | src/ste/Calc.php | 48 | ||||
-rw-r--r-- | src/ste/CantLoadTemplate.php | 8 | ||||
-rw-r--r-- | src/ste/CantSaveTemplate.php | 8 | ||||
-rw-r--r-- | src/ste/FatalRuntimeError.php | 6 | ||||
-rw-r--r-- | src/ste/FilesystemStorageAccess.php | 28 | ||||
-rw-r--r-- | src/ste/Misc.php | 4 | ||||
-rw-r--r-- | src/ste/ParseCompileError.php | 8 | ||||
-rw-r--r-- | src/ste/Parser.php | 114 | ||||
-rw-r--r-- | src/ste/RuntimeError.php | 8 | ||||
-rw-r--r-- | src/ste/STECore.php | 177 | ||||
-rw-r--r-- | src/ste/STEStandardLibrary.php | 74 | ||||
-rw-r--r-- | src/ste/Scope.php | 39 | ||||
-rw-r--r-- | src/ste/StorageAccess.php | 51 | ||||
-rw-r--r-- | src/ste/TagNode.php | 11 | ||||
-rw-r--r-- | src/ste/TextNode.php | 7 | ||||
-rw-r--r-- | src/ste/Transcompiler.php | 181 | ||||
-rw-r--r-- | src/ste/VariableNode.php | 13 |
18 files changed, 593 insertions, 200 deletions
diff --git a/src/ste/ASTNode.php b/src/ste/ASTNode.php index 2163161..3e74635 100644 --- a/src/ste/ASTNode.php +++ b/src/ste/ASTNode.php @@ -4,8 +4,16 @@ namespace kch42\ste; abstract class ASTNode { + /** @var string */ public $tpl; + + /** @var int */ public $offset; + + /** + * @param string $tpl + * @param int $off + */ public function __construct($tpl, $off) { $this->tpl = $tpl; diff --git a/src/ste/Calc.php b/src/ste/Calc.php index 64d2b7a..2f2d09d 100644 --- a/src/ste/Calc.php +++ b/src/ste/Calc.php @@ -2,15 +2,25 @@ namespace kch42\ste; -/* Class Calc contains static methods needed by <ste:calc /> */ +/** + * Class Calc contains static methods needed by <ste:calc /> + */ class Calc { private function __construct() { } - /* We could also just eval() the $infix_math code, but this is much cooler :-D (Parser inception) */ - public static function shunting_yard($infix_math) + /** + * Parse a mathematical expression with the shunting yard algorithm (https://en.wikipedia.org/wiki/Shunting-yard_algorithm) + * + * We could also just eval() the $infix_math code, but this is much cooler :-D (Parser inception) + + * @param string $infix_math + * @return array + * @throws RuntimeError + */ + private static function shunting_yard($infix_math) { $operators = array( "+" => array("l", 2), @@ -68,11 +78,17 @@ class Calc } else { $priority = $operators[$token][1]; if ($operators[$token][0] == "l") { - while ((!empty($op_stack)) and ($priority <= $operators[$op_stack[count($op_stack)-1]][1])) { + while ( + !empty($op_stack) + && $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])) { + while ( + !empty($op_stack) + && $priority < $operators[$op_stack[count($op_stack)-1]][1] + ) { $output_queue[] = array_pop($op_stack); } } @@ -91,7 +107,12 @@ class Calc return $output_queue; } - public static function pop2(&$array) + /** + * @param array $array + * @return array + * @throws RuntimeError + */ + private static function pop2(&$array) { $rv = array(array_pop($array), array_pop($array)); if (array_search(null, $rv, true) !== false) { @@ -100,7 +121,12 @@ class Calc return $rv; } - public static function calc_rpn($rpn) + /** + * @param array $rpn A mathematical expression in reverse polish notation + * @return int|float + * @throws RuntimeError + */ + private static function calc_rpn($rpn) { $stack = array(); foreach ($rpn as $token) { @@ -140,6 +166,14 @@ class Calc return array_pop($stack); } + /** + * Calculate a simple mathematical expression. Supported operators are +, -, *, /, ^. + * You can use ( and ) to group expressions together. + * + * @param string $expr + * @return float|int + * @throws RuntimeError + */ public static function calc($expr) { return self::calc_rpn(self::shunting_yard($expr)); diff --git a/src/ste/CantLoadTemplate.php b/src/ste/CantLoadTemplate.php index 20200d6..92604cc 100644 --- a/src/ste/CantLoadTemplate.php +++ b/src/ste/CantLoadTemplate.php @@ -1,13 +1,9 @@ <?php -// File: CantLoadTemplate.php - -// Namespace: kch42\ste namespace kch42\ste; -/* - * Class: CantLoadTemplate - * An exception that a <StorageAccess> implementation can throw, if it is unable to load a template. +/** + * An exception that a {@see StorageAccess} implementation can throw, if it is unable to load a template. */ class CantLoadTemplate extends StorageAccessFailure { diff --git a/src/ste/CantSaveTemplate.php b/src/ste/CantSaveTemplate.php index edbb93e..92cdfe7 100644 --- a/src/ste/CantSaveTemplate.php +++ b/src/ste/CantSaveTemplate.php @@ -1,13 +1,9 @@ <?php -// File: CantSaveTemplate.php - -// Namespace: kch42\ste namespace kch42\ste; -/* - * Class: CantSaveTemplate - * An exception that a <StorageAccess> implementation can throw, if it is unable to save a template. +/** + * An exception that a {@see StorageAccess} implementation can throw, if it is unable to save a template. */ class CantSaveTemplate extends StorageAccessFailure { diff --git a/src/ste/FatalRuntimeError.php b/src/ste/FatalRuntimeError.php index ab40af5..3f5183f 100644 --- a/src/ste/FatalRuntimeError.php +++ b/src/ste/FatalRuntimeError.php @@ -1,12 +1,8 @@ <?php -// File: FatalRuntimeError.php - -// Namespace: kch42\ste namespace kch42\ste; -/* - * Class: FatalRuntimeError +/** * An Exception a tag can throw, if a fatal (irreparable) runtime error occurred. * This Exception will always "bubble up" so you probably want to catch them. Remember that this exception is also in the namespace ste! */ diff --git a/src/ste/FilesystemStorageAccess.php b/src/ste/FilesystemStorageAccess.php index d477e36..cb2fe7f 100644 --- a/src/ste/FilesystemStorageAccess.php +++ b/src/ste/FilesystemStorageAccess.php @@ -1,25 +1,21 @@ <?php -// File: FilesystemStorageAccess.php - -// Namespace: kch42\ste namespace kch42\ste; -/* - * Class: FilesystemStorageAccess - * The default <StorageAccess> implementation for loading / saving templates into a directory structure. +/** + * The default {@see StorageAccess} implementation for loading / saving templates into a directory structure. */ class FilesystemStorageAccess implements StorageAccess { + /** @var string */ protected $sourcedir; + + /** @var string */ protected $transcompileddir; - /* - * Constructor: __construct - * - * Parameters: - * $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). + /** + * @param string $src - The directory with the sources (Writing permissions are not mandatory, because STE does not save template sources). + * @param string $transc - The directory with the compiled templates (the PHP instance / the HTTP Server needs writing permissions to this directory). */ public function __construct($src, $transc) { @@ -43,13 +39,16 @@ class FilesystemStorageAccess implements StorageAccess $src_stat = @stat($src_fn); $transc_stat = @stat($transc_fn); - if (($src_stat === false) and ($transc_stat === false)) { + if ($src_stat === false && $transc_stat === false) { throw new CantLoadTemplate("Template not found."); } elseif ($transc_stat === false) { $mode = StorageAccess::MODE_SOURCE; return file_get_contents($src_fn); } elseif ($src_stat === false) { include($transc_fn); + if (!isset($transcompile_fx)) { + throw new CantLoadTemplate("Compiled template file $transc_fn does not set \$transcompile_fx"); + } return $transcompile_fx; } else { if ($src_stat["mtime"] > $transc_stat["mtime"]) { @@ -57,6 +56,9 @@ class FilesystemStorageAccess implements StorageAccess return file_get_contents($src_fn); } else { include($transc_fn); + if (!isset($transcompile_fx)) { + throw new CantLoadTemplate("Compiled template file $transc_fn does not set \$transcompile_fx"); + } return $transcompile_fx; } } diff --git a/src/ste/Misc.php b/src/ste/Misc.php index d017ee3..330b3d2 100644 --- a/src/ste/Misc.php +++ b/src/ste/Misc.php @@ -4,6 +4,10 @@ namespace kch42\ste; class Misc { + /** + * @param string $text + * @return string + */ public static function escape_text($text) { return addcslashes($text, "\r\n\t\$\0..\x1f\\\"\x7f..\xff"); diff --git a/src/ste/ParseCompileError.php b/src/ste/ParseCompileError.php index f5f0272..29590da 100644 --- a/src/ste/ParseCompileError.php +++ b/src/ste/ParseCompileError.php @@ -2,6 +2,9 @@ namespace kch42\ste; +/** + * An exception thrown by the parser or compiler + */ class ParseCompileError extends \Exception { public $msg; @@ -16,10 +19,13 @@ class ParseCompileError extends \Exception $this->message = "$msg (Template $tpl, Offset $offset)"; } + /** + * Update the message to include a human readable offset. + * @param string $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; } } diff --git a/src/ste/Parser.php b/src/ste/Parser.php index 40f5f20..0280f6d 100644 --- a/src/ste/Parser.php +++ b/src/ste/Parser.php @@ -1,20 +1,25 @@ <?php -// File: Parser.php - -// Namespace: kch42\ste namespace kch42\ste; -/* - * Class: Parser +use LogicException; + +/** * The class, where the parser lives in. Can not be constructed manually. * Use the static method parse. */ class Parser { + /** @var string */ private $text; + + /** @var string */ private $name; + + /** @var int */ private $off; + + /** @var int */ private $len; const PARSE_SHORT = 1; @@ -22,6 +27,10 @@ class Parser const ESCAPES_DEFAULT = '$?~{}|\\'; + /** + * @param string $text + * @param string $name + */ private function __construct($text, $name) { $this->text = $text; @@ -30,6 +39,10 @@ class Parser $this->len = mb_strlen($text); } + /** + * @param int $n + * @return string + */ private function next($n = 1) { if ($n <= 0) { @@ -40,11 +53,17 @@ class Parser return $c; } + /** + * @return bool + */ private function eof() { return ($this->off == $this->len); } + /** + * @param int $n + */ private function back($n = 1) { if ($n <= 0) { @@ -53,11 +72,19 @@ class Parser $this->off = max($this->off - $n, 0); } + /** + * @param string $needle + * @return false|int + */ private function search_off($needle) { return mb_strpos($this->text, $needle, $this->off); } + /** + * @param string[] $needles + * @return array 4-tuple of found key, offset, text preceding offset, old offset + */ private function search_multi($needles) { $oldoff = $this->off; @@ -81,6 +108,10 @@ class Parser return array($which, $minoff, mb_substr($this->text, $oldoff, $minoff - $oldoff), $oldoff); } + /** + * @param string $needle + * @return array 3-tuple of offset (or false), text preceding offset, old offset + */ private function search($needle) { $oldoff = $this->off; @@ -95,7 +126,11 @@ class Parser return array($off, mb_substr($this->text, $oldoff, $off - $oldoff), $oldoff); } - private function take_while($cb) + /** + * @param callable $cb + * @return string + */ + private function take_while(callable $cb) { $s = ""; while (($c = $this->next()) !== "") { @@ -113,6 +148,10 @@ class Parser $this->take_while("ctype_space"); } + /** + * @return string + * @throws ParseCompileError + */ private function get_name() { $off = $this->off; @@ -125,21 +164,16 @@ class Parser return $name; } - /* - * Function: parse + /** * Parses the input into an AST. * - * You only need this function, if you want to manually trnascompile a template. + * You only need this function, if you want to manually compile a template. * - * Parameters: - * $text - The input code. - * $name - The name of the template. + * @param string $text The input code. + * @param string $name The name of the template. * - * Returns: - * An array of <ASTNode> objects. - * - * Throws: - * <ParseCompileError> + * @return ASTNode[] + * @throws ParseCompileError */ public static function parse($text, $name) { @@ -151,10 +185,15 @@ class Parser return self::tidyup_ast($res[0]); } + /** + * @param ASTNode[] $ast + * @return ASTNode[] + */ private static function tidyup_ast($ast) { $out = array(); + /** @var TextNode|null $prevtext */ $prevtext = null; $first = true; @@ -206,6 +245,17 @@ class Parser return $out; } + /** + * @param string $escapes + * @param int $flags + * @param string|null $breakon + * @param string|null $separator + * @param null $nullaction + * @param string|null $opentag + * @param int $openedat + * @return ASTNode[][] + * @throws ParseCompileError + */ private function parse_text($escapes, $flags, $breakon = null, $separator = null, $nullaction = null, $opentag = null, $openedat = -1) { $elems = array(); @@ -250,14 +300,14 @@ class Parser } break; case "commentopen": - list($off, $before, $offbefore) = $this->search("</ste:comment>"); + list($off, , $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>"); + list($off, $before, ) = $this->search("</ste:rawtext>"); if ($off === false) { throw new ParseCompileError("ste:rawtext was not closed", $this->name, $off_start); } @@ -352,6 +402,12 @@ class Parser return $elems; } + /** + * @param string $shortname + * @param int $openedat + * @return ASTNode[][] + * @throws ParseCompileError + */ private function parse_short($shortname, $openedat) { $tplname = $this->name; @@ -369,6 +425,12 @@ class Parser ); } + /** + * @param int $openedat + * @param bool $curly + * @return VariableNode + * @throws ParseCompileError + */ private function parse_var($openedat, $curly) { $varnode = new VariableNode($this->name, $openedat); @@ -377,12 +439,16 @@ class Parser $varnode->arrayfields = $this->parse_array(); } - if (($curly) && ($this->next() != "}")) { + if ($curly && ($this->next() != "}")) { throw new ParseCompileError("Unclosed '\${'", $this->name, $openedat); } return $varnode; } + /** + * @return ASTNode[] + * @throws ParseCompileError + */ private function parse_array() { $tplname = $this->name; @@ -409,6 +475,11 @@ class Parser return $arrayfields; } + /** + * @param int $openedat + * @return TagNode + * @throws ParseCompileError + */ private function parse_tag($openedat) { $tplname = $this->name; @@ -475,5 +546,8 @@ class Parser $tag->params[$param] = $paramval[0]; } } + + // Help PhpStorm detect that we shouldn't be here + throw new LogicException("Somehow we left the infinite loop?"); } } diff --git a/src/ste/RuntimeError.php b/src/ste/RuntimeError.php index dace1d4..51fd29d 100644 --- a/src/ste/RuntimeError.php +++ b/src/ste/RuntimeError.php @@ -1,14 +1,10 @@ <?php -// File: RuntimeError.php - -// Namespace: kch42\ste namespace kch42\ste; -/* - * Class: RuntimeError +/** * An Exception that a tag can throw, if a non-fatal runtime error occurred. - * By default this will return in no output at all. But if <STECore::$mute_runtime_errors> is false, this will generate a error message instead of the tag's output. + * By default this will return in no output at all. But if {@see STECore::$mute_runtime_errors} is false, this will generate a error message instead of the tag's output. */ class RuntimeError extends \Exception { diff --git a/src/ste/STECore.php b/src/ste/STECore.php index 13fb2f6..40d680a 100644 --- a/src/ste/STECore.php +++ b/src/ste/STECore.php @@ -1,43 +1,64 @@ <?php -// File: STECore.php - -// Namespace: kch42\ste namespace kch42\ste; -/* - * Class: STECore +use Exception; + +/** * The Core of STE */ class STECore { + /** @var callable[] */ private $tags; + + /** @var StorageAccess */ private $storage_access; + + /** @var string */ private $cur_tpl_dir; + + /** @var Scope */ public $scope; - /* - * Variables: Public variables - * - * $blocks - Associative array of blocks (see the language definition). - * $blockorder - The order of the blocks (an array) - * $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>). - * $vars - Variables in the top scope of the template. + /** + * @var array + * Associative array of blocks (see the language definition). */ public $blocks; + + /** + * @var array + * The order of the blocks (an array) + */ public $blockorder; + + /** + * @var bool + * If true (default) a {@see RuntimeError} exception will result in no + * output from the tag, if false a error message will be written to output. + */ public $mute_runtime_errors = true; + + /** + * @var bool + * If true, STE will throw a {@see FatalRuntimeError} if a tag was called + * that was not registered, otherwise (default) a regular + * {@see RuntimeError} will be thrown and automatically handled by STE + * (see {@see STECore::$mute_runtime_errors}). + */ public $fatal_error_on_missing_tag = false; + + /** + * @var array + * Variables in the top scope of the template. + */ public $vars; - /* - * Constructor: __construct - * - * Parameters: - * $storage_access - An Instance of a <StorageAccess> implementation. + /** + * @param StorageAccess $storage_access */ - public function __construct($storage_access) + public function __construct(StorageAccess $storage_access) { $this->storage_access = $storage_access; $this->cur_tpl_dir = "/"; @@ -50,42 +71,47 @@ class STECore $this->scope->vars =& $this->vars; } - /* - * 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)). + * @param string $name The name of the tag. + * @param callable $callback A callable function + * Must take three parameters: * - * Throws: - * An Exception if the tag could not be registered (if $callback is not callable or if $name is empty) + * The {@see STECore} instance, + * an associative array of parameters, + * and a function representing the tags content + * (This expects the {@see STECore} instance as + * its only parameter and returns its text result, + * i.e to get the text, you need to call this + * function with the {@see STECore} instance as a + * parameter). + * + * @throws 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."); + throw new Exception("Can not register tag \"$name\", not callable."); } if (empty($name)) { - throw new \Exception("Can not register tag, 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. + * @param string $name The Tag's name + * @param array $params Associative array of parameters + * @param callable $sub A callable function (expecting an {@see STECore} instance as it's parameter) that represents the tag's content. * - * Throws: - * Might throw a <FatalRuntimeError> (see <$fatal_error_on_missing_tag>. + * @throws FatalRuntimeError see {@see STECore::$fatal_error_on_missing_tag}. * - * Returns: - * The output of the tag or, if a <RuntimeError> was thrown, the appropiate result (see <$mute_runtime_errors>). + * @return string The output of the tag or, if a {@see RuntimeError} was thrown, the appropriate result + * (see {@see STECore::$mute_runtime_errors}). */ public function call_tag($name, $params, $sub) { @@ -103,28 +129,37 @@ class STECore return "RuntimeError occurred on tag '$name': " . $e->getMessage(); } } + + return ""; } + /** + * {@see Calc::calc()} + * + * @param string $expression + * @return float|int + * @throws RuntimeError + */ 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. + /** + * Executes a template and returns the result. * - * Parameters: - * $tpl - The name of the template to execute. + * The huge difference to {@see STECore::load()} is that this function will also output all blocks. * - * 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). + * @param string $tpl The name of the template to execute. * - * Returns: - * The output of the template. + * @throws CantLoadTemplate If the template could not be loaded. + * @throws ParseCompileError If the template could not be parsed or compiled. + * @throws 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 {@see RuntimeError} or {@see FatalRuntimeError} to make it possible for STE to handle them correctly). + * + * @return string The output of the template. */ public function exectemplate($tpl) { @@ -211,22 +246,21 @@ class STECore return $this->scope->get_var_by_name($name); } - /* - * Function: load - * Load a template and return its result (blocks not included, use <exectemplate> for this). + /** + * Load a template and return its result (blocks not included, use {@see STECore::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. + * @param string $tpl The name of the template to be loaded. + * @param bool $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). + * @throws CantLoadTemplate If the template could not be loaded. + * @throws CantSaveTemplate If the template could not be saved. + * @throws ParseCompileError If the template could not be parsed or compiled. + * @throws FatalRuntimeError If a tag threw it or if a tag was not found and {@see STECore::$fatal_error_on_missing_tag} is true. * - * Returns: - * The result of the template (if $quiet == false). + * Might also throw different exceptions, if a external tag threw it + * (but they should use {@see RuntimeError} or {@see FatalRuntimeError} to make it possible for STE to handle them correctly). + * + * @return string|null The result of the template (if $quiet == false). */ public function load($tpl, $quiet = false) { @@ -238,7 +272,7 @@ class STECore $tpl = $this->cur_tpl_dir . "/" . $tpl; } $pathex = array_filter(explode("/", $tpl), function ($s) { - return ($s != ".") and (!empty($s)); + return $s != "." && !empty($s); }); $pathex = array_merge($pathex); while (($i = array_search("..", $pathex)) !== false) { @@ -277,26 +311,31 @@ class STECore if ($quiet) { $this->blocks = $blocks_back; $this->blockorder = $blockorder_back; + + return null; } 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. + * @param string $txt The text to test. * - * Returns: - * true/false. + * @return bool */ public function evalbool($txt) { return trim(@(string)$txt) != ""; } + /** + * Internal function for implementing tag content and custom tags. + * + * @param callable $fx + * @return \Closure + */ public function make_closure($fx) { $bound_scope = $this->scope; @@ -312,7 +351,7 @@ class STECore $result = call_user_func_array($fx, $args); $ste->scope = $prev; return $result; - } catch (\Exception $e) { + } catch (Exception $e) { $ste->scope = $prev; throw $e; } diff --git a/src/ste/STEStandardLibrary.php b/src/ste/STEStandardLibrary.php index 9850afe..e39b7af 100644 --- a/src/ste/STEStandardLibrary.php +++ b/src/ste/STEStandardLibrary.php @@ -13,6 +13,12 @@ class STEStandardLibrary } } + /** + * @param STECore $ste + * @param array $params + * @param callable $sub + * @return string + */ public static function escape($ste, $params, $sub) { if ($ste->evalbool(@$params["lines"])) { @@ -22,11 +28,24 @@ class STEStandardLibrary } } + /** + * @param STECore $ste + * @param array $params + * @param callable $sub + * @return string + */ public static function strlen($ste, $params, $sub) { return strlen($sub($ste)); } + /** + * @param STECore $ste + * @param array $params + * @param callable $sub + * @return string + * @throws RuntimeError + */ public static function arraylen($ste, $params, $sub) { if (empty($params["array"])) { @@ -36,6 +55,13 @@ class STEStandardLibrary return (is_array($a)) ? count($a) : ""; } + /** + * @param STECore $ste + * @param array $params + * @param callable $sub + * @return string + * @throws RuntimeError + */ public static function inc($ste, $params, $sub) { if (empty($params["var"])) { @@ -45,6 +71,13 @@ class STEStandardLibrary $ref++; } + /** + * @param STECore $ste + * @param array $params + * @param callable $sub + * @return string + * @throws RuntimeError + */ public static function dec($ste, $params, $sub) { if (empty($params["var"])) { @@ -54,11 +87,24 @@ class STEStandardLibrary $ref--; } + /** + * @param STECore $ste + * @param array $params + * @param callable $sub + * @return string + */ public static function date($ste, $params, $sub) { return @strftime($sub($ste), empty($params["timestamp"]) ? @time() : (int) $params["timestamp"]); } + /** + * @param STECore $ste + * @param array $params + * @param callable $sub + * @return string + * @throws RuntimeError + */ public static function in_array($ste, $params, $sub) { if (empty($params["array"])) { @@ -71,6 +117,13 @@ class STEStandardLibrary return in_array($sub($ste), $ar) ? "y" : ""; } + /** + * @param STECore $ste + * @param array $params + * @param callable $sub + * @return string + * @throws RuntimeError + */ public static function join($ste, $params, $sub) { if (empty($params["array"])) { @@ -79,6 +132,13 @@ class STEStandardLibrary return implode($sub($ste), $ste->get_var_by_name($params["array"])); } + /** + * @param STECore $ste + * @param array $params + * @param callable $sub + * @return string + * @throws RuntimeError + */ public static function split($ste, $params, $sub) { if (empty($params["array"])) { @@ -90,6 +150,13 @@ class STEStandardLibrary $ste->set_var_by_name($params["array"], explode($params["delim"], $sub($ste))); } + /** + * @param STECore $ste + * @param array $params + * @param callable $sub + * @return string + * @throws RuntimeError + */ public static function array_add($ste, $params, $sub) { if (empty($params["array"])) { @@ -104,6 +171,13 @@ class STEStandardLibrary } } + /** + * @param STECore $ste + * @param array $params + * @param callable $sub + * @return string + * @throws RuntimeError + */ public static function array_filter($ste, $params, $sub) { if (empty($params["array"])) { diff --git a/src/ste/Scope.php b/src/ste/Scope.php index e312876..afb2bfc 100644 --- a/src/ste/Scope.php +++ b/src/ste/Scope.php @@ -4,9 +4,17 @@ namespace kch42\ste; class Scope implements \ArrayAccess { + /** @var self|null */ private $parent = null; + + /** @var array */ public $vars = array(); + /** + * @param string $name + * @return string[] + * @throws RuntimeError + */ private static function parse_name($name) { $remain = $name; @@ -44,6 +52,12 @@ class Scope implements \ArrayAccess return $fields; } + /** + * @param string $name + * @param bool $localonly + * @return mixed A reference to the resolved variable + * @throws VarNotInScope + */ private function &get_topvar_reference($name, $localonly) { if (array_key_exists($name, $this->vars)) { @@ -59,6 +73,13 @@ class Scope implements \ArrayAccess throw new VarNotInScope(); } + /** + * @param string $name + * @param bool $create_if_not_exist + * @param bool $localonly + * @return mixed A reference to the resolved variable + * @throws RuntimeError + */ public function &get_var_reference($name, $create_if_not_exist, $localonly=false) { $nullref = null; @@ -107,24 +128,42 @@ class Scope implements \ArrayAccess return $ref; } + /** + * @param string $name + * @param mixed $val + * @throws RuntimeError + */ public function set_var_by_name($name, $val) { $ref = &$this->get_var_reference($name, true); $ref = $val; } + /** + * @param string $name + * @param mixed $val + * @throws RuntimeError + */ public function set_local_var($name, $val) { $ref = &$this->get_var_reference($name, true, true); $ref = $val; } + /** + * @param string $name + * @return mixed Returns an empty string, if not found or unset + * @throws RuntimeError + */ public function get_var_by_name($name) { $ref = $this->get_var_reference($name, false); return $ref === null ? "" : $ref; } + /** + * @return self + */ public function new_subscope() { $o = new self(); diff --git a/src/ste/StorageAccess.php b/src/ste/StorageAccess.php index e2e8727..38dea7e 100644 --- a/src/ste/StorageAccess.php +++ b/src/ste/StorageAccess.php @@ -1,56 +1,45 @@ <?php -// File: StorageAccess.php - -// Namespace: kch42\ste namespace kch42\ste; -/* - * Class: StorageAccess - * An interface. +/** * 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 { - /* - * Constants: Template modes - * - * MODE_SOURCE - The Templates source - * MODE_TRANSCOMPILED - The transcompiled template - */ + /** @var int The template's source */ const MODE_SOURCE = 0; + + /** @var int The compiled template */ const MODE_TRANSCOMPILED = 1; - /* - * Function: load + /** * Loading a template. * - * Parameters: - * $tpl - The name of the template. - * &$mode - Which mode is preferred? One of the <Template modes>. - * If <MODE_SOURCE>, the raw sourcecode is expected, if <MODE_TRANSCOMPILED> the transcompiled template *as a callable function* (expecting an <STECore> instance as first parameter) is expected. - * If the transcompiled version is not available or older than the source, you can set this parameter to <MODE_SOURCE> and return the source. + * @param string $tpl The name of the template. + * @param string &$mode Which mode is preferred? One of the MODE_* constants. + * If {@see StorageAccess::MODE_SOURCE}, the raw sourcecode is expected, + * if {@see StorageAccess::MODE_TRANSCOMPILED} the compiled template + * *as a callable function* (expecting an {@see STECore} instance as first parameter) is expected. + * + * If the compiled version is not available or older than the source, you can set this + * parameter to {@see StorageAccess::MODE_SOURCE} and return the source. * - * Throws: - * A <CantLoadTemplate> exception if the template could not be loaded. + * @throws CantLoadTemplate If the template could not be loaded. * - * Returns: - * Either the sourcecode or a callable function (first, and only parameter: an <STECore> instance). + * @return string|callable Either the sourcecode or a callable function (first, and only parameter: an {@see STECore} instance). */ public function load($tpl, &$mode); - /* - * Function: save + /** * Saves a template. * - * Throws: - * A <CantSaveTemplate> exception if the template could not be saved. + * @param string $tpl -The name of the template. + * @param string $data - The data to be saved. + * @param int $mode - One of the MODE_* constants. * - * Parameters: - * $tpl -The name of the template. - * $data - The data to be saved. - * $mode - A <Template mode> constant. + * @throws CantSaveTemplate If the template could not be saved. */ public function save($tpl, $data, $mode); } diff --git a/src/ste/TagNode.php b/src/ste/TagNode.php index c1a14e5..18567b2 100644 --- a/src/ste/TagNode.php +++ b/src/ste/TagNode.php @@ -4,9 +4,20 @@ namespace kch42\ste; class TagNode extends ASTNode { + /** @var string */ public $name; + + /** @var ASTNode[][] */ public $params = array(); + + /** @var ASTNode[] */ public $sub = array(); + + /** + * @param string $tpl + * @param int $off + * @param string $name + */ public function __construct($tpl, $off, $name = "") { parent::__construct($tpl, $off); diff --git a/src/ste/TextNode.php b/src/ste/TextNode.php index c68dd2f..199c203 100644 --- a/src/ste/TextNode.php +++ b/src/ste/TextNode.php @@ -4,7 +4,14 @@ namespace kch42\ste; class TextNode extends ASTNode { + /** @var string */ public $text; + + /** + * @param string $tpl + * @param int $off + * @param string $text + */ public function __construct($tpl, $off, $text = "") { parent::__construct($tpl, $off); diff --git a/src/ste/Transcompiler.php b/src/ste/Transcompiler.php index d223b53..88c008e 100644 --- a/src/ste/Transcompiler.php +++ b/src/ste/Transcompiler.php @@ -1,13 +1,9 @@ <?php -// File: Transcompiler.php - -// Namespace: kch42\ste namespace kch42\ste; -/* - * Class: Transcompiler - * Contains the STE transcompiler. You'll only need this, if you want to manually transcompile a STE template. +/** + * Contains the STE compiler. You'll only need this, if you want to manually compile a STE template. */ class Transcompiler { @@ -18,33 +14,44 @@ class Transcompiler return $typ . '_' . str_replace('.', '_', uniqid('', true)); } + private static function mark_builtin_callable(callable $c) + { + return $c; + } + public static function init_builtins() { if (self::$builtins !== null) { return; } + /** @var callable[] builtins */ self::$builtins = array( - "if" => array("\\kch42\\ste\\Transcompiler", "builtin_if"), - "cmp" => array("\\kch42\\ste\\Transcompiler", "builtin_cmp"), - "not" => array("\\kch42\\ste\\Transcompiler", "builtin_not"), - "even" => array("\\kch42\\ste\\Transcompiler", "builtin_even"), - "for" => array("\\kch42\\ste\\Transcompiler", "builtin_for"), - "foreach" => array("\\kch42\\ste\\Transcompiler", "builtin_foreach"), - "infloop" => array("\\kch42\\ste\\Transcompiler", "builtin_infloop"), - "break" => array("\\kch42\\ste\\Transcompiler", "builtin_break"), - "continue" => array("\\kch42\\ste\\Transcompiler", "builtin_continue"), - "block" => array("\\kch42\\ste\\Transcompiler", "builtin_block"), - "load" => array("\\kch42\\ste\\Transcompiler", "builtin_load"), - "mktag" => array("\\kch42\\ste\\Transcompiler", "builtin_mktag"), - "tagcontent" => array("\\kch42\\ste\\Transcompiler", "builtin_tagcontent"), - "set" => array("\\kch42\\ste\\Transcompiler", "builtin_set"), - "setlocal" => array("\\kch42\\ste\\Transcompiler", "builtin_setlocal"), - "get" => array("\\kch42\\ste\\Transcompiler", "builtin_get"), - "calc" => array("\\kch42\\ste\\Transcompiler", "builtin_calc") + "if" => self::mark_builtin_callable(array("\\kch42\\ste\\Transcompiler", "builtin_if")), + "cmp" => self::mark_builtin_callable(array("\\kch42\\ste\\Transcompiler", "builtin_cmp")), + "not" => self::mark_builtin_callable(array("\\kch42\\ste\\Transcompiler", "builtin_not")), + "even" => self::mark_builtin_callable(array("\\kch42\\ste\\Transcompiler", "builtin_even")), + "for" => self::mark_builtin_callable(array("\\kch42\\ste\\Transcompiler", "builtin_for")), + "foreach" => self::mark_builtin_callable(array("\\kch42\\ste\\Transcompiler", "builtin_foreach")), + "infloop" => self::mark_builtin_callable(array("\\kch42\\ste\\Transcompiler", "builtin_infloop")), + "break" => self::mark_builtin_callable(array("\\kch42\\ste\\Transcompiler", "builtin_break")), + "continue" => self::mark_builtin_callable(array("\\kch42\\ste\\Transcompiler", "builtin_continue")), + "block" => self::mark_builtin_callable(array("\\kch42\\ste\\Transcompiler", "builtin_block")), + "load" => self::mark_builtin_callable(array("\\kch42\\ste\\Transcompiler", "builtin_load")), + "mktag" => self::mark_builtin_callable(array("\\kch42\\ste\\Transcompiler", "builtin_mktag")), + "tagcontent" => self::mark_builtin_callable(array("\\kch42\\ste\\Transcompiler", "builtin_tagcontent")), + "set" => self::mark_builtin_callable(array("\\kch42\\ste\\Transcompiler", "builtin_set")), + "setlocal" => self::mark_builtin_callable(array("\\kch42\\ste\\Transcompiler", "builtin_setlocal")), + "get" => self::mark_builtin_callable(array("\\kch42\\ste\\Transcompiler", "builtin_get")), + "calc" => self::mark_builtin_callable(array("\\kch42\\ste\\Transcompiler", "builtin_calc")) ); } + /** + * @param TagNode $ast + * @return string + * @throws ParseCompileError + */ private static function builtin_if($ast) { $output = ""; @@ -53,9 +60,9 @@ class Transcompiler $else = null; foreach ($ast->sub as $node) { - if (($node instanceof TagNode) and ($node->name == "then")) { + if (($node instanceof TagNode) && $node->name == "then") { $then = $node->sub; - } elseif (($node instanceof TagNode) and ($node->name == "else")) { + } elseif (($node instanceof TagNode) && $node->name == "else") { $else = $node->sub; } else { $condition[] = $node; @@ -78,6 +85,12 @@ class Transcompiler } return $output; } + + /** + * @param TagNode $ast + * @return string + * @throws ParseCompileError + */ private static function builtin_cmp($ast) { $operators = array( @@ -116,7 +129,10 @@ class Transcompiler if (!isset($ast->params["op"])) { throw new ParseCompileError("self::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 + && ($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; @@ -144,6 +160,12 @@ class Transcompiler } return $code; } + + /** + * @param TagNode $ast + * @return string + * @throws ParseCompileError + */ private static function builtin_not($ast) { $code = "\$outputstack[] = '';\n\$outputstack_i++;\n"; @@ -151,6 +173,12 @@ class Transcompiler $code .= "\$outputstack_i--;\n\$outputstack[\$outputstack_i] .= (!\$ste->evalbool(array_pop(\$outputstack))) ? 'yes' : '';\n"; return $code; } + + /** + * @param TagNode $ast + * @return string + * @throws ParseCompileError + */ private static function builtin_even($ast) { $code = "\$outputstack[] = '';\n\$outputstack_i++;\n"; @@ -158,6 +186,12 @@ class Transcompiler $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; } + + /** + * @param TagNode $ast + * @return string + * @throws ParseCompileError + */ private static function builtin_for($ast) { $code = ""; @@ -179,7 +213,10 @@ class Transcompiler $step = null; /* i.e. not known at compilation time */ if (empty($ast->params["step"])) { $step = 1; - } elseif ((count($ast->params["step"]) == 1) and ($ast->params["step"][0] instanceof TextNode)) { + } elseif ( + count($ast->params["step"]) == 1 + && ($ast->params["step"][0] instanceof TextNode) + ) { $step = $ast->params["step"][0]->text + 0; } else { list($val, $pre) = self::_transcompile($ast->params["step"], true); @@ -216,6 +253,12 @@ class Transcompiler return $code; } + + /** + * @param TagNode $ast + * @return string + * @throws ParseCompileError + */ private static function builtin_foreach($ast) { $loopname = self::tempvar("foreachloop"); @@ -282,18 +325,40 @@ class Transcompiler return $code; } + + /** + * @param TagNode $ast + * @return string + * @throws ParseCompileError + */ private static function builtin_infloop($ast) { return "while(true)\n{\n" . self::indent_code(self::loopbody(self::indent_code(self::_transcompile($ast->sub)))) . "\n}\n"; } + + /** + * @param TagNode $ast + * @return string + */ private static function builtin_break($ast) { return "throw new \\kch42\\ste\\BreakException();\n"; } + + /** + * @param TagNode $ast + * @return string + */ private static function builtin_continue($ast) { return "throw new \\kch42\\ste\\ContinueException();\n"; } + + /** + * @param TagNode $ast + * @return string + * @throws ParseCompileError + */ private static function builtin_block($ast) { if (empty($ast->params["name"])) { @@ -315,6 +380,12 @@ class Transcompiler return $code; } + + /** + * @param TagNode $ast + * @return string + * @throws ParseCompileError + */ private static function builtin_load($ast) { if (empty($ast->params["name"])) { @@ -325,6 +396,12 @@ class Transcompiler $code .= "\$outputstack[\$outputstack_i] .= \$ste->load(" . $val . ");\n"; return $code; } + + /** + * @param TagNode $ast + * @return string + * @throws ParseCompileError + */ private static function builtin_mktag($ast) { $code = ""; @@ -356,10 +433,21 @@ class Transcompiler return $code; } + + /** + * @param TagNode $ast + * @return string + */ private static function builtin_tagcontent($ast) { return "\$outputstack[\$outputstack_i] .= \$sub(\$ste);"; } + + /** + * @param TagNode $ast + * @return string + * @throws ParseCompileError + */ private static function builtin_set($ast) { if (empty($ast->params["var"])) { @@ -376,6 +464,12 @@ class Transcompiler return $code; } + + /** + * @param TagNode $ast + * @return string + * @throws ParseCompileError + */ private static function builtin_setlocal($ast) { if (empty($ast->params["var"])) { @@ -392,6 +486,12 @@ class Transcompiler return $code; } + + /** + * @param TagNode $ast + * @return string + * @throws ParseCompileError + */ private static function builtin_get($ast) { if (empty($ast->params["var"])) { @@ -401,6 +501,12 @@ class Transcompiler list($val, $pre) = self::_transcompile($ast->params["var"], true); return "$pre\$outputstack[\$outputstack_i] .= \$ste->get_var_by_name(" . $val . ");"; } + + /** + * @param TagNode $ast + * @return string + * @throws ParseCompileError + */ private static function builtin_calc($ast) { $code = "\$outputstack[] = '';\n\$outputstack_i++;\n"; @@ -425,6 +531,12 @@ class Transcompiler return "try\n{\n" . self::indent_code($code) . "\n}\ncatch(\\kch42\\ste\\BreakException \$e) { break; }\ncatch(\\kch42\\ste\\ContinueException \$e) { continue; }\n"; } + /** + * @param ASTNode[] $ast + * @param bool $avoid_outputstack + * @return array|string + * @throws ParseCompileError + */ private static function _transcompile($ast, $avoid_outputstack = false) { /* The real self::transcompile function, does not add boilerplate code. */ $code = ""; @@ -477,16 +589,15 @@ class Transcompiler return $code; } - /* - * Function: transcompile - * Transcompiles an abstract syntax tree to PHP. - * You only need this function, if you want to manually transcompile a template. + /** + * Compiles an abstract syntax tree to PHP. + * You only need this function, if you want to manually compile a template. * - * Parameters: - * $ast - The abstract syntax tree to transcompile. + * @param ASTNode[] $ast The abstract syntax tree to compile. * - * 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). + * @return string PHP code. The PHP code is an anonymous function expecting an {@see STECore} instance as its + * parameter and returns a string (everything that was not packed into a section). + * @throws ParseCompileError */ public static function transcompile($ast) { /* self::Transcompile and add some boilerplate code. */ diff --git a/src/ste/VariableNode.php b/src/ste/VariableNode.php index d03fdce..9a4ced8 100644 --- a/src/ste/VariableNode.php +++ b/src/ste/VariableNode.php @@ -4,13 +4,24 @@ namespace kch42\ste; class VariableNode extends ASTNode { + /** @var string */ public $name; + + /** @var ASTNode[][] */ public $arrayfields = array(); + + /** + * @return string + */ public function transcompile() { $varaccess = '@$ste->scope[' . (is_numeric($this->name) ? $this->name : '"' . Misc::escape_text($this->name) . '"'). ']'; foreach ($this->arrayfields as $af) { - if ((count($af) == 1) and ($af[0] instanceof TextNode) and is_numeric($af[0]->text)) { + if ( + count($af) == 1 + && ($af[0] instanceof TextNode) + && is_numeric($af[0]->text) + ) { $varaccess .= '[' . $af->text . ']'; } else { $varaccess .= '[' . implode(".", array_map(function ($node) { |