diff options
Diffstat (limited to 'php/luigi-template.php')
-rw-r--r-- | php/luigi-template.php | 393 |
1 files changed, 0 insertions, 393 deletions
diff --git a/php/luigi-template.php b/php/luigi-template.php deleted file mode 100644 index 69e3fb8..0000000 --- a/php/luigi-template.php +++ /dev/null @@ -1,393 +0,0 @@ -<?php -declare(strict_types = 1); - -namespace Luigi; - -const VERSION = '0.4.2'; - -class Error extends \Exception {}; - -class UnknownTypeError extends Error { - public $type, - $name; - - public function __construct(string $type, string $name) { - $this->type = $type; - $this->name = $name; - parent::__construct("unknown $type: $name"); - } -}; - -final class UnknownTemplateError extends UnknownTypeError { - public function __construct(string $name) { - parent::__construct('template', $name); - } -}; - -final class UnknownFilterError extends UnknownTypeError { - public function __construct(string $name) { - parent::__construct('filter', $name); - } -}; - -final class UnknownKeyError extends UnknownTypeError { - public function __construct(string $name) { - parent::__construct('key', $name); - } -}; - -final class MissingFilterParameterError extends Error { - public $filter_name; - - public function __construct(string $filter_name) { - $this->filter_name = $filter_name; - parent::__construct("missing required filter parameter for filter $filter_name"); - } -}; - -final class InvalidTemplateError extends Error { - public $template; - - public function __construct(string $template) { - $this->template = $template; - parent::__construct("invalid template: $template"); - } -}; - -namespace Luigi; - -final class RunContext { - public $args, - $filters; - - public function __construct(array $args, array $filters) { - $this->args = $args; - $this->filters = $filters; - } -}; - -final class TemplateFilter { - private $name, - $args; - - public function __construct(string $name, array $args) { - $this->name = $name; - $this->args = $args; - } - - public function run($v, array &$args, array &$filters) { - if (!isset($filters[$this->name])) { - throw new UnknownFilterError($this->name); - } - - # get callback - $cb = $filters[$this->name]; - - # invoke callback, return result - return $cb($v, $this->args, $args); - } -}; - -namespace Luigi\Parser; - -use Luigi\RunContext; -use Luigi - -abstract class Token { - public function run(array $args, array $filters) : string; -}; - -final class LiteralToken extends Token { - private $val; - - public function __construct(string $val) { - $this->val = $val; - } - - public function run(array &$args, array &$filters) : string { - return $this->val; - } -}; - -final class FilterToken extends Token { - private $key, - $filters; - - public function __construct(string $key, array $filters) { - $this->key = $key; - $this->filters = $filters; - } - - public function run(array &$args, array &$filters) : string { - if (!isset($args[$this->key])) { - throw new UnknownKeyError($this->key); - } - - # get initial value - $r = $args[$this->key]; - - if ($this->filters && count($this->filters) > 0) { - # pass value through filters - $r = array_reduce($this->filters, function($r, $f) use (&$args, &$filters) { - return $f->run($r, $args, $filters); - }, $r); - } - - # return result - return $r; - } -}; - -const TOKEN_RE = '/ - # match opening brace - %\{ - - # match optional whitespace - \s* - - # match key - (?<key>[^\s\|\}]+) - - # match filter(s) - (?<filters>(\s*\|(\s*[^\s\|\}]+)+)*) - - # match optional whitespace - \s* - - # match closing brace - \} - - # or match up all non-% chars or a single % char - | (?<text>[^%]* | %) -/mx'; - -const FILTER_RE = '/ - # match filter name - (?<name>\S+) - - # match filter arguments (optional) - (?<args>(\s*\S+)*) - - # optional trailing whitespace - \s* -/mx'; - -const DELIM_FILTERS_RE = '/\s*\|\s*/m'; - -const DELIM_ARGS_RE = '/\s+/m'; - -function parse_filters(string $filters) : array { - # split into individual filters - $r = []; - - foreach (preg_split(DELIM_FILTERS_RE, $filters) as $f) { - # trim whitespace - $f = trim($f); - - # skip empty filters - if (!$f) - continue; - - # match filter - $md = []; - if (!preg_match(FILTER_RE, $f, $md)) { - throw new UnknownFilterError($f); - } - - # add filter to results - $r[] = new TemplateFilter($md['name'], (count($md) > 2) ? preg_split( - DELIM_ARGS_RE, - trim($md['args']) - ) : []); - } - - # return results - return $r; -} - -function parse_template(string $template) : array { - # build list of matches - $matches = []; - $num_matches = preg_match_all(TOKEN_RE, $template, $matches, PREG_SET_ORDER); - - # check for error - if ($num_matches === false) { - throw new InvalidTemplateError($template); - } - - # map matches to tokens - return array_map(function($m) { - if ($m['key'] !== '') { - # filter token - return new FilterToken($m['key'], parse_filters($m['filters'])); - } else { - # literal token - return new LiteralToken($m['text']); - } - }, $matches); -} - -namespace Luigi; - -public $FILTERS = [ - 'h' => function($s) { - return htmlspecialchars($v, ENT_QUOTES); - }, - - 'u' => function($s) { - return urlencode($v); - }, - - 'json' => function($v) { - return json_encode($v); - }, - - 'hash' => function($v, $args) { - if (count($args) !== 1) { - throw new MissingFilterParameterError('hash'); - } - - return hash($args[0], $v); - }, - - 'base64' => function($v) { - return base64_encode($v); - }, - - 'nl2br' => function($v) { - return nl2br($v); - }, - - 'uc' => function($v) { - return strtoupper($v); - }, - - 'lc' => function($v) { - return strtolower($v); - }, - - 'trim' => function($v) { - return trim($v); - }, - - 'rtrim' => function($v) { - return rtrim($v); - }, - - 'ltrim' => function($v) { - return ltrim($v); - }, - - 's' => function($v) { - return ($v == 1) ? '' : 's'; - }, - - 'strlen' => function($v) { - return strlen($v); - }, - - 'count' => function($v) { - return count($v); - }, - - 'key' => function($v, $args) { - if (count($args) !== 1) { - throw new MissingFilterParameterError('key'); - } - - # get key - $key = $args[0]; - - # make sure key exists - if (!isset($v[$key])) { - throw new UnknownKeyError($key); - } - - # return key - return $v[$key]; - }, -]; - -final class Template { - private $template, - $filters, - $tokens; - - public function __construct( - string $template, - array $custom_filters = [] - ) { - global $FILTERS; - - $this->template = $template; - $this->filters = count($custom_filters) ? $custom_filters : $FILTERS; - - # parse template into list of tokens - $this->tokens = Parser\parse_template($template); - } - - public function run(array $args = []) : string { - # php sucks - $me = $this; - - return join('', array_map(function($token) use ($me, $args) { - return $token->run($args, $this->filters); - }, $this->tokens)); - } - - public static function once($str, $args = [], array $filters = []) { - $t = new Template($str, $filters); - return $t->run($args); - } -}; - -public function run( - string $template, - array $args = [], - array $filters = [] -) : string { - $t = new Template($template, $filters); - return $t->run($args); -} - -final class Cache implements \ArrayAccess { - private $templates, - $filters, - $lut = []; - - public function __construct(array $templates, array $filters = []) { - $this->templates = $templates; - $this->filters = $filters; - } - - public function offsetExists($key) : bool { - return isset($this->templates[$key]); - } - - public function offsetGet($key) { - if (isset($this->lut[$key])) { - return $this->lut[$key]; - } else if (isset($this->templates[$key]) { - $this->lut[$key] = new Template($this->templates[$key], $this->filters); - return $this->lut[$key]; - } else { - throw new UnknownTemplateError($key); - } - } - - public function offsetUnset($key) { - delete($this->lut[$key]); - delete($this->templates[$key]); - } - - public function offsetSet($key, $val) : bool { - delete($this->lut[$key]); - $this->templates[$key] = $val; - return isset($this->templates[$key]); - } - - - public function run(string $key, array $args = []) : string { - return $this->offsetGet($key)->run($args); - } -}; |