diff options
author | Evgeny Zinoviev <me@ch1p.io> | 2024-02-19 01:44:02 +0300 |
---|---|---|
committer | Evgeny Zinoviev <me@ch1p.io> | 2024-02-19 01:44:11 +0300 |
commit | 3741f7cf78a288e967415ccb6736c888a21c211b (patch) | |
tree | a48d8331c9936d6c108de4d0f9179a089b1e56e6 /localwebsite/engine | |
parent | d79309e498cdc1358c81367ce2a93a5731e517d1 (diff) |
web_kbn: almost completely ported lws to python
Diffstat (limited to 'localwebsite/engine')
-rw-r--r-- | localwebsite/engine/debug.php | 355 | ||||
-rw-r--r-- | localwebsite/engine/model.php | 243 | ||||
-rw-r--r-- | localwebsite/engine/request_handler.php | 142 | ||||
-rw-r--r-- | localwebsite/engine/router.php | 199 | ||||
-rw-r--r-- | localwebsite/engine/tpl.php | 520 |
5 files changed, 0 insertions, 1459 deletions
diff --git a/localwebsite/engine/debug.php b/localwebsite/engine/debug.php deleted file mode 100644 index b1b959f..0000000 --- a/localwebsite/engine/debug.php +++ /dev/null @@ -1,355 +0,0 @@ -<?php - -// require_once 'engine/mysql.php'; - -class debug { - - protected static $Types = [ - 1 => 'E_ERROR', - 2 => 'E_WARNING', - 4 => 'E_PARSE', - 8 => 'E_NOTICE', - 16 => 'E_CORE_ERROR', - 32 => 'E_CORE_WARNING', - 64 => 'E_COMPILE_ERROR', - 128 => 'E_COMPILE_WARNING', - 256 => 'E_USER_ERROR', - 512 => 'E_USER_WARNING', - 1024 => 'E_USER_NOTICE', - 2048 => 'E_STRICT', - 4096 => 'E_RECOVERABLE_ERROR', - 8192 => 'E_DEPRECATED', - 16384 => 'E_USER_DEPRECATED', - 32767 => 'E_ALL' - ]; - - const STORE_NONE = -1; - const STORE_MYSQL = 0; - const STORE_FILE = 1; - const STORE_BOTH = 2; - - private static $instance = null; - - protected $enabled = false; - protected $errCounter = 0; - protected $logCounter = 0; - protected $messagesStoreType = self::STORE_NONE; - protected $errorsStoreType = self::STORE_NONE; - protected $filter; - protected $reportRecursionLevel = 0; - protected $overridenDebugFile = null; - protected $silent = false; - protected $prefix; - - private function __construct($filter) { - $this->filter = $filter; - } - - public static function getInstance($filter = null) { - if (is_null(self::$instance)) { - self::$instance = new self($filter); - } - return self::$instance; - } - - public function enable() { - $self = $this; - - set_error_handler(function($no, $str, $file, $line) use ($self) { - if ($self->silent || !$self->enabled) { - return; - } - if ((is_callable($this->filter) && !($this->filter)($no, $file, $line, $str)) || !$self->canReport()) { - return; - } - $self->report(true, $str, $no, $file, $line); - }); - - append_shutdown_function(function() use ($self) { - if (!$self->enabled || !($error = error_get_last())) { - return; - } - if (is_callable($this->filter) - && !($this->filter)($error['type'], $error['file'], $error['line'], $error['message'])) { - return; - } - if (!$self->canReport()) { - return; - } - $self->report(true, $error['message'], $error['type'], $error['file'], $error['line']); - }); - - $this->enabled = true; - } - - public function disable() { - restore_error_handler(); - $this->enabled = false; - } - - public function report($is_error, $text, $errno = 0, $errfile = '', $errline = '') { - global $config; - - $this->reportRecursionLevel++; - - $logstarted = $this->errCounter > 0 || $this->logCounter > 0; - $num = $is_error ? $this->errCounter++ : $this->logCounter++; - $custom = $is_error && !$errno; - $ts = time(); - $exectime = exectime(); - $bt = backtrace(2); - - $store_file = (!$is_error && $this->checkMessagesStoreType(self::STORE_FILE)) - || ($is_error && $this->checkErrorsStoreType(self::STORE_FILE)); - - $store_mysql = (!$is_error && $this->checkMessagesStoreType(self::STORE_MYSQL)) - || ($is_error && $this->checkErrorsStoreType(self::STORE_MYSQL)); - - if ($this->prefix) - $text = $this->prefix.$text; - - // if ($store_mysql) { - // $db = getMySQL('local_logs', true); - // $data = [ - // 'ts' => $ts, - // 'num' => $num, - // 'time' => $exectime, - // 'custom' => intval($custom), - // 'errno' => $errno, - // 'file' => $errfile, - // 'line' => $errline, - // 'text' => $text, - // 'stacktrace' => $bt, - // 'is_cli' => PHP_SAPI == 'cli' ? 1 : 0, - // ]; - // if (PHP_SAPI == 'cli') { - // $data += [ - // 'ip' => '', - // 'ua' => '', - // 'url' => '', - // ]; - // } else { - // $data += [ - // 'ip' => ip2ulong($_SERVER['REMOTE_ADDR']), - // 'ua' => $_SERVER['HTTP_USER_AGENT'] ?? '', - // 'url' => $_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'] - // ]; - // } - // $db->insert('backend_errors', $data); - // } - - if ($store_file) { - $title = PHP_SAPI == 'cli' ? 'cli' : $_SERVER['REQUEST_URI']; - $date = date('d/m/y H:i:s', $ts); - $exectime = (string)$exectime; - if (strlen($exectime) < 6) - $exectime .= str_repeat('0', 6 - strlen($exectime)); - - $buf = ""; - if (!$logstarted) { - $buf .= "\n<e fg=white bg=magenta style=fgbright,bold> {$title} </e><e fg=white bg=blue style=fgbright> {$date} </e>\n"; - } - $buf .= "<e fg=".($is_error ? 'red' : 'white').">".($is_error ? 'E' : 'I')."=<e style=bold>${num}</e> <e fg=cyan>{$exectime}</e> "; - if ($is_error && !$custom) { - $buf .= "<e fg=green>{$errfile}<e fg=white>:<e fg=green style=fgbright>{$errline}</e> (".self::errname($errno).") "; - } - $buf = stransi($buf); - - $buf .= $text; - $buf .= "\n"; - if ($is_error && $config['debug_backtrace']) { - $buf .= $bt."\n"; - } - - $debug_file = $this->getDebugFile(); - - $logdir = dirname($debug_file); - if (!file_exists($logdir)) { - mkdir($logdir); - setperm($logdir); - } - - $f = fopen($debug_file, 'a'); - if ($f) { - fwrite($f, $buf); - fclose($f); - } - } - - $this->reportRecursionLevel--; - } - - public function canReport() { - return $this->reportRecursionLevel < 2; - } - - public function setErrorsStoreType($errorsStoreType) { - $this->errorsStoreType = $errorsStoreType; - } - - public function setMessagesStoreType($messagesStoreType) { - $this->messagesStoreType = $messagesStoreType; - } - - public function checkMessagesStoreType($store_type) { - return $this->messagesStoreType == $store_type || $this->messagesStoreType == self::STORE_BOTH; - } - - public function checkErrorsStoreType($store_type) { - return $this->errorsStoreType == $store_type || $this->errorsStoreType == self::STORE_BOTH; - } - - public function overrideDebugFile($file) { - $this->overridenDebugFile = $file; - } - - protected function getDebugFile() { - global $config; - return is_null($this->overridenDebugFile) ? ROOT.'/'.$config['debug_file'] : $this->overridenDebugFile; - } - - public function setSilence($silent) { - $this->silent = $silent; - } - - public function setPrefix($prefix) { - $this->prefix = $prefix; - } - - public static function errname($errno) { - static $errors = null; - if (is_null($errors)) { - $errors = array_flip(array_slice(get_defined_constants(true)['Core'], 0, 15, true)); - } - return $errors[$errno]; - } - - public static function getTypes() { - return self::$Types; - } - -} - -class debug_measure { - - private $name; - private $time; - private $started = false; - - /** - * @param string $name - * @return $this - */ - public function start($name = null) { - if (is_null($name)) { - $name = strgen(3); - } - $this->name = $name; - $this->time = microtime(true); - $this->started = true; - return $this; - } - - /** - * @return float|string|null - */ - public function finish() { - if (!$this->started) { - debugLog("debug_measure::finish(): not started, name=".$this->name); - return null; - } - - $time = (microtime(true) - $this->time); - debugLog("MEASURE".($this->name != '' ? ' '.$this->name : '').": ".$time); - - $this->started = false; - return $time; - } - -} - -/** - * @param $var - * @return string - */ -function str_print_r($var) { - ob_start(); - print_r($var); - return trim(ob_get_clean()); -} - -/** - * @param $var - * @return string - */ -function str_var_dump($var) { - ob_start(); - var_dump($var); - return trim(ob_get_clean()); -} - -/** - * @param $args - * @param bool $all_dump - * @return string - */ -function str_vars($args, $all_dump = false) { - return implode(' ', array_map(function($a) use ($all_dump) { - if ($all_dump) { - return str_var_dump($a); - } - $type = gettype($a); - if ($type == 'string' || $type == 'integer' || $type == 'double') { - return $a; - } else if ($type == 'array' || $type == 'object') { - return str_print_r($a); - } else { - return str_var_dump($a); - } - }, $args)); -} - -/** - * @param int $shift - * @return string - */ -function backtrace($shift = 0) { - $bt = debug_backtrace(); - $lines = []; - foreach ($bt as $i => $t) { - if ($i < $shift) { - continue; - } - if (!isset($t['file'])) { - $lines[] = 'from ?'; - } else { - $lines[] = 'from '.$t['file'].':'.$t['line']; - } - } - return implode("\n", $lines); -} - -/** - * @param mixed ...$args - */ -function debugLog(...$args) { - global $config; - if (!$config['is_dev']) - return; - - debug::getInstance()->report(false, str_vars($args)); -} - -function debugLogOnProd(...$args) { - debug::getInstance()->report(false, str_vars($args)); -} - -/** - * @param mixed ...$args - */ -function debugError(...$args) { - $debug = debug::getInstance(); - if ($debug->canReport()) { - $debug->report(true, str_vars($args)); - } -} diff --git a/localwebsite/engine/model.php b/localwebsite/engine/model.php deleted file mode 100644 index 4dd981c..0000000 --- a/localwebsite/engine/model.php +++ /dev/null @@ -1,243 +0,0 @@ -<?php - -abstract class model { - - const DB_TABLE = null; - const DB_KEY = 'id'; - - const STRING = 0; - const INTEGER = 1; - const FLOAT = 2; - const ARRAY = 3; - const BOOLEAN = 4; - const JSON = 5; - const SERIALIZED = 6; - - protected static array $SpecCache = []; - - public static function create_instance(...$args) { - $cl = get_called_class(); - return new $cl(...$args); - } - - public function __construct(array $raw) { - if (!isset(self::$SpecCache[static::class])) { - list($fields, $model_name_map, $db_name_map) = static::get_spec(); - self::$SpecCache[static::class] = [ - 'fields' => $fields, - 'model_name_map' => $model_name_map, - 'db_name_map' => $db_name_map - ]; - } - - foreach (self::$SpecCache[static::class]['fields'] as $field) - $this->{$field['model_name']} = self::cast_to_type($field['type'], $raw[$field['db_name']]); - - if (is_null(static::DB_TABLE)) - trigger_error('class '.get_class($this).' doesn\'t have DB_TABLE defined'); - } - - /** - * @param $fields - * - * TODO: support adding or subtracting (SET value=value+1) - */ - public function edit($fields) { - $db = getDB(); - - $model_upd = []; - $db_upd = []; - - foreach ($fields as $name => $value) { - $index = self::$SpecCache[static::class]['db_name_map'][$name] ?? null; - if (is_null($index)) { - debugError(__METHOD__.': field `'.$name.'` not found in '.static::class); - continue; - } - - $field = self::$SpecCache[static::class]['fields'][$index]; - switch ($field['type']) { - case self::ARRAY: - if (is_array($value)) { - $db_upd[$name] = implode(',', $value); - $model_upd[$field['model_name']] = $value; - } else { - debugError(__METHOD__.': field `'.$name.'` is expected to be array. skipping.'); - } - break; - - case self::INTEGER: - $value = (int)$value; - $db_upd[$name] = $value; - $model_upd[$field['model_name']] = $value; - break; - - case self::FLOAT: - $value = (float)$value; - $db_upd[$name] = $value; - $model_upd[$field['model_name']] = $value; - break; - - case self::BOOLEAN: - $db_upd[$name] = $value ? 1 : 0; - $model_upd[$field['model_name']] = $value; - break; - - case self::JSON: - $db_upd[$name] = jsonEncode($value); - $model_upd[$field['model_name']] = $value; - break; - - case self::SERIALIZED: - $db_upd[$name] = serialize($value); - $model_upd[$field['model_name']] = $value; - break; - - default: - $value = (string)$value; - $db_upd[$name] = $value; - $model_upd[$field['model_name']] = $value; - break; - } - } - - if (!empty($db_upd) && !$db->update(static::DB_TABLE, $db_upd, static::DB_KEY."=?", $this->get_id())) { - debugError(__METHOD__.': failed to update database'); - return; - } - - if (!empty($model_upd)) { - foreach ($model_upd as $name => $value) - $this->{$name} = $value; - } - } - - public function get_id() { - return $this->{to_camel_case(static::DB_KEY)}; - } - - public function as_array(array $fields = [], array $custom_getters = []): array { - if (empty($fields)) - $fields = array_keys(static::$SpecCache[static::class]['db_name_map']); - - $array = []; - foreach ($fields as $field) { - if (isset($custom_getters[$field]) && is_callable($custom_getters[$field])) { - $array[$field] = $custom_getters[$field](); - } else { - $array[$field] = $this->{to_camel_case($field)}; - } - } - - return $array; - } - - protected static function cast_to_type(int $type, $value) { - switch ($type) { - case self::BOOLEAN: - return (bool)$value; - - case self::INTEGER: - return (int)$value; - - case self::FLOAT: - return (float)$value; - - case self::ARRAY: - return array_filter(explode(',', $value)); - - case self::JSON: - $val = jsonDecode($value); - if (!$val) - $val = null; - return $val; - - case self::SERIALIZED: - $val = unserialize($value); - if ($val === false) - $val = null; - return $val; - - default: - return (string)$value; - } - } - - protected static function get_spec(): array { - $rc = new ReflectionClass(static::class); - $props = $rc->getProperties(ReflectionProperty::IS_PUBLIC); - - $list = []; - $index = 0; - - $model_name_map = []; - $db_name_map = []; - - foreach ($props as $prop) { - if ($prop->isStatic()) - continue; - - $name = $prop->getName(); - if (startsWith($name, '_')) - continue; - - $type = $prop->getType(); - $phpdoc = $prop->getDocComment(); - - $mytype = null; - if (!$prop->hasType() && !$phpdoc) - $mytype = self::STRING; - else { - $typename = $type->getName(); - switch ($typename) { - case 'string': - $mytype = self::STRING; - break; - case 'int': - $mytype = self::INTEGER; - break; - case 'float': - $mytype = self::FLOAT; - break; - case 'array': - $mytype = self::ARRAY; - break; - case 'bool': - $mytype = self::BOOLEAN; - break; - } - - if ($phpdoc != '') { - $pos = strpos($phpdoc, '@'); - if ($pos === false) - continue; - - if (substr($phpdoc, $pos+1, 4) == 'json') - $mytype = self::JSON; - else if (substr($phpdoc, $pos+1, 5) == 'array') - $mytype = self::ARRAY; - else if (substr($phpdoc, $pos+1, 10) == 'serialized') - $mytype = self::SERIALIZED; - } - } - - if (is_null($mytype)) - debugError(__METHOD__.": ".$name." is still null in ".static::class); - - $dbname = from_camel_case($name); - $list[] = [ - 'type' => $mytype, - 'model_name' => $name, - 'db_name' => $dbname - ]; - - $model_name_map[$name] = $index; - $db_name_map[$dbname] = $index; - - $index++; - } - - return [$list, $model_name_map, $db_name_map]; - } - -} diff --git a/localwebsite/engine/request_handler.php b/localwebsite/engine/request_handler.php deleted file mode 100644 index 535e850..0000000 --- a/localwebsite/engine/request_handler.php +++ /dev/null @@ -1,142 +0,0 @@ -<?php - -abstract class request_handler { - - const GET = 'GET'; - const POST = 'POST'; - - private static array $AllowedInputTypes = ['i', 'f', 'b', 'e' /* enum */]; - - public function dispatch(string $act) { - $method = $_SERVER['REQUEST_METHOD'] == 'POST' ? 'POST' : 'GET'; - return $this->call_act($method, $act); - } - - protected function before_dispatch(string $method, string $act)/*: ?array*/ { - return null; - } - - protected function call_act(string $method, string $act, array $input = []) { - global $RouterInput; - - $notfound = !method_exists($this, $method.'_'.$act) || !((new ReflectionMethod($this, $method.'_'.$act))->isPublic()); - if ($notfound) - $this->method_not_found($method, $act); - - if (!empty($input)) { - foreach ($input as $k => $v) - $RouterInput[$k] = $v; - } - - $args = $this->before_dispatch($method, $act); - return call_user_func_array([$this, $method.'_'.$act], is_array($args) ? [$args] : []); - } - - abstract protected function method_not_found(string $method, string $act); - - protected function input(string $input, bool $as_assoc = false): array { - $input = preg_split('/,\s+?/', $input, null, PREG_SPLIT_NO_EMPTY); - - $ret = []; - foreach ($input as $var) { - list($type, $name, $enum_values, $enum_default) = self::parse_input_var($var); - - $value = param($name); - - switch ($type) { - case 'i': - if (is_null($value) && !is_null($enum_default)) { - $value = (int)$enum_default; - } else { - $value = (int)$value; - } - break; - - case 'f': - if (is_null($value) && !is_null($enum_default)) { - $value = (float)$enum_default; - } else { - $value = (float)$value; - } - break; - - case 'b': - if (is_null($value) && !is_null($enum_default)) { - $value = (bool)$enum_default; - } else { - $value = (bool)$value; - } - break; - - case 'e': - if (!in_array($value, $enum_values)) { - $value = !is_null($enum_default) ? $enum_default : ''; - } - break; - } - - if (!$as_assoc) { - $ret[] = $value; - } else { - $ret[$name] = $value; - } - } - - return $ret; - } - protected static function parse_input_var(string $var): array { - $type = null; - $name = null; - $enum_values = null; - $enum_default = null; - - $pos = strpos($var, ':'); - if ($pos !== false) { - $type = substr($var, 0, $pos); - $rest = substr($var, $pos+1); - - if (!in_array($type, self::$AllowedInputTypes)) { - trigger_error('request_handler::parse_input_var('.$var.'): unknown type '.$type); - $type = null; - } - - switch ($type) { - case 'e': - $br_from = strpos($rest, '('); - $br_to = strpos($rest, ')'); - - if ($br_from === false || $br_to === false) { - trigger_error('request_handler::parse_input_var('.$var.'): failed to parse enum values'); - $type = null; - $name = $rest; - break; - } - - $enum_values = array_map('trim', explode('|', trim(substr($rest, $br_from+1, $br_to-$br_from-1)))); - $name = trim(substr($rest, 0, $br_from)); - - if (!empty($enum_values)) foreach ($enum_values as $key => $val) { - if (substr($val, 0, 1) == '=') { - $enum_values[$key] = substr($val, 1); - $enum_default = $enum_values[$key]; - } - } - break; - - default: - if (($eq_pos = strpos($rest, '=')) !== false) { - $enum_default = substr($rest, $eq_pos+1); - $rest = substr($rest, 0, $eq_pos); - } - $name = trim($rest); - break; - } - } else { - $type = 's'; - $name = $var; - } - - return [$type, $name, $enum_values, $enum_default]; - } - -} diff --git a/localwebsite/engine/router.php b/localwebsite/engine/router.php deleted file mode 100644 index 5e966a9..0000000 --- a/localwebsite/engine/router.php +++ /dev/null @@ -1,199 +0,0 @@ -<?php - -class router { - - protected array $routes = [ - 'children' => [], - 're_children' => [] - ]; - - public function add($template, $value) { - if ($template == '') { - return; - } - - // expand {enum,erat,ions} - $templates = [[$template, $value]]; - if (preg_match_all('/\{([\w\d_\-,]+)\}/', $template, $matches)) { - foreach ($matches[1] as $match_index => $variants) { - $variants = explode(',', $variants); - $variants = array_map('trim', $variants); - $variants = array_filter($variants, function($s) { return $s != ''; }); - - for ($i = 0; $i < count($templates); ) { - list($template, $value) = $templates[$i]; - $new_templates = []; - foreach ($variants as $variant_index => $variant) { - $new_templates[] = [ - str_replace_once($matches[0][$match_index], $variant, $template), - str_replace('${'.($match_index+1).'}', $variant, $value) - ]; - } - array_splice($templates, $i, 1, $new_templates); - $i += count($new_templates); - } - } - } - - // process all generated routes - foreach ($templates as $template) { - list($template, $value) = $template; - - $start_pos = 0; - $parent = &$this->routes; - $template_len = strlen($template); - - while ($start_pos < $template_len) { - $slash_pos = strpos($template, '/', $start_pos); - if ($slash_pos !== false) { - $part = substr($template, $start_pos, $slash_pos-$start_pos+1); - $start_pos = $slash_pos+1; - } else { - $part = substr($template, $start_pos); - $start_pos = $template_len; - } - - $parent = &$this->_addRoute($parent, $part, - $start_pos < $template_len ? null : $value); - } - } - } - - protected function &_addRoute(&$parent, $part, $value = null) { - $par_pos = strpos($part, '('); - $is_regex = $par_pos !== false && ($par_pos == 0 || $part[$par_pos-1] != '\\'); - - $children_key = !$is_regex ? 'children' : 're_children'; - - if (isset($parent[$children_key][$part])) { - if (is_null($value)) { - $parent = &$parent[$children_key][$part]; - } else { - if (!isset($parent[$children_key][$part]['value'])) { - $parent[$children_key][$part]['value'] = $value; - } else { - trigger_error(__METHOD__.': route is already defined'); - } - } - return $parent; - } - - $child = [ - 'children' => [], - 're_children' => [] - ]; - if (!is_null($value)) { - $child['value'] = $value; - } - - $parent[$children_key][$part] = $child; - return $parent[$children_key][$part]; - } - - public function find($uri) { - if ($uri != '/' && $uri[0] == '/') { - $uri = substr($uri, 1); - } - $start_pos = 0; - $parent = &$this->routes; - $uri_len = strlen($uri); - $matches = []; - - while ($start_pos < $uri_len) { - $slash_pos = strpos($uri, '/', $start_pos); - if ($slash_pos !== false) { - $part = substr($uri, $start_pos, $slash_pos-$start_pos+1); - $start_pos = $slash_pos+1; - } else { - $part = substr($uri, $start_pos); - $start_pos = $uri_len; - } - - $found = false; - if (isset($parent['children'][$part])) { - $parent = &$parent['children'][$part]; - $found = true; - } else if (!empty($parent['re_children'])) { - foreach ($parent['re_children'] as $re => &$child) { - $exp = '#^'.$re.'$#'; - $re_result = preg_match($exp, $part, $match); - if ($re_result === false) { - debugError(__METHOD__.": regex $exp failed"); - continue; - } - - if ($re_result) { - if (count($match) > 1) { - $matches = array_merge($matches, array_slice($match, 1)); - } - $parent = &$child; - $found = true; - break; - } - } - } - - if (!$found) { - return false; - } - } - - if (!isset($parent['value'])) { - return false; - } - - $value = $parent['value']; - if (!empty($matches)) { - foreach ($matches as $i => $match) { - $needle = '$('.($i+1).')'; - $pos = strpos($value, $needle); - if ($pos !== false) { - $value = substr_replace($value, $match, $pos, strlen($needle)); - } - } - } - - return $value; - } - - public function load($routes) { - $this->routes = $routes; - } - - public function dump() { - return $this->routes; - } - -} - -function routerFind(router $router) { - $document_uri = $_SERVER['REQUEST_URI']; - if (($pos = strpos($document_uri, '?')) !== false) - $document_uri = substr($document_uri, 0, $pos); - $document_uri = urldecode($document_uri); - - $fixed_document_uri = preg_replace('#/+#', '/', $document_uri); - if ($fixed_document_uri != $document_uri && !is_xhr_request()) { - redirect($fixed_document_uri); - } else { - $document_uri = $fixed_document_uri; - } - - $route = $router->find($document_uri); - if ($route === false) - return false; - - $route = preg_split('/ +/', $route); - $handler = $route[0]; - $act = $route[1]; - $input = []; - if (count($route) > 2) { - for ($i = 2; $i < count($route); $i++) { - $var = $route[$i]; - list($k, $v) = explode('=', $var); - $input[trim($k)] = trim($v); - } - } - - return [$handler, $act, $input]; -}
\ No newline at end of file diff --git a/localwebsite/engine/tpl.php b/localwebsite/engine/tpl.php deleted file mode 100644 index 3d18c9a..0000000 --- a/localwebsite/engine/tpl.php +++ /dev/null @@ -1,520 +0,0 @@ -<?php - -abstract class base_tpl { - - public $twig; - protected $vars = []; - protected $global_vars = []; - protected $title = ''; - protected $title_modifiers = []; - protected $keywords = ''; - protected $description = ''; - protected $js = []; - protected $lang_keys = []; - protected $static = []; - protected $external_static = []; - protected $head = []; - protected $globals_applied = false; - protected $static_time; - - public function __construct($templates_dir, $cache_dir) { - global $config; - - // $cl = get_called_class(); - - $this->twig = self::twig_instance($templates_dir, $cache_dir, $config['is_dev']); - $this->static_time = time(); - } - - public static function twig_instance($templates_dir, $cache_dir, $auto_reload) { - // must specify a second argument ($rootPath) here - // otherwise it will be getcwd() and it's www-prod/htdocs/ for apache and www-prod/ for cli code - // this is bad for templates rebuilding - $twig_loader = new \Twig\Loader\FilesystemLoader($templates_dir, ROOT); - - $env_options = []; - if (!is_null($cache_dir)) { - $env_options += [ - 'cache' => $cache_dir, - 'auto_reload' => $auto_reload - ]; - } - - $twig = new \Twig\Environment($twig_loader, $env_options); - $twig->addExtension(new Twig_MyExtension); - - return $twig; - } - - public function render($template, array $vars = []) { - $this->apply_globals(); - return $this->do_render($template, array_merge($this->vars, $vars)); - } - - protected function do_render($template, $vars) { - global $config; - $s = ''; - try { - $s = $this->twig->render($template, $vars); - } catch (\Twig\Error\Error $e) { - $error = get_class($e).": failed to render"; - $source_ctx = $e->getSourceContext(); - if ($source_ctx) { - $path = $source_ctx->getPath(); - if (startsWith($path, ROOT)) - $path = substr($path, strlen(ROOT)+1); - $error .= " ".$source_ctx->getName()." (".$path.") at line ".$e->getTemplateLine(); - } - $error .= ": "; - $error .= $e->getMessage(); - debugError($error); - if ($config['is_dev']) - $s = $error."\n"; - } - return $s; - } - - public function set($arg1, $arg2 = null) { - if (is_array($arg1)) { - foreach ($arg1 as $key => $value) { - $this->vars[$key] = $value; - } - } elseif ($arg2 !== null) { - $this->vars[$arg1] = $arg2; - } - } - - public function is_set($key): bool { - return isset($this->vars[$key]); - } - - public function set_global($arg1, $arg2 = null) { - if (is_array($arg1)) { - foreach ($arg1 as $key => $value) { - $this->global_vars[$key] = $value; - } - } elseif ($arg2 !== null) { - $this->global_vars[$arg1] = $arg2; - } - } - - public function is_global_set($key): bool { - return isset($this->global_vars[$key]); - } - - public function get_global($key) { - return $this->is_global_set($key) ? $this->global_vars[$key] : null; - } - - public function apply_globals() { - if (!empty($this->global_vars) && !$this->globals_applied) { - foreach ($this->global_vars as $key => $value) - $this->twig->addGlobal($key, $value); - $this->globals_applied = true; - } - } - - /** - * @param string $title - */ - public function set_title($title) { - $this->title = $title; - } - - /** - * @return string - */ - public function get_title() { - $title = $this->title != '' ? $this->title : 'Домашний сайт'; - if (!empty($this->title_modifiers)) { - foreach ($this->title_modifiers as $modifier) { - $title = $modifier($title); - } - } - return $title; - } - - /** - * @param callable $callable - */ - public function add_page_title_modifier(callable $callable) { - if (!is_callable($callable)) { - trigger_error(__METHOD__.': argument is not callable'); - } else { - $this->title_modifiers[] = $callable; - } - } - - /** - * @param string $css_name - * @param null $extra - */ - public function add_static(string $name, $extra = null) { - global $config; - // $is_css = endsWith($name, '.css'); - $this->static[] = [$name, $extra]; - } - - public function add_external_static($type, $url) { - $this->external_static[] = ['type' => $type, 'url' => $url]; - } - - public function add_js($js) { - $this->js[] = $js; - } - - public function add_lang_keys(array $keys) { - $this->lang_keys = array_merge($this->lang_keys, $keys); - } - - public function add_head($html) { - $this->head[] = $html; - } - - public function get_head_html() { - global $config; - $lines = []; - $public_path = $config['static_public_path']; - foreach ($this->static as $val) { - list($name, $extra) = $val; - if (endsWith($name, '.js')) - $lines[] = self::js_link($public_path.'/'.$name, $config['static'][$name] ?? 1); - else - $lines[] = self::css_link($public_path.'/'.$name, $config['static'][$name] ?? 1, $extra); - } - if (!empty($this->external_static)) { - foreach ($this->external_static as $ext) { - if ($ext['type'] == 'js') - $lines[] = self::js_link($ext['url']); - else if ($ext['type'] == 'css') - $lines[] = self::css_link($ext['url']); - } - } - if (!empty($this->head)) { - $lines = array_merge($lines, $this->head); - } - return implode("\n", $lines); - } - - public static function js_link($name, $version = null): string { - if ($version !== null) - $name .= '?'.$version; - return '<script src="'.$name.'" type="text/javascript"></script>'; - } - - public static function css_link($name, $version = null, $extra = null) { - if ($version !== null) - $name .= '?'.$version; - $s = '<link'; - if (is_array($extra)) { - if (!empty($extra['id'])) - $s .= ' id="'.$extra['id'].'"'; - } - $s .= ' rel="stylesheet" type="text/css"'; - if (is_array($extra) && !empty($extra['media'])) - $s .= ' media="'.$extra['media'].'"'; - $s .= ' href="'.$name.'"'; - $s .= '>'; - return $s; - } - - public function get_lang_keys() { - global $lang; - $keys = []; - if (!empty($this->lang_keys)) { - foreach ($this->lang_keys as $key) - $keys[$key] = $lang[$key]; - } - return $keys; - } - - public function render_not_found() { - http_response_code(404); - if (!is_xhr_request()) { - $this->render_page('404.twig'); - } else { - ajax_error(['code' => 404]); - } - } - - /** - * @param null|string $reason - */ - public function render_forbidden($reason = null) { - http_response_code(403); - if (!is_xhr_request()) { - $this->set(['reason' => $reason]); - $this->render_page('403.twig'); - } else { - $data = ['code' => 403]; - if (!is_null($reason)) - $data['reason'] = $reason; - ajax_error($data); - } - } - - public function must_revalidate() { - header('Cache-Control: no-store, no-cache, must-revalidate'); - } - - abstract public function render_page($template); - -} - -class web_tpl extends base_tpl { - - protected $alternate = false; - - public function __construct() { - global $config; - $templates = $config['templates']['web']; - parent::__construct( - ROOT.'/'. $templates['root'], - $config['twig_cache'] - ? ROOT.'/'.$templates['cache'] - : null - ); - } - - public function set_alternate($alt) { - $this->alternate = $alt; - } - - public function render_page($template) { - echo $this->_render_header(); - echo $this->_render_body($template); - echo $this->_render_footer(); - exit; - } - - public function _render_header() { - global $config; - $this->apply_globals(); - - $vars = [ - 'title' => $this->get_title(), - 'keywords' => $this->keywords, - 'description' => $this->description, - 'alternate' => $this->alternate, - 'static' => $this->get_head_html(), - ]; - return $this->do_render('header.twig', $vars); - } - - public function _render_body($template) { - return $this->do_render($template, $this->vars); - } - - public function _render_footer() { - $exec_time = microtime(true) - START_TIME; - $exec_time = round($exec_time, 4); - - $footer_vars = [ - 'exec_time' => $exec_time, - 'js' => !empty($this->js) ? implode("\n", $this->js) : '', - ]; - return $this->do_render('footer.twig', $footer_vars); - } - -} - -class Twig_MyExtension extends \Twig\Extension\AbstractExtension { - - public function getFilters() { - global $lang; - - return array( - new \Twig\TwigFilter('lang', 'lang'), - - new \Twig\TwigFilter('lang', function($key, array $args = []) use (&$lang) { - array_walk($args, function(&$item, $key) { - $item = htmlescape($item); - }); - array_unshift($args, $key); - return call_user_func_array([$lang, 'get'], $args); - }, ['is_variadic' => true]), - - new \Twig\TwigFilter('plural', function($text, array $args = []) use (&$lang) { - array_unshift($args, $text); - return call_user_func_array([$lang, 'num'], $args); - }, ['is_variadic' => true]), - - new \Twig\TwigFilter('format_number', function($number, array $args = []) { - array_unshift($args, $number); - return call_user_func_array('formatNumber', $args); - }, ['is_variadic' => true]), - - new \Twig\TwigFilter('short_number', function($number, array $args = []) { - array_unshift($args, $number); - return call_user_func_array('shortNumber', $args); - }, ['is_variadic']), - - new \Twig\TwigFilter('format_time', function($ts, array $args = []) { - array_unshift($args, $ts); - return call_user_func_array('formatTime', $args); - }, ['is_variadic' => true]), - - new \Twig\TwigFilter('format_duration', function($seconds, array $args = []) { - array_unshift($args, $seconds); - return call_user_func_array('formatDuration', $args); - }, ['is_variadic' => true]), - ); - } - - public function getTokenParsers() { - return [new JsTagTokenParser()]; - } - - public function getName() { - return 'lang'; - } - -} - -// Based on https://stackoverflow.com/questions/26170727/how-to-create-a-twig-custom-tag-that-executes-a-callback -class JsTagTokenParser extends \Twig\TokenParser\AbstractTokenParser { - - public function parse(\Twig\Token $token) { - $lineno = $token->getLine(); - $stream = $this->parser->getStream(); - - // recovers all inline parameters close to your tag name - $params = array_merge([], $this->getInlineParams($token)); - - $continue = true; - while ($continue) { - // create subtree until the decideJsTagFork() callback returns true - $body = $this->parser->subparse(array ($this, 'decideJsTagFork')); - - // I like to put a switch here, in case you need to add middle tags, such - // as: {% js %}, {% nextjs %}, {% endjs %}. - $tag = $stream->next()->getValue(); - switch ($tag) { - case 'endjs': - $continue = false; - break; - default: - throw new \Twig\Error\SyntaxError(sprintf('Unexpected end of template. Twig was looking for the following tags "endjs" to close the "mytag" block started at line %d)', $lineno), -1); - } - - // you want $body at the beginning of your arguments - array_unshift($params, $body); - - // if your endjs can also contains params, you can uncomment this line: - // $params = array_merge($params, $this->getInlineParams($token)); - // and comment this one: - $stream->expect(\Twig\Token::BLOCK_END_TYPE); - } - - return new JsTagNode(new \Twig\Node\Node($params), $lineno, $this->getTag()); - } - - /** - * Recovers all tag parameters until we find a BLOCK_END_TYPE ( %} ) - * - * @param \Twig\Token $token - * @return array - */ - protected function getInlineParams(\Twig\Token $token) { - $stream = $this->parser->getStream(); - $params = array (); - while (!$stream->test(\Twig\Token::BLOCK_END_TYPE)) { - $params[] = $this->parser->getExpressionParser()->parseExpression(); - } - $stream->expect(\Twig\Token::BLOCK_END_TYPE); - return $params; - } - - /** - * Callback called at each tag name when subparsing, must return - * true when the expected end tag is reached. - * - * @param \Twig\Token $token - * @return bool - */ - public function decideJsTagFork(\Twig\Token $token) { - return $token->test(['endjs']); - } - - /** - * Your tag name: if the parsed tag match the one you put here, your parse() - * method will be called. - * - * @return string - */ - public function getTag() { - return 'js'; - } - -} - -class JsTagNode extends \Twig\Node\Node { - - public function __construct($params, $lineno = 0, $tag = null) { - parent::__construct(['params' => $params], [], $lineno, $tag); - } - - public function compile(\Twig\Compiler $compiler) { - $count = count($this->getNode('params')); - - $compiler->addDebugInfo($this); - $compiler - ->write('global $__tpl;') - ->raw(PHP_EOL); - - for ($i = 0; ($i < $count); $i++) { - // argument is not an expression (such as, a \Twig\Node\Textbody) - // we should trick with output buffering to get a valid argument to pass - // to the functionToCall() function. - if (!($this->getNode('params')->getNode($i) instanceof \Twig\Node\Expression\AbstractExpression)) { - $compiler - ->write('ob_start();') - ->raw(PHP_EOL); - - $compiler - ->subcompile($this->getNode('params')->getNode($i)); - - $compiler - ->write('$js = ob_get_clean();') - ->raw(PHP_EOL); - } - } - - $compiler - ->write('$__tpl->add_js($js);') - ->raw(PHP_EOL) - ->write('unset($js);') - ->raw(PHP_EOL); - } - -} - - - -/** - * @param $data - */ -function ajax_ok($data) { - ajax_response(['response' => $data]); -} - -/** - * @param $error - * @param int $code - */ -function ajax_error($error, $code = 200) { - ajax_response(['error' => $error], $code); -} - -/** - * @param $data - * @param int $code - */ -function ajax_response($data, $code = 200) { - header('Cache-Control: no-cache, must-revalidate'); - header('Pragma: no-cache'); - header('Content-Type: application/json; charset=utf-8'); - http_response_code($code); - echo jsonEncode($data); - exit; -}
\ No newline at end of file |