summaryrefslogtreecommitdiff
path: root/localwebsite/engine
diff options
context:
space:
mode:
authorEvgeny Zinoviev <me@ch1p.io>2024-02-19 01:44:02 +0300
committerEvgeny Zinoviev <me@ch1p.io>2024-02-19 01:44:11 +0300
commit3741f7cf78a288e967415ccb6736c888a21c211b (patch)
treea48d8331c9936d6c108de4d0f9179a089b1e56e6 /localwebsite/engine
parentd79309e498cdc1358c81367ce2a93a5731e517d1 (diff)
web_kbn: almost completely ported lws to python
Diffstat (limited to 'localwebsite/engine')
-rw-r--r--localwebsite/engine/debug.php355
-rw-r--r--localwebsite/engine/model.php243
-rw-r--r--localwebsite/engine/request_handler.php142
-rw-r--r--localwebsite/engine/router.php199
-rw-r--r--localwebsite/engine/tpl.php520
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