diff --git a/database_dump/smart-home-server_database.sql b/database_dump/smart-home-server_database.sql new file mode 100644 index 0000000..be9b27a --- /dev/null +++ b/database_dump/smart-home-server_database.sql @@ -0,0 +1,22 @@ +-- phpMyAdmin SQL Dump +-- version 5.2.1 +-- https://www.phpmyadmin.net/ +-- +-- Хост: localhost +-- Время создания: Авг 08 2025 г., 06:09 +-- Версия сервера: 11.8.2-MariaDB +-- Версия PHP: 8.4.10 + +SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO"; +START TRANSACTION; +SET time_zone = "+00:00"; + + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8mb4 */; + +-- +-- База данных: `smart-home-server` +-- diff --git a/database_dump/smart-home-server_extra.sql b/database_dump/smart-home-server_extra.sql new file mode 100644 index 0000000..87e842d --- /dev/null +++ b/database_dump/smart-home-server_extra.sql @@ -0,0 +1,92 @@ + +-- +-- Индексы сохранённых таблиц +-- + +-- +-- Индексы таблицы `devices` +-- +ALTER TABLE `devices` + ADD PRIMARY KEY (`id`); + +-- +-- Индексы таблицы `groups` +-- +ALTER TABLE `groups` + ADD PRIMARY KEY (`id`); + +-- +-- Индексы таблицы `logs` +-- +ALTER TABLE `logs` + ADD PRIMARY KEY (`id`); + +-- +-- Индексы таблицы `notifications` +-- +ALTER TABLE `notifications` + ADD PRIMARY KEY (`id`); + +-- +-- Индексы таблицы `scripts` +-- +ALTER TABLE `scripts` + ADD PRIMARY KEY (`id`); + +-- +-- Индексы таблицы `users` +-- +ALTER TABLE `users` + ADD PRIMARY KEY (`id`); + +-- +-- Индексы таблицы `user_sessions` +-- +ALTER TABLE `user_sessions` + ADD PRIMARY KEY (`id`); + +-- +-- AUTO_INCREMENT для сохранённых таблиц +-- + +-- +-- AUTO_INCREMENT для таблицы `devices` +-- +ALTER TABLE `devices` + MODIFY `id` int(11) NOT NULL AUTO_INCREMENT; + +-- +-- AUTO_INCREMENT для таблицы `groups` +-- +ALTER TABLE `groups` + MODIFY `id` int(11) NOT NULL AUTO_INCREMENT; + +-- +-- AUTO_INCREMENT для таблицы `logs` +-- +ALTER TABLE `logs` + MODIFY `id` int(11) NOT NULL AUTO_INCREMENT; + +-- +-- AUTO_INCREMENT для таблицы `notifications` +-- +ALTER TABLE `notifications` + MODIFY `id` int(11) NOT NULL AUTO_INCREMENT; + +-- +-- AUTO_INCREMENT для таблицы `scripts` +-- +ALTER TABLE `scripts` + MODIFY `id` int(11) NOT NULL AUTO_INCREMENT; + +-- +-- AUTO_INCREMENT для таблицы `users` +-- +ALTER TABLE `users` + MODIFY `id` int(11) NOT NULL AUTO_INCREMENT; + +-- +-- AUTO_INCREMENT для таблицы `user_sessions` +-- +ALTER TABLE `user_sessions` + MODIFY `id` int(11) NOT NULL AUTO_INCREMENT; diff --git a/database_dump/smart-home-server_table_devices.sql b/database_dump/smart-home-server_table_devices.sql new file mode 100644 index 0000000..6fe9f6d --- /dev/null +++ b/database_dump/smart-home-server_table_devices.sql @@ -0,0 +1,23 @@ + +-- -------------------------------------------------------- + +-- +-- Структура таблицы `devices` +-- + +CREATE TABLE `devices` ( + `id` int(11) NOT NULL, + `alias` varchar(100) NOT NULL, + `name` varchar(100) NOT NULL, + `device_type` varchar(50) NOT NULL, + `device_name` varchar(100) NOT NULL, + `device_ip` varchar(50) NOT NULL, + `device_mac` varchar(100) NOT NULL, + `device_id` varchar(100) NOT NULL, + `firmware_version` varchar(10) NOT NULL, + `connection_state` varchar(50) NOT NULL COMMENT 'active | lost', + `description` text NOT NULL, + `lact_contact` timestamp NOT NULL, + `update_at` timestamp NOT NULL DEFAULT current_timestamp(), + `create_at` timestamp NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; diff --git a/database_dump/smart-home-server_table_groups.sql b/database_dump/smart-home-server_table_groups.sql new file mode 100644 index 0000000..e63cd81 --- /dev/null +++ b/database_dump/smart-home-server_table_groups.sql @@ -0,0 +1,16 @@ + +-- -------------------------------------------------------- + +-- +-- Структура таблицы `groups` +-- + +CREATE TABLE `groups` ( + `id` int(11) NOT NULL, + `alias` varchar(100) NOT NULL, + `name` varchar(100) NOT NULL, + `description` text NOT NULL, + `permissions` text NOT NULL DEFAULT '[]' COMMENT 'JSON struct. Array of permissions', + `update_at` timestamp NOT NULL DEFAULT current_timestamp(), + `create_at` timestamp NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; diff --git a/database_dump/smart-home-server_table_logs.sql b/database_dump/smart-home-server_table_logs.sql new file mode 100644 index 0000000..8963082 --- /dev/null +++ b/database_dump/smart-home-server_table_logs.sql @@ -0,0 +1,18 @@ + +-- -------------------------------------------------------- + +-- +-- Структура таблицы `logs` +-- + +CREATE TABLE `logs` ( + `id` int(11) NOT NULL, + `type` varchar(50) DEFAULT NULL COMMENT 'user | group | device | script | event | system | [custom_type] - (Для удобного поиска)', + `user_id` int(11) DEFAULT 0, + `group_alias` varchar(100) DEFAULT NULL, + `device_id` int(11) DEFAULT 0, + `script_alias` varchar(100) DEFAULT NULL, + `event_name` varchar(100) DEFAULT NULL, + `content` text NOT NULL, + `create_at` timestamp NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; diff --git a/database_dump/smart-home-server_table_notifications.sql b/database_dump/smart-home-server_table_notifications.sql new file mode 100644 index 0000000..d566f6c --- /dev/null +++ b/database_dump/smart-home-server_table_notifications.sql @@ -0,0 +1,17 @@ + +-- -------------------------------------------------------- + +-- +-- Структура таблицы `notifications` +-- + +CREATE TABLE `notifications` ( + `id` int(11) NOT NULL, + `user_id` int(11) NOT NULL COMMENT 'for user with id...', + `type` varchar(50) NOT NULL COMMENT '"system" | "script" | "aside"', + `icon` varchar(255) NOT NULL COMMENT 'Load from icon pack or url to img', + `title` varchar(255) NOT NULL, + `body` text NOT NULL, + `link` varchar(255) NOT NULL, + `create_at` timestamp NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; diff --git a/database_dump/smart-home-server_table_scripts.sql b/database_dump/smart-home-server_table_scripts.sql new file mode 100644 index 0000000..59665f0 --- /dev/null +++ b/database_dump/smart-home-server_table_scripts.sql @@ -0,0 +1,20 @@ + +-- -------------------------------------------------------- + +-- +-- Структура таблицы `scripts` +-- + +CREATE TABLE `scripts` ( + `id` int(11) NOT NULL, + `alias` varchar(100) NOT NULL, + `path` varchar(255) NOT NULL, + `filename` varchar(100) NOT NULL, + `state` varchar(50) NOT NULL COMMENT 'enabled | disabled', + `name` varchar(100) NOT NULL, + `description` text NOT NULL, + `added_by` int(11) NOT NULL COMMENT 'user_id', + `created_by` varchar(255) NOT NULL COMMENT 'Author of script', + `update_at` timestamp NOT NULL DEFAULT current_timestamp(), + `create_at` timestamp NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; diff --git a/database_dump/smart-home-server_table_user_sessions.sql b/database_dump/smart-home-server_table_user_sessions.sql new file mode 100644 index 0000000..d57eb1b --- /dev/null +++ b/database_dump/smart-home-server_table_user_sessions.sql @@ -0,0 +1,16 @@ + +-- -------------------------------------------------------- + +-- +-- Структура таблицы `user_sessions` +-- + +CREATE TABLE `user_sessions` ( + `id` int(11) NOT NULL, + `user_id` int(11) NOT NULL, + `token` varchar(100) NOT NULL, + `state` varchar(50) NOT NULL DEFAULT 'active' COMMENT 'active | outdated | killed', + `is_eternal` tinyint(1) NOT NULL DEFAULT 0, + `last_activity` timestamp NOT NULL DEFAULT current_timestamp(), + `create_at` timestamp NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; diff --git a/database_dump/smart-home-server_table_users.sql b/database_dump/smart-home-server_table_users.sql new file mode 100644 index 0000000..cf21849 --- /dev/null +++ b/database_dump/smart-home-server_table_users.sql @@ -0,0 +1,19 @@ + +-- -------------------------------------------------------- + +-- +-- Структура таблицы `users` +-- + +CREATE TABLE `users` ( + `id` int(11) NOT NULL, + `group_id` int(11) NOT NULL DEFAULT 0, + `nickname` varchar(100) NOT NULL COMMENT 'like login', + `role` varchar(50) NOT NULL DEFAULT '''user''' COMMENT 'user | admin | superadmin', + `password` varchar(255) NOT NULL COMMENT 'hash', + `first_name` int(11) NOT NULL, + `mid_name` int(11) NOT NULL, + `userpic` int(11) NOT NULL, + `contacts` text NOT NULL DEFAULT '{}' COMMENT 'json object', + `create_at` timestamp NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; diff --git a/server/.gitignore b/server/.gitignore new file mode 100644 index 0000000..a379718 --- /dev/null +++ b/server/.gitignore @@ -0,0 +1,9 @@ + +.phpintel/ +SHServ/Logs/ +node_modules/ +users_files/ +side_scripts/__pycache__ +side_scripts/*/__pycache__ +side_scripts/iziua_data_lendlease/used_urls.json +*.pyc diff --git a/server/.htaccess b/server/.htaccess new file mode 100644 index 0000000..f6caf00 --- /dev/null +++ b/server/.htaccess @@ -0,0 +1,8 @@ +#Options +FollowSymlinks +RewriteEngine On + +RewriteBase / +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d +RewriteCond %{REQUEST_URI} !.*\.(ico|gif|jpg|jpeg|png|js|css|json) +RewriteRule ^([^?]*) index.php \ No newline at end of file diff --git a/server/Fury/Drivers/TemplateDriver.php b/server/Fury/Drivers/TemplateDriver.php new file mode 100644 index 0000000..72137a6 --- /dev/null +++ b/server/Fury/Drivers/TemplateDriver.php @@ -0,0 +1,60 @@ + + * @version 0.1 + */ + +namespace Fury\Drivers; + +use \Fury\Modules\Template\DriverInterface; +use \Fury\Kernel\AppContainer; + +class TemplateDriver implements DriverInterface{ + public $events_ins; + + public function __construct(){ + $this -> events_ins = AppContainer::events(); + } + + public function event_create_template_instance($template_instance){ + $this -> events_ins -> module_call( + 'Template.create_template_instance', + compact('template_instance') + ); + } + + public function event_start_making(String $template_name, String $template_file, Array $inside_data, $template_instance){ + $this -> events_ins -> module_call( + 'Template.start_making', + compact( + 'template_instance', + 'template_name', + 'inside_data', + 'template_file' + ) + ); + } + + public function event_ready_template(String $template_name, $template_instance){ + $this -> events_ins -> module_call( + 'Template.ready_template', + compact( + 'template_instance', + 'template_name' + ) + ); + } + + public function event_start_joining(String $child_template_name, Array $inside_data){ + $this -> events_ins -> module_call( + 'Template.start_joining', + compact( + 'child_template_name', + 'inside_data' + ) + ); + } +} \ No newline at end of file diff --git a/server/Fury/Drivers/ThinBuilderDriver.php b/server/Fury/Drivers/ThinBuilderDriver.php new file mode 100644 index 0000000..a2cdaf2 --- /dev/null +++ b/server/Fury/Drivers/ThinBuilderDriver.php @@ -0,0 +1,22 @@ + bootstrap = $bootstrap; + } + + public function event_ready_sql(String $sql){ + events() -> module_call('ThinBuilder.ready_sql', ['sql' => $sql]); + } + + public function event_query(String $sql, $result){ + events() -> module_call('ThinBuilder.query', [ + 'sql' => $sql, + 'result' => $result + ]); + } +} \ No newline at end of file diff --git a/server/Fury/Kernel/AppContainer.php b/server/Fury/Kernel/AppContainer.php new file mode 100644 index 0000000..aa500fa --- /dev/null +++ b/server/Fury/Kernel/AppContainer.php @@ -0,0 +1,106 @@ + + * Date: 31.01.2020 + * @version 0.1 + */ + +class AppContainer{ + /** + * Array with flags + * + * @var Array + */ + protected static $already_set = [ + 'bootstrap' => false, + 'app' => false, + 'events' => false, + 'logging' => false + ]; + + /** + * Container with instances + * + * @var array + */ + protected static $container = []; + + /** + * Magic method for adding instance to container + * @example AppContainer::set_app($app_instance) or AppContainer::set_bootstrap($app_instance) or ... + * + * @method __callStatic + * + */ + public static function __callStatic(String $name, $args){ + try{ + if(strpos($name, 'set_') === false){ + throw new \Exception('Undefined method ' . $name); + } + + list(, $var_name) = explode('set_', $name); + if(!isset(self::$already_set[$var_name])){ + throw new \Exception('Undefined method ' . $name); + } + + if(!self::$already_set[$var_name]){ + self::$already_set[$var_name] = true; + self::$container[$var_name] = $args[0]; + return true; + }else{ + return false; + } + }catch(\Exception $e){ + echo $e -> getMessage(); + } + } + + /** + * Get application instance + * + * @method app + * + * @return mixed + */ + public static function app(){ + return self::$container['app']; + } + + /** + * Get bootstrap instance + * + * @method bootstrap + * + * @return mixed + */ + public static function bootstrap(){ + return self::$container['bootstrap']; + } + + /** + * Get Event instance + * + * @method events + * + * @return Events + */ + public static function events(){ + return self::$container['events']; + } + + /** + * Get Logging instance + * + * @method logging + * + * @return Logging + */ + public static function logging(){ + return self::$container['logging']; + } +} \ No newline at end of file diff --git a/server/Fury/Kernel/BaseApp.php b/server/Fury/Kernel/BaseApp.php new file mode 100644 index 0000000..6fd0dc4 --- /dev/null +++ b/server/Fury/Kernel/BaseApp.php @@ -0,0 +1,9 @@ + + * Date: 05.01.2020 + * Update At: 09.02.2020 + * @version 0.1 + */ + +class Bootstrap{ + /** + * Name of current project folder + * + * @var String + */ + public $project_folder; + + /** + * DB class instance + * + * @var DB + */ + public $db; + + /** + * CallControl instance + * + * @var CallControl + */ + protected $call_control; + + /** + * Init class instance + * + * @var Init + */ + protected $init; + + /** + * Constructor with params + * + * @method __construct + * + * @param String $project_folder Name of current project folder + */ + public function __construct(String $project_folder){ + $this -> project_folder = $project_folder; + AppContainer::set_bootstrap($this); + $this -> init = new Init($this); + $this -> init -> init(); + } + + /** + * Initialization global application config + * + * @method init_config + * + * @return void + */ + public function init_config(){ + // init project config + if(!file_exists("{$this -> project_folder}/config.php")){ + die("Config file not found!"); + } + define("FCONF", include_once("{$this -> project_folder}/config.php")); + } + + /** + * Initialization global application constants + * + * @method init_consts + * + * @return void + */ + public function init_consts(){ + define("APP_NAME", FCONF['app_name']); + define("PROJECT_FOLDER", $this -> project_folder); + } + + /** + * Initialization of default DB connection, but only if exists connect parameters to database + * + * @method init_db + * + * @return void + */ + public function init_db(){ + if(isset(FCONF['db']) and FCONF['default_db_wrap']){ + $this -> db = new DB(FCONF['db']); + } + } + + /** + * Initialization of application started files + * + * @method init_app_file + * + * @return void + */ + public function init_app_file(){ + if(isset(FCONF['app_file'])){ + $path_to_app_file = "{$this -> project_folder}/" . FCONF['app_file']; + if(file_exists($path_to_app_file)){ + include_once($path_to_app_file); + } + } + } + + /** + * Initialization of events system + * + * @method init_events + * + * @return void + */ + public function init_events(){ + $events = new Events(); + AppContainer::set_events($events); + } + + /** + * Initialization of call controller + * + * @method init_call_control + * + * @return void + */ + public function init_call_control(){ + $this -> call_control = CallControl::ins($this); + } + + /** + * Initialization of logging system + * + * @method init_logging + * + * @return void + */ + public function init_logging(){ + $logging = new Logging(); + AppContainer::set_logging($logging); + } + + /** + * Initialization of base model + * + * @method init_model + * + * @return void + */ + public function init_model(){ + Model::ins($this -> db); + } + + /** + * Adding call of event about application on ready + * + * @method ready_app_event + * + * @return void + */ + public function ready_app_event(){ + events() -> kernel_call('Bootstrap.ready_app', ['bootstrap' => $this]); + } + + /** + * Adding call of event about application started + * + * @method app_starting_event + * + * @return void + */ + public function app_starting_event(){ + events() -> kernel_call('Bootstrap.app_starting', ['bootstrap' => $this]); + } + + /** + * Adding call of event about application finished + * + * @method app_finished_event + * + * @return void + */ + public function app_finished_event(){ + events() -> kernel_call('Bootstrap.app_finished', ['bootstrap' => $this]); + } +} \ No newline at end of file diff --git a/server/Fury/Kernel/CallControl.php b/server/Fury/Kernel/CallControl.php new file mode 100644 index 0000000..208cd65 --- /dev/null +++ b/server/Fury/Kernel/CallControl.php @@ -0,0 +1,222 @@ + + * @version 0.1 + * Date: 08.01.2020 + * Update At: 31.01.2020 + */ + +class CallControl extends \Fury\Libs\Singleton{ + /** + * loca container for bootstrap instance + * + * @var Bootstrap + */ + protected $bootstrap; + + /** + * Flag about call + * + * @var boolean + */ + public $call_was_been = false; + + /** + * This is constructor. You know him + * + * @method __construct + * + * @param Bootstrap | NULL $bootstrap Bootstrap class instance + */ + public function __construct($bootstrap = NULL){ + if($bootstrap){ + $this -> bootstrap = $bootstrap; + } + + events() -> handler('kernel:Bootstrap.app_finished', function($params){ + $call_control = CallControl::ins(); + if(!$call_control -> call_was_been){ + events() -> kernel_call('CallControl.no_calls', []); + } + }); + } + + /** + * Call to controller class method. + * + * @method call_action + * + * @param String $getpost_flag 'GET_POST' | 'URI' + * @param String $src_route Raw route + * @param Mixed $action Action. We need call to him + * @param array $src_params Raw array with params + * + * @return void + */ + public function call_action(String $getpost_flag, String $src_route, $action, $src_params = []){ + $type = $getpost_flag ? 'GET_POST' : 'URI'; + + // make final params; + if($getpost_flag){ + if(strpos($src_route, '?')){ + list(, $src_route) = explode('?', $src_route); + } + $route = explode('&', $src_route); + $params = []; + foreach ($route as $i => $var) { + $params[$var] = $src_params[$var]; + } + }else{ + $params = $src_params; + } + + // call action with params + if(is_object($action)){ + $this -> action_result($this -> call_for_anon_func($type, $src_route, $action, $params)); + }elseif(strpos($action, '@') === false){ + $this -> action_result($this -> call_for_simple_func($type, $src_route, $action, $params)); + }else{ + $this -> action_result($this -> call_for_class_meth($type, $src_route, $action, $params)); + } + } + + /** + * Call to anonymize function + * + * @method call_for_anon_func + * + * @param String $type 'GET_POST' | 'URI' + * @param String $src_route Raw route string + * @param Function $action Anon func. We call to her + * @param Array $params Parameters for our anon func + * + * @return mixed [Result of working our action (anon func)] + */ + protected function call_for_anon_func($type, $src_route, $action, $params){ + $this -> gen_event_leading_call($type, $src_route, $action, $params); + $res = $action($params); // call + $this -> gen_event_completed_call($type, $src_route, $action, $params, $res); + return $res; + } + + /** + * Call to action if action is simple function + * + * @method call_for_simple_func + * + * @param String $type 'GET_POST' | 'URI' + * @param Mixed $src_template raw route template + * @param String $action Function name in string type + * @param Array $params Function arguments + * + * @return mixed [Result of working our action (anon func)] + */ + protected function call_for_simple_func(String $type, $src_template, String $action, $params){ + // call for simple func + $ref_func = new \ReflectionFunction($action); + $real_action_params = $ref_func -> getParameters(); + $final_action_params = []; + foreach ($real_action_params as $arg) { + if(isset($params[$arg -> name])){ + $final_action_params[$arg -> name] = $params[$arg -> name]; + } + } + + logging() -> set('CallControl@call_for_simple_func', 'Calling controller function', "$type, $src_template, $action, $final_action_params"); + $this -> gen_event_leading_call($type, $src_template, $action, $final_action_params); + $res = call_user_func_array($action, $final_action_params); + $this -> gen_event_completed_call($type, $src_template, $action, $final_action_params, $res); + return $res; + } + + /** + * Call to action if action is method of class + * + * @method call_for_class_meth + * + * @param String $type 'GET_POST' | 'URI' + * @param Mixed $src_template Raw route template + * @param String $action Class name and method name in string type. Like "Classname@methname" + * @param Array $params Method arguments + * + * @return mixed [Result of working our action (anon func)] + */ + protected function call_for_class_meth(String $type, $src_template, String $action, $params){ + list($action_class, $action_meth) = explode('@', $action); + $class_object = call_user_func_array([$action_class, 'ins'], [$this -> bootstrap]); + $ref_class = new \ReflectionClass($action_class); + $real_action_params = $ref_class -> getMethod($action_meth) -> getParameters(); + $final_action_params = []; + foreach ($real_action_params as $arg) { + if(isset($params[$arg -> name])){ + $final_action_params[$arg -> name] = $params[$arg -> name]; + } + } + + logging() -> set('CallControl@call_for_simple_func', 'Calling controller class and method', $type, $src_template, $action, $final_action_params); + $this -> gen_event_leading_call($type, $src_template, $action, $final_action_params); + $res = call_user_func_array([$class_object, $action_meth], $final_action_params); + $this -> gen_event_completed_call($type, $src_template, $action, $final_action_params, $res); + return $res; + } + + /** + * Generating event leading call. Will be runned before call action + * + * @method gen_event_leading_call + * + * @param String $type 'GET_POST' | 'URI' + * @param String $route_template Raw route template + * @param Mixed $action + * @param Array $params Arguments for action + * + * @return void + */ + protected function gen_event_leading_call(String $type, String $route_template, $action, $params){ + $this -> call_was_been = true; + events() -> kernel_call( + 'CallControl.leading_call', + compact('type', 'route_template', 'action', 'params') + ); + } + + /** + * Generating event completed call. Will be runned after call to action + * + * @method gen_event_completed_call + * + * @param String $type 'GET_POST' | 'URI' + * @param String $route_template Raw route template + * @param Mixed $action + * @param Array $params Arguments for action + * @param Mixed $result Result of worked action + * + * @return void + */ + protected function gen_event_completed_call(String $type, String $route_template, $action, $params, $result){ + $this -> call_was_been = true; + events() -> kernel_call( + 'CallControl.completed_call', + compact('type', 'route_template', 'action', 'params', 'result') + ); + } + + /** + * Result of action. Will be runned when action woking is finished. + * + * @method action_result + * + * @param Mixed $result Result of worked action + * + * @return void + */ + protected function action_result($result){ + echo $result; + + logging() -> dump(); + } +} \ No newline at end of file diff --git a/server/Fury/Kernel/Controller.php b/server/Fury/Kernel/Controller.php new file mode 100644 index 0000000..2562549 --- /dev/null +++ b/server/Fury/Kernel/Controller.php @@ -0,0 +1,17 @@ + events_ins = events(); + return $this -> create_connect($db_params); + } + + public function create_connect($db_params){ + $this -> db_params = $db_params; + $dblib = "{$db_params['dblib']}:host={$db_params['host']};dbname={$db_params['dbname']};charset={$db_params['charset']}"; + $this -> connect = new \PDO($dblib, $db_params['user'], $db_params['password']); + $this -> gen_event_create_connect($this -> connect, $this -> db_params); + return $this -> connect; + } + + private function gen_event_create_connect($connect, $db_params){ + $this -> events_ins -> kernel_call( + 'DB.create_connect', + compact('connect', 'db_params') + ); + } + + public function get_connect(){ + return $this -> connect; + } + + public function query($sql, $params = NULL){ + if(is_null($params)){ + return $this -> connect -> query($sql); + } + + return $this -> connect -> query($sql, $params); + } +} \ No newline at end of file diff --git a/server/Fury/Kernel/Events.php b/server/Fury/Kernel/Events.php new file mode 100644 index 0000000..f211ba1 --- /dev/null +++ b/server/Fury/Kernel/Events.php @@ -0,0 +1,168 @@ + map = []; + foreach($this -> event_types as $type){ + $this -> map[$type] = []; + } + } + + /** + * Get events map + * + * @method get_map + * + * @param boolean $full_map_flag Flag. If true - returned full map with app and kernel events, else only events map of app + * + * @return array + */ + public function get_map($full_map_flag = false){ + if($full_map_flag){ + return $this -> map; + } + + return $this -> map['app']; + } + + /** + * Get history of calling events handlers + * + * @method get_call_history + * + * @return array + */ + public function get_call_history(){ + return $this -> call_history; + } + + /** + * Event generation in the right places + * + * @method call + * + * @param string $type kernel or app + * @param string $event_name Uniq name of event + * @param array $params Array with parameters for event handlers + * + * @return boolean + */ + private function call($type, $event_name, $params){ + if(!isset($this -> map[$type][$event_name])){ + return false; + } + + foreach ($this -> map[$type][$event_name] as $i => $handler) { + $log_id = $this -> log_call_history($type, $event_name, $params); + $result = $handler($params); + $this -> add_result_to_log_call_history($log_id, $result); + } + + return true; + } + + /** + * logging call to history list + * + * @method log_call_history + * + * @param string $type Events type + * @param string $event_name Name of events + * @param array $params Parameters + * + * @return int Unique ID of handler call + */ + private function log_call_history($type, $event_name, $params){ + if(!FCONF['debug']){ + return false; + } + + $log_id = uniqid(); + $this -> call_history[$log_id] = [ + 'type' => $type, + 'event_name' => $event_name, + 'params' => $params + ]; + return $log_id; + } + + /** + * Add result of working event handler + * + * @method add_result_to_log_call_history + * + * @param int $log_id Unique log id + * @param any $result Result of working event handler + */ + private function add_result_to_log_call_history($log_id, $result){ + if(!FCONF['debug']){ + return false; + } + + if(isset($this -> call_history[$log_id])){ + $this -> call_history[$log_id]['result'] = $result; + return true; + } + return false; + } + + /** + * Add new handler for event + * + * @method handler + * + * @param string $event_name Name of events with type. Example "app:my_event" or "kernel:some_event" + * @param function $handler Anonymous function that will be the handler + * + * @return boolean result + */ + public function handler($event_name, $handler){ + list($type, $event_name) = explode(':', $event_name); + $type = trim(strtolower($type)); + $event_name = trim($event_name); + + if(!isset($this -> map[$type])){ + return false; + } + + if($event_name == ''){ + return false; + } + + if(isset($this -> map[$type][$event_name]) and is_array($this -> map[$type][$event_name])){ + $this -> map[$type][$event_name][] = $handler; + }else{ + $this -> map[$type][$event_name] = [$handler]; + } + + return true; + } + + public function __call($methname, $args){ + foreach ($this -> event_types as $i => $type) { + if(strpos($methname, $type . '_call') === false) + continue; + return $this -> call($type, $args[0], $args[1]); + } + return false; + } + +} \ No newline at end of file diff --git a/server/Fury/Kernel/Init.php b/server/Fury/Kernel/Init.php new file mode 100644 index 0000000..44c0b43 --- /dev/null +++ b/server/Fury/Kernel/Init.php @@ -0,0 +1,57 @@ + + * @version 0.1 + * Date: 09.02.2020 + */ + +class Init{ + /** + * Bootstrap class instance + * + * @var Bootstrap + */ + protected $bootstrap; + + /** + * Constructor with params + * + * @method __construct + * + * @param Bootstrap $bootstrap Bootstrap instance + */ + public function __construct(Bootstrap $bootstrap){ + $this -> bootstrap = $bootstrap; + } + + /** + * Method for framework and application initialization + * If you need changed order of initialization - ok, but be careful :) + * + * @method init + * + * @return void + */ + public function init(){ + $this -> bootstrap -> init_config(); + $this -> bootstrap -> init_consts(); + $this -> bootstrap -> init_logging(); + $this -> bootstrap -> init_events(); + + $this -> bootstrap -> app_starting_event(); + + $this -> bootstrap -> init_call_control(); + $this -> bootstrap -> init_app_file(); + + $this -> bootstrap -> init_db(); + $this -> bootstrap -> init_model(); + + $this -> bootstrap -> ready_app_event(); + + $this -> bootstrap -> app_finished_event(); + } +} \ No newline at end of file diff --git a/server/Fury/Kernel/Logging.php b/server/Fury/Kernel/Logging.php new file mode 100644 index 0000000..3e2a60d --- /dev/null +++ b/server/Fury/Kernel/Logging.php @@ -0,0 +1,98 @@ + storage = []; + $this -> session_id = uniqid(); + $this -> logs_folder = FCONF['logs_folder']; + } + + /** + * Set new log item + * + * @method set + * + * @param string $place String in format "Classname@methname" or "funcname" + * @param string $title Title of log. + * @param string $message Any text message + */ + public function set($place, $title, $message){ + if(!FCONF['logs_enable']) + return false; + + if(strpos($place, '@') === false){ + $class = ''; + $meth = $place; + }else{ + list($class, $meth) = explode('@', $place); + } + + $this -> storage[] = [ + 'class' => $class, + 'meth' => $meth, + 'title' => $title, + 'message' => $message, + 'timestamp' => microtime(true) + ]; + + return true; + } + + + /** + * Dumping session logs to json file + * + * @method dump + * + * @return boolean Result of writing to log file + */ + public function dump(){ + $log_filename = date('d.m.Y') . '.log.json'; + $path_to_log_file = $this -> logs_folder . '/' . $log_filename; + $session = [ + 'session_id' => $this -> session_id, + 'timestamp' => microtime(true), + 'logs' => $this -> storage + ]; + + if(!file_exists($path_to_log_file)){ + file_put_contents($path_to_log_file, ''); + chmod($path_to_log_file, 0755); + return file_put_contents($path_to_log_file, json_encode([$session], JSON_UNESCAPED_SLASHES|JSON_PRETTY_PRINT)); + } + + $logs = json_decode(file_get_contents($path_to_log_file), true); + $logs[] = $session; + return file_put_contents($path_to_log_file, json_encode($logs, JSON_UNESCAPED_SLASHES|JSON_PRETTY_PRINT)); + } +} \ No newline at end of file diff --git a/server/Fury/Kernel/Model.php b/server/Fury/Kernel/Model.php new file mode 100644 index 0000000..2d944bd --- /dev/null +++ b/server/Fury/Kernel/Model.php @@ -0,0 +1,17 @@ + lang = $lang; + $this -> vocabulary = ["en" => [ + "Aenean facilisis venenatis ipsum vel aliquet.", + "Aenean rhoncus malesuada auctor.", + "Cras sit amet magna sit amet felis eleifend lacinia.", + "Donec nec velit eget tellus adipiscing iaculis eu non lorem.", + "Donec ac tellus eu tellus dignissim blandit.", + "Donec diam dui, mollis venenatis lacinia ac, pretium id augue.", + "Donec lectus dolor, cursus eget facilisis id, ultrices ac mauris.", + "Duis sem quam, euismod ac rhoncus id, adipiscing sed sem.", + "Etiam sagittis tellus sapien.", + "In accumsan bibendum magna a egestas.", + "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", + "Maecenas tristique magna nulla, in fringilla purus.", + "Nulla eleifend velit faucibus eros rhoncus molestie.", + "Nulla pellentesque dolor at metus molestie ultrices nec vitae metus.", + "Pellentesque vel felis purus, ut dignissim erat.", + "Pellentesque quis purus nec odio aliquet bibendum ut vitae risus.", + "Phasellus sed felis vitae purus dictum fermentum.", + "Praesent ac tellus dui, in euismod leo.", + "Proin vulputate tincidunt erat id auctor.", + "Sed non justo enim.", + "Vestibulum imperdiet nunc id metus pellentesque eleifend.", + "Vivamus ultricies iaculis arcu, vitae bibendum tellus feugiat venenatis.", + "Sed ultricies mi vel aliquet eleifend.", + "Ut egestas est id ligula gravida, quis lobortis lacus consectetur.", + "Cras ac urna fringilla, bibendum leo sit amet, suscipit nisi.", + "Sed gravida erat sit amet neque rutrum mattis.", + "Integer at ex nec nunc euismod scelerisque.", + "Nullam faucibus dui imperdiet rutrum rhoncus.", + "Proin scelerisque sapien et augue sodales auctor.", + "In posuere erat ut posuere mollis.", + "Morbi lobortis ipsum eu ipsum eleifend, eu tincidunt arcu sagittis." + ], + "ru" => [ + "Интеллект естественно понимает под собой интеллигибельный закон внешнего мира, открывая новые горизонты.", + "Гедонизм осмысляет дедуктивный метод. Надстройка нетривиальна.", + "Дискретность амбивалентно транспонирует гравитационный парадокс.", + "Смысл жизни, следовательно, творит данный закон внешнего мира.", + "Дедуктивный метод решительно представляет собой бабувизм.", + "Имеется спорная точка зрения, гласящая примерно следующее: элементы политического процесса, превозмогая сложившуюся непростую экономическую ситуацию, превращены в посмешище, хотя само их существование приносит несомненную пользу обществу.", + "Но явные признаки победы институционализации в равной степени предоставлены сами себе.", + "В своём стремлении повысить качество жизни, они забывают, что существующая теория предоставляет широкие возможности для экономической целесообразности принимаемых решений.", + "Не следует, однако, забывать, что глубокий уровень погружения не оставляет шанса для экспериментов, поражающих по своей масштабности и грандиозности.", + "Сложно сказать, почему некоторые особенности внутренней политики и по сей день остаются уделом либералов, которые жаждут быть ограничены исключительно образом мышления!", + "В целом, конечно, социально-экономическое развитие выявляет срочную потребность первоочередных требований.", + "Каждый из нас понимает очевидную вещь: социально-экономическое развитие позволяет выполнить важные задания по разработке инновационных методов управления процессами.", + "Предварительные выводы неутешительны: новая модель организационной деятельности, а также свежий взгляд на привычные вещи - безусловно открывает новые горизонты для позиций, занимаемых участниками в отношении поставленных задач!", + "А также элементы политического процесса преданы социально-демократической анафеме.", + "В частности, убеждённость некоторых оппонентов предполагает независимые способы реализации глубокомысленных рассуждений.", + "Задача организации, в особенности же консультация с широким активом позволяет выполнить важные задания по разработке системы массового участия.", + "Таким образом, граница обучения кадров способствует подготовке и реализации переосмысления внешнеэкономических политик.", + "Есть над чем задуматься: независимые государства формируют глобальную экономическую сеть и при этом - преданы социально-демократической анафеме.", + "Предприниматели в сети интернет освещают чрезвычайно интересные особенности картины в целом, однако конкретные выводы, разумеется, преданы социально-демократической анафеме.", + "Принимая во внимание показатели успешности, консультация с широким активом представляет собой интересный эксперимент проверки первоочередных требований.", + "Как уже неоднократно упомянуто, стремящиеся вытеснить традиционное производство, нанотехнологии, инициированные исключительно синтетически, объединены в целые кластеры себе подобных.", + "Учитывая ключевые сценарии поведения, высококачественный прототип будущего проекта напрямую зависит от экспериментов, поражающих по своей масштабности и грандиозности.", + "Идейные соображения высшего порядка, а также граница обучения кадров предопределяет высокую востребованность дальнейших направлений развития.", + "В частности, постоянный количественный рост и сфера нашей активности, в своём классическом представлении, допускает внедрение приоритизации разума над эмоциями.", + "Для современного мира курс на социально-ориентированный национальный проект обеспечивает актуальность глубокомысленных рассуждений.", + "И нет сомнений, что некоторые особенности внутренней политики объявлены нарушающими общечеловеческие нормы этики и морали.", + "С другой стороны, перспективное планирование представляет собой интересный эксперимент проверки анализа существующих паттернов поведения.", + "Мы вынуждены отталкиваться от того, что реализация намеченных плановых заданий однозначно определяет каждого участника как способного принимать собственные решения касаемо прогресса профессионального сообщества.", + "Значимость этих проблем настолько очевидна, что убеждённость некоторых оппонентов представляет собой интересный эксперимент проверки экономической целесообразности принимаемых решений.", + "Учитывая ключевые сценарии поведения, экономическая повестка сегодняшнего дня требует анализа системы массового участия." + ] + ]; + + $this -> count_sentence = count($this -> vocabulary[$this -> lang]); + + $this -> names = ["male" => [], "female" => []]; + $this -> names["male"] = [ + "Liam", "Noah", "Mason", "Ethan", "Logan", + "Lucas", "Jackson", "Aiden", "Oliver", "Jacob", + "Elijah", "Alexander", "James", "Benjamin", "Jack", + "Luke", "William", "Michael", "Owen", "Daniel", + "Carter", "Gabriel", "Henry", "Matthew", "Wyatt", + "Caleb", "Jayden", "Nathan", "Ryan", "Isaac", + "Liam","Noah","Oliver","Elijah","James","William","Benjamin","Lucas","Henry","Theodore","Jack","Levi", + "Alexander","Jackson","Mateo","Daniel","Michael","Mason","Sebastian","Ethan","Logan","Owen","Samuel", + "Jacob","Asher","Aiden","John","Joseph","Wyatt","David","Leo","Luke","Julian","Hudson","Grayson","Matthew", + "Ezra","Gabriel","Carter","Isaac","Jayden","Luca","Anthony","Dylan","Lincoln","Thomas","Maverick", + "Elias","Josiah","Charles","Caleb","Christopher","Ezekiel","Miles","Jaxon","Isaiah","Andrew", + "Joshua","Nathan","Nolan","Adrian","Cameron","Santiago","Eli","Aaron","Ryan","Angel","Cooper", + "Waylon","Easton","Kai","Christian","Landon","Colton","Roman","Axel","Brooks","Jonathan","Robert", + "Jameson","Ian","Everett","Greyson","Wesley","Jeremiah","Hunter","Leonardo","Jordan","Jose","Bennett", + "Silas","Nicholas","Parker","Beau","Weston","Austin","Connor","Carson","Dominic","Xavier","Jaxson", + "Jace","Emmett","Adam","Declan","Rowan","Micah","Kayden","Gael","River","Ryder","Kingston","Damian", + "Sawyer","Luka","Evan","Vincent","Legend","Myles","Harrison","August","Bryson","Amir","Giovanni", + "Chase","Diego","Milo","Jasper","Walker","Jason","Brayden","Cole","Nathaniel","George","Lorenzo", + "Zion","Luis","Archer","Enzo","Jonah","Thiago","Theo","Ayden","Zachary","Calvin","Braxton","Ashton", + "Rhett","Atlas","Jude","Bentley","Carlos","Ryker","Adriel","Arthur","Ace","Tyler","Jayce","Max", + ]; + $this -> names["female"] = [ + "Emma", "Olivia", "Ava", "Sophia", "Isabella", + "Mia", "Charlotte", "Amelia", "Emily", "Madison", + "Harper", "Abigail", "Avery", "Lily", "Ella", + "Chloe", "Evelyn", "Sofia", "Aria", "Ellie", + "Aubrey", "Scarlett", "Zoey", "Hannah", "Audrey", + "Grace", "Addison", "Zoe", "Elizabeth", "Nora" + ]; + + $this -> surnames = [ + "Smith", "Johnson", "Williams", "Jones", "Brown", + "Davis", "Miller", "Wilson", "Moore", "Taylor", + "Anderson", "Thomas", "Jackson", "White", "Harris", + "Martin", "Thompson", "Garcia", "Martinez", "Robinson", + "Clark", "Rodriguez", "Lewis", "Lee", "Walker", + "Hall", "Allen", "Young", "Hernandez", "King", + "Wright", "Lopez", "Hill", "Scott", "Green", + "Adams", "Baker", "Gonzalez", "Nelson", "Carter", + "Mitchell", "Perez", "Roberts", "Turner", "Phillips", + "Campbell", "Parker", "Evans", "Edwards", "Collins", + "Stewart", "Sanchez", "Morris", "Rogers", "Reed", + "Cook", "Morgan", "Bell", "Murphy", "Bailey", + "Rivera", "Cooper", "Richardson", "Cox", "Howard", + "Ward", "Torres", "Peterson", "Gray", "Ramirez", + "James", "Watson", "Brooks", "Kelly", "Sanders", + "Price", "Bennett", "Wood", "Barnes", "Ross", + "Henderson", "Coleman", "Jenkins", "Perry", "Powell", + "Long", "Patterson", "Flores", "Washington", "Hughes", + "Butler", "Simmons", "Gonzales", "Foster", "Bryant", + "Alexander", "Russell", "Griffin", "Diaz", "Hayes", + "Elliot","Graham","Kaiden","Maxwell","Juan","Dean","Matteo","Malachi","Ivan","Elliott","Jesus", + "Emiliano","Messiah","Gavin","Maddox","Camden","Hayden","Leon","Antonio","Justin","Tucker","Brandon", + "Kevin","Judah","Finn","King","Brody","Xander","Nicolas","Charlie","Arlo","Emmanuel","Barrett", + "Felix","Alex","Miguel","Abel","Alan","Beckett","Amari","Karter","Timothy","Abraham","Jesse", + "Zayden","Blake","Alejandro","Dawson","Tristan","Victor","Avery","Joel","Grant","Eric","Patrick", + "Peter","Richard","Edward","Andres","Emilio","Colt","Knox","Beckham","Adonis","Kyrie","Matias", + "Oscar","Lukas","Marcus","Hayes","Caden","Remington","Griffin","Nash","Israel","Steven","Holden", + "Rafael","Zane","Jeremy","Kash","Preston","Kyler","Jax","Jett","Kaleb","Riley","Simon","Phoenix", + "Javier","Bryce","Louis","Mark","Cash","Lennox","Paxton","Malakai","Paul","Kenneth","Nico","Kaden", + "Lane","Kairo","Maximus","Omar","Finley","Atticus","Crew","Brantley","Colin","Dallas","Walter", + "Brady","Callum","Ronan","Hendrix","Jorge","Tobias","Clayton","Emerson","Damien","Zayn","Malcolm", + "Kayson","Bodhi","Bryan","Aidan","Cohen","Brian","Cayden","Andre","Niko","Maximiliano","Zander", + "Khalil","Rory","Francisco","Cruz","Kobe","Reid","Daxton","Derek","Martin","Jensen","Karson","Tate", + "Muhammad","Jaden","Joaquin","Josue","Gideon","Dante","Cody","Bradley","Orion","Spencer","Angelo", + "Erick","Jaylen","Julius","Manuel","Ellis","Colson","Cairo","Gunner","Wade","Chance","Odin","Anderson", + "Kane","Raymond","Cristian","Aziel","Prince","Ezequiel","Jake","Otto","Eduardo","Rylan","Ali","Cade", + "Stephen","Ari","Kameron","Dakota","Warren","Ricardo","Killian","Mario","Romeo","Cyrus","Ismael", + "Russell","Tyson","Edwin","Desmond","Nasir","Remy","Tanner","Fernando","Hector","Titus","Lawson", + "Sean","Kyle","Elian","Corbin","Bowen","Wilder","Armani","Royal","Stetson","Briggs","Sullivan", + "Leonel","Callan","Finnegan","Jay","Zayne","Marshall","Kade","Travis","Sterling","Raiden","Sergio", + "Tatum","Cesar","Zyaire","Milan","Devin","Gianni","Kamari","Royce","Malik","Jared","Franklin", + "Clark","Noel","Marco","Archie","Apollo","Pablo","Garrett","Oakley","Memphis","Quinn","Onyx", + "Alijah","Baylor","Edgar","Nehemiah","Winston","Major","Rhys","Forrest","Jaiden","Reed","Santino", + "Troy","Caiden","Harvey","Collin","Solomon","Donovan","Damon","Jeffrey","Kason","Sage","Grady", + "Kendrick","Leland","Luciano","Pedro","Hank","Hugo","Esteban","Johnny","Kashton","Ronin", + "Ford","Mathias","Porter","Erik","Johnathan","Frank","Tripp","Casey","Fabian","Leonidas","Baker", + "Matthias","Philip","Jayceon","Kian","Saint","Ibrahim","Jaxton","Augustus","Callen","Trevor","Ruben", + "Adan","Conor","Dax","Braylen","Kaison","Francis","Kyson","Andy","Lucca","Mack","Peyton","Alexis", + "Deacon","Kasen","Kamden","Frederick","Princeton","Braylon","Wells","Nikolai","Iker","Bo","Dominick", + "Moshe","Cassius","Gregory","Lewis","Kieran","Isaias","Seth","Marcos","Omari","Shane","Keegan","Jase", + "Asa","Sonny","Uriel","Pierce","Jasiah","Eden","Rocco","Banks","Cannon","Denver","Zaiden","Roberto", + "Shawn","Drew","Emanuel","Kolton","Ayaan","Ares","Conner","Jalen","Alonzo","Enrique","Dalton","Moses", + "Koda","Bodie","Jamison","Phillip","Zaire","Jonas","Kylo","Moises","Shepherd","Allen","Kenzo", + "Mohamed","Keanu","Dexter","Conrad","Bruce","Sylas","Soren","Raphael","Rowen","Gunnar","Sutton", + "Quentin","Jaziel","Emmitt","Makai","Koa","Maximilian","Brixton","Dariel","Zachariah","Roy","Armando", + "Corey","Saul","Izaiah","Danny","Davis","Ridge","Yusuf","Ariel","Valentino","Jayson","Ronald", + "Albert","Gerardo","Ryland","Dorian","Drake","Gage","Rodrigo","Hezekiah","Kylan","Boone","Ledger", + "Santana","Jamari","Jamir","Lawrence","Reece","Kaysen","Shiloh","Arjun","Marcelo","Abram","Benson", + "Huxley","Nikolas","Zain","Kohen","Samson","Miller","Donald","Finnley","Kannon","Lucian","Watson","Keith", + "Westin","Tadeo","Sincere","Boston","Axton","Amos","Chandler","Leandro","Raul","Scott","Reign", + "Alessandro","Camilo","Derrick","Morgan","Julio","Clay","Edison","Jaime","Augustine","Julien","Zeke", + "Marvin","Bellamy","Landen","Dustin","Jamie","Krew","Kyree","Colter","Johan","Houston","Layton", + "Quincy","Case","Atreus","Cayson","Aarav","Darius","Harlan","Justice","Abdiel","Layne","Raylan", + "Arturo","Taylor","Anakin","Ander","Hamza","Otis","Azariah","Leonard","Colby","Duke","Flynn","Trey", + "Gustavo","Fletcher","Issac","Sam","Trenton","Callahan","Chris","Mohammad","Rayan","Lionel","Bruno", + "Jaxxon","Zaid","Brycen","Roland","Dillon","Lennon","Ambrose","Rio","Mac","Ahmed","Samir","Yosef", + "Tru","Creed","Tony","Alden","Aden","Alec","Carmelo","Dario","Marcel","Roger","Ty","Ahmad","Emir", + "Landyn","Skyler","Mohammed","Dennis","Kareem","Nixon","Rex","Uriah","Lee","Louie","Rayden","Reese", + "Alberto","Cason","Quinton","Kingsley","Chaim","Alfredo","Mauricio","Caspian","Legacy","Ocean","Ozzy", + "Briar","Wilson","Forest","Grey","Joziah","Salem","Neil","Remi","Bridger","Harry","Jefferson", + "Lachlan","Nelson","Casen","Salvador","Magnus","Tommy","Marcellus","Maximo","Jerry","Clyde","Aron", + "Keaton","Eliam","Lian","Trace","Douglas","Junior","Titan","Cullen","Cillian","Musa","Mylo","Hugh", + "Tomas","Vincenzo","Westley","Langston","Byron","Kiaan","Loyal","Orlando","Kyro","Amias","Amiri", + "Jimmy","Vicente","Khari","Brendan","Rey","Ben","Emery","Zyair","Bjorn","Evander","Ramon","Alvin", + "Ricky","Jagger","Brock","Dakari","Eddie","Blaze","Gatlin","Alonso","Curtis","Kylian","Nathanael", + "Devon","Wayne","Zakai","Mathew","Rome","Riggs","Aryan","Avi","Hassan","Lochlan","Stanley","Dash", + "Kaiser","Benicio","Bryant","Talon","Rohan","Wesson","Joe","Noe","Melvin","Vihaan","Zayd","Darren", + "Enoch","Mitchell","Jedidiah","Brodie","Castiel","Ira","Lance","Guillermo","Thatcher","Ermias","Misael", + "Jakari","Emory","Mccoy","Rudy","Thaddeus","Valentin","Yehuda","Bode","Madden","Kase","Bear","Boden", + "Jiraiya","Maurice","Alvaro","Ameer","Demetrius","Eliseo","Kabir","Kellan","Allan","Azrael","Calum", + "Niklaus","Ray","Damari","Elio","Jon","Leighton","Axl","Dane","Eithan","Eugene","Kenji","Jakob", + "Colten","Eliel","Nova","Santos","Zahir","Idris","Ishaan","Kole","Korbin","Seven","Alaric","Kellen", + "Bronson","Franco","Wes","Larry","Mekhi","Jamal","Dilan","Elisha","Brennan","Kace","Van","Felipe","Fisher", + "Cal","Dior","Judson","Alfonso","Deandre","Rocky","Henrik","Reuben","Anders","Arian","Damir","Jacoby", + "Khalid","Kye","Mustafa","Jadiel","Stefan","Yousef","Aydin","Jericho","Robin","Wallace","Alistair","Davion", + "Alfred","Ernesto","Kyng","Everest","Gary","Leroy","Yahir","Braden","Kelvin","Kristian","Adler","Avyaan", + "Brayan","Jones","Truett","Aries","Joey","Randy","Jaxx","Jesiah","Jovanni","Azriel","Brecken","Harley", + "Zechariah","Gordon","Jakai","Carl","Graysen","Kylen","Ayan","Branson","Crosby","Dominik","Jabari", + "Jaxtyn","Kristopher","Ulises","Zyon","Fox","Howard","Salvatore","Turner","Vance","Harlem","Jair","Jakobe", + "Jeremias","Osiris","Azael","Bowie","Canaan","Elon","Granger","Karsyn","Zavier","Cain","Dangelo","Heath", + "Yisroel","Gian","Shepard","Harold","Kamdyn","Rene","Rodney","Yaakov","Adrien","Kartier","Cassian", + "Coleson","Ahmir","Darian","Genesis","Kalel","Agustin","Wylder","Yadiel","Ephraim","Kody","Neo","Ignacio", + "Osman","Aldo","Abdullah","Cory","Blaine","Dimitri","Khai","Landry","Palmer","Benedict","Leif","Koen", + "Maxton","Mordechai","Zev","Atharv","Bishop","Blaise","Davian" + ]; + + $this -> phone_numbers = ["country_code" => ["+1", "+3", "+7", "+9", "+2", "+4"], "region_code" => [ + 209, 213, 310, 323, 408, 415, 424, 510, 530, 559, 562, 619, 626, 650, 661, 707, 714, 760, 805, 818, 831, 858, 909, 925, 949, + 239, 305, 321, 352, 386, 407, 561, 727, 754, 772, 786, 813, 850, 863, 904, 941, 954, + 217, 224, 309, 312, 331, 464, 618, 630, 708, 773, 779, 815, 847, 872 + ]]; + $this -> phone_numbers["count_country_code"] = count($this -> phone_numbers["country_code"]); + $this -> phone_numbers["count_region_code"] = count($this -> phone_numbers["region_code"]); + $this -> email_domen_list = [ + "gmail", + "yahoo", + "hotmail", + "outlook", + "mail" + ]; + } + + /** + * wrap for $this -> gen() + * + * @return Array + */ + public function gen_list(Int $count_items){ + return $this -> gen($count_items); + } + + /** + * Generate preset words + * + * @return String + */ + public function gen_words(Int $count_words){ + $max_count = 30; + if($count_words > $max_count) $count_words = $max_count; + list($paragraph) = $this -> gen_paragraphs(1, 10, 20); + $paragraph = explode(" ", $paragraph); + $words = []; + for($i = 0; $i < $count_words; $i++){ + $words[] = $paragraph[$i]; + } + $words = implode(" ", $words); + $words = trim($words, ","); + return $words; + } + + /** + * Generate preset count paragraphs + * + * @return Array + */ + public function gen_paragraphs(Int $count_p, Int $min_len = 1, Int $max_len = 30){ + $p = []; + for($i=0; $i<$count_p; $i++){ + $paragraphy_len = rand($min_len, $max_len); + $p[] = implode(" ", $this -> gen($paragraphy_len)); + } + + return $p; + } + + /** + * Generate one paragraph + * + * @return String + */ + public function gen_paragraph(Int $count_sentence = 0){ + if(!$count_sentence) $count_sentence = rand(1, 30); + $p = implode(" ", $this -> gen($count_sentence)); + return $p; + } + + /** + * gen Generated base list width random sentence + * + * @return Array + */ + private function gen(Int $count){ + $prevnum = -1; + $li = []; + for($i=0; $i<$count; $i++){ + $currentnum = rand(0, $this -> count_sentence - 1); + if($currentnum == $prevnum){ + $currentnum = rand(0, $this -> count_sentence - 1); + } + $li[] = $this -> vocabulary[$this -> lang][$currentnum]; + $prevnum = $currentnum; + } + + return $li; + } + + // ---- NAMES ---- // + + /** + * Generate and return random eng name + * + * @return String + */ + public function get_name(String $sex = ""){ + if($sex == ""){ + $sex = rand(0, 1) ? "male" : "female"; + } + $count_names = count($this -> names[$sex]); + return $this -> names[$sex][rand(0, $count_names - 1)]; + } + + /** + * Wrapper for get_name func + * + * @return String [female name] + */ + public function get_female_name(){ + return $this -> get_name("female"); + } + + /** + * Wwrapper for get_name func + * + * @return String [male name] + */ + public function get_male_name(){ + return $this -> get_name("male"); + } + + /** + * @return String [return surname] + */ + public function get_surname(){ + $count_surnames = count($this -> surnames); + return $this -> surnames[rand(0, $count_surnames - 1)]; + } + + /** + * Return name and surname with separator space + * + * @return String [return name and surname with separator space] + */ + public function get_full_name_to_str(){ + return $this -> get_name() . " " . $this -> get_surname(); + } + + /** + * Return name and surname in array + * + * @return Array [return name and surname in array] + */ + public function get_full_name_to_arr(){ + return ["name" => $this -> get_name(), "surname" => $this -> get_surname()]; + } + + // ---- PHONE NUMBERS ---- // + + /** + * @return String [formated phone number] + */ + public function get_phone_number(){ + $count_country_code = $this -> phone_numbers["count_country_code"] - 1; + $count_region_code = $this -> phone_numbers["count_region_code"] - 1; + $country_code = $this -> phone_numbers["country_code"][ rand(0, $count_country_code) ]; + $region_code = $this -> phone_numbers["region_code"][ rand(0, $count_region_code) ]; + $phone_number = $country_code . " " . $region_code . "-" . rand(100, 999) . "-" . rand(1000, 9999); + return $phone_number; + } + + // ---- EMAIL ---- // + + /** + * Email address + * + * @return String [email] + */ + public function get_email(String $name = "", String $surname = ""){ + $count = count($this -> email_domen_list) - 1; + $name = $name != "" ? $name : $this -> get_name(); + $surname = $surname != "" ? $surname : $this -> get_surname(); + $sep_arr = [".", "_", ""]; + $sep = $sep_arr[ rand(0, count($sep_arr) - 1) ]; + $host = $this -> email_domen_list[ rand(0, $count) ]; + $email = strtolower($name) . $sep . strtolower($surname) . "@" . $host . ".com"; + return $email; + } + + // ---- USER ---- // + + /** + * data about one random user + * + * @return Array [user card in assoc array] + */ + public function get_user_card(String $sex = ""){ + if($sex == ""){ + $sex = rand(0, 1) ? "male" : "female"; + } + $name = $this -> get_name($sex); + $surname = $this -> get_surname(); + $user = [ + "name" => $name, + "surname" => $surname, + "sex" => $sex, + "phone" => $this -> get_phone_number(), + "email" => $this -> get_email($name, $surname) + ]; + + return $user; + } +} \ No newline at end of file diff --git a/server/Fury/Libs/Singleton.php b/server/Fury/Libs/Singleton.php new file mode 100644 index 0000000..01c7331 --- /dev/null +++ b/server/Fury/Libs/Singleton.php @@ -0,0 +1,19 @@ + + * @version 0.1 + * Date: 19.07.2022 + */ + +class ErrorHandler{ + /** + * With all errs in source view + */ + private Array $errs_src; + + /** + * What errors need to be displayed and logined + */ + private Array $important_errors; + + public function __construct(){ + $this -> important_errors = FCONF["error_handler"]["important_errors"]; + + if(!FCONF["debug"]){ + error_reporting(-1); + }else{ + error_reporting(0); + } + + $this -> set_err_handler(); + } + + /** + * Set custom error handler + */ + public function set_err_handler(){ + set_error_handler([$this, "error_handler"], E_ALL); + register_shutdown_function([$this, "fatal_error_handler"]); + // set_exception_handler([$this, "exception_handler"]); + } + + // FIXME + public function exception_handler(\Exception $e) { + $this -> error_handler( + $e -> getCode(), + $e -> getMessage(), + $e -> getFile(), + $e -> getLine() + ); + } + + /** + * Set custom FATAL error handler + */ + public function fatal_error_handler(){ + $error = error_get_last(); + if ($error){ + $this -> view_fatal_error($error["type"], $error["message"], $error["file"], $error["line"]); + } + } + + /** + * Handle of error + */ + public function error_handler(Int $errno, String $errstr, String $errfile, Int $errline){ + $err_type = $this -> get_err_type($errno); + if(!$this -> error_is_important($err_type)){ + return true; + } + + $this -> view_fatal_error($errno, $errstr, $errfile, $errline); + return true; + } + + /** + * Get type of error + */ + private function get_err_type(Int $errno){ + $errors = array( + E_ERROR => "E_ERROR", + E_WARNING => "E_WARNING", + E_PARSE => "E_PARSE", + E_NOTICE => "E_NOTICE", + E_CORE_ERROR => "E_CORE_ERROR", + E_CORE_WARNING => "E_CORE_WARNING", + E_COMPILE_ERROR => "E_COMPILE_ERROR", + E_COMPILE_WARNING => "E_COMPILE_WARNING", + E_USER_ERROR => "E_USER_ERROR", + E_USER_WARNING => "E_USER_WARNING", + E_USER_NOTICE => "E_USER_NOTICE", + E_STRICT => "E_STRICT", + E_RECOVERABLE_ERROR => "E_RECOVERABLE_ERROR", + E_DEPRECATED => "E_DEPRECATED", + E_USER_DEPRECATED => "E_USER_DEPRECATED", + ); + + return isset($errors[$errno]) ? $errors[$errno] : "EXCEPTION"; + } + + /** + * Get lines with errors from file + */ + public function get_prog_code(String $errfile, Int $errline) { + $file = explode("\n", file_get_contents($errfile)); + $code = []; + for($i = $errline - 8; $i < $errline + 6; $i++){ + if(!isset($file[$i])) continue; + if(trim($file[$i]) == "") continue; + $code[$i + 1] = str_replace("\t", "  ", htmlspecialchars($file[$i])); + } + return $code; + } + + protected function error_is_important(String $errtype){ + foreach($this -> important_errors as $important_error){ + if($errtype == $important_error){ + return true; + } + } + return false; + } + + /** + * show styles and html code for fatal error + */ + public function view_fatal_error(Int $errno, String $errstr, String $errfile, Int $errline){ + http_response_code(500); + if(!FCONF["debug"]) return false; + $err_type = $this -> get_err_type($errno); + $code = $this -> get_prog_code($errfile, $errline); + $errstr = str_replace(["\\", "\n", "\r", "\t", "\""], ["\", "", "", "", "'"], str_replace("`", "'", $errstr)); + $code = str_replace(["\\", "\n"], ["\", ""], str_replace("`", "'", $code)); + $this -> show_err_page(compact("errno", "err_type", "errstr", "errfile", "errline", "code")); + } + + protected function show_err_page(Array $data) { + $json_data = json_encode($data); + echo ""; + echo ''; + echo "
{$data["err_type"]}: {$data["errstr"]}
{$data["errfile"]} In line {$data["errline"]}
"; + echo ""; + echo ''; + echo "
"; + die(); + } +} \ No newline at end of file diff --git a/server/Fury/Modules/Router/Router.php b/server/Fury/Modules/Router/Router.php new file mode 100644 index 0000000..6e0d055 --- /dev/null +++ b/server/Fury/Modules/Router/Router.php @@ -0,0 +1,108 @@ + call_control_instance = CallControl::ins(); + + if(is_array($routes_map)){ + $this -> routes_map = $routes_map; + } + + $this -> uri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : ""; + if(strpos($this -> uri, '?') !== false){ + list($this -> uri) = explode('?', $this -> uri); + } + + $uri_length = strlen($this -> uri); + if($uri_length > 1 and $this -> uri[$uri_length - 1] == '/'){ + $this -> uri = mb_substr($this -> uri, 0, -1); + } + } + + public function get($route, $action, $static_uri = ''){ + if(is_array($route)){ + $route = implode('&', $route); + $route = strlen($static_uri) ? $static_uri . '?' . $route : $route; + } + $this -> add_route('get', $route, $action); + } + + public function post($route, $action, $static_uri = ''){ + if(is_array($route)){ + $route = implode('&', $route); + $route = strlen($static_uri) ? $static_uri . '?' . $route : $route; + } + $this -> add_route('post', $route, $action); + } + + public function uri($route, $action){ + $this -> add_route('uri', $route, $action); + } + + public function get_routes_map(){ + return $this -> routes_map; + } + + public function start_routing(){ + $result = []; + + $result['get'] = $this -> GET_and_POST_routing($this -> routes_map['get'], $_GET); + $result['post'] = $this -> GET_and_POST_routing($this -> routes_map['post'], $_POST); + $result['uri'] = $this -> URI_routing($this -> routes_map['uri']); + } + + public function route_to(String $action){ + $routes_list = []; + + $routes_map = array_merge($this -> routes_map['uri'], $this -> routes_map['get'], $this -> routes_map['post']); + + $route = array_search($action, $routes_map); + + return $route === false ? '' : $route; + } + + public function urlto(String $action, Array $params = []){ + $route_template = $this -> route_to($action); + if(strpos($route_template, '?') === false){ + $route_params = $this -> get_params_from_route_template($route_template); + $url = str_replace($route_params, $params, $route_template); + }else{ + $route_params = []; + + foreach($params as $var => $val){ + $route_params[] = $var; + $params[$var] = $var . '=' . $val; + } + + $route_template = explode('?', $route_template); + $route_template[1] = str_replace($route_params, $params, $route_template[1]); + $url = implode('?', $route_template); + } + + return $url; + } +} \ No newline at end of file diff --git a/server/Fury/Modules/Router/RouterImplementation.php b/server/Fury/Modules/Router/RouterImplementation.php new file mode 100644 index 0000000..e86cbe2 --- /dev/null +++ b/server/Fury/Modules/Router/RouterImplementation.php @@ -0,0 +1,198 @@ + [], 'post' => [], 'uri' => []]; + + /** + * Current URI REQUEST + * + * @var string + */ + public $uri; + + /** + * Instance of class CallControl + * + * @var CallControl + */ + protected $call_control_instance; + + /** + * Method for getting params from uri request by route template + * + * @method required_params_from_uri + * + * @param [string] $route_template [Route template] + * @param [string] $uri_path [Current URI request] + * + * @return [array] [Array with result searching params] + */ + protected function required_params_from_uri($route_template, $uri_path){ + $route_template_parts = explode('/', $route_template); + $uri_parts = explode('/', $uri_path); + $params = []; + foreach ($route_template_parts as $i => $part) { + if(strlen($part) and $part[0] != '$'){ + continue; + } + $params[mb_substr($part, 1, strlen($part))] = $uri_parts[$i]; + } + + return $params; + } + + /** + * Routing by GET and POST vars + * + * @method GET_and_POST_routing + * + * @param [array] $routes_map_part [Array with routes templates] + * @param [array] $vars [Current vars GET or POST] + */ + protected function GET_and_POST_routing($routes_map_part, $vars){ + $result_routes = []; + + foreach ($routes_map_part as $route => $action) { + if(strpos($route, '?')){ + list($route_uri, $route_vars) = explode('?', $route); + }else{ + $route_vars = $route; + } + + $route_vars = explode('&', $route_vars); + $flag = true; + if(isset($route_uri) and $route_uri != $this -> uri){ + $flag = false; + } + + foreach ($route_vars as $i => $rvar) { + if(!isset($vars[$rvar])){ + $flag = false; + break; + } + } + + if($flag){ + $result_routes[$route] = $action; + $this -> call_control_instance -> call_action(true, $route, $action, $vars); + } + } + + return $result_routes; + } + + /** + * Searching routes templates by current URI + * + * @method searching_route_by_uri + * + * @param [array] $routes_map Where need searching + * @param [string] $uri Current URI + * + * @return [array] [Array with result searching] + */ + protected function searching_route_by_uri($routes_map, $uri){ + $results_routes_templates = []; + + $uri_parts = explode('/', $uri); + $count_uri_parts = count($uri_parts); + foreach ($routes_map as $route_template => $action) { + if(strpos($route_template, '$') === false){ + continue; + } + $route_parts = explode('/', $route_template); + if(count($route_parts) != $count_uri_parts){ + continue; + } + + $flag = true; + foreach ($route_parts as $i => $part) { + if(strlen($part) and $part[0] == '$'){ + continue; + } + if($part != $uri_parts[$i]){ + $flag = false; + break; + } + } + + if($flag){ + $results_routes_templates[] = $route_template; + } + } + + return $results_routes_templates; + } + + /** + * Implementation URI Routing + * + * @method URI_routing + * + * @param [array] $routes_map_part [Part of routes map for URI routing] + * + * @return [array] [Array with routes templates, that we need] + */ + protected function URI_routing($routes_map_part){ + $result_routes_templates = []; + if(isset($routes_map_part[$this -> uri])){ + $this -> call_control_instance -> call_action(false, $this -> uri, $routes_map_part[$this -> uri]); + }else{ + $routes_templates = $this -> searching_route_by_uri($routes_map_part, $this -> uri); + $params = []; + foreach($routes_templates as $i => $template){ + $params[$template] = $this -> required_params_from_uri($template, $this -> uri); + $this -> call_control_instance -> call_action(false, $template, $routes_map_part[$template], $params[$template]); + } + $result_routes_templates[] = [ + 'routes_templates' => $routes_templates, + 'params' => $params + ]; + } + + return $result_routes_templates; + } + + /** + * [Add new route to routes map] + * + * @method add_route + * + * @param [string] $method [Method of routing "GET_POST" or "URI"] + * @param [string or array] $route [uri route or array with vars names GET/POST] + * @param [string or function] $action [anon func or string name of function or Classname@methodname] + */ + protected function add_route($method, $route, $action){ + $this -> routes_map[$method][$route] = $action; + } + + /** + * Get Parameters from route template. Only for uri routes + * + * @method get_params_from_route_template + * + * @param String $route_template + * + * @return array + */ + protected function get_params_from_route_template(String $route_template){ + $template_parts = explode('/', $route_template); + $params = []; + foreach ($template_parts as $part) { + if(isset($part[0]) and $part[0] == '$'){ + $params[] = $part; + } + } + + return $params; + } +} \ No newline at end of file diff --git a/server/Fury/Modules/Router/RouterInterface.php b/server/Fury/Modules/Router/RouterInterface.php new file mode 100644 index 0000000..42990bf --- /dev/null +++ b/server/Fury/Modules/Router/RouterInterface.php @@ -0,0 +1,91 @@ + 23]) [return like "/post/post_id/23"] + * + * @return string + */ + public function urlto(String $action, Array $params = []); +} \ No newline at end of file diff --git a/server/Fury/Modules/RoutesHelper/RoutesHelper.php b/server/Fury/Modules/RoutesHelper/RoutesHelper.php new file mode 100644 index 0000000..7a02518 --- /dev/null +++ b/server/Fury/Modules/RoutesHelper/RoutesHelper.php @@ -0,0 +1,72 @@ + router = $router; + } + + public function class(String $classname, Array $without = []){ + $without = array_merge($this -> forbidden_to_create_for, $without); + + $current_router_meth = $this -> router_meth; + + $class = new \ReflectionClass($classname); + $methods = $class -> getMethods(); + + $result_routes = []; + + foreach ($methods as $method){ + if(in_array($method -> name, $without)){ + continue; + } + + $action_str = "{$classname}@{$method -> name}"; + $this -> change_routing_meth($current_router_meth); + $result_routes = $this -> method($action_str); + } + + return $result_routes; + } + + public function method(String $action_str){ + list($classname, $methname) = explode('@', $action_str); + $route = $this -> generate_route_by_method($classname, $methname); + $this -> add_route($route, $action_str); + $this -> log_generated_route($classname, $methname, $route); + return $route; + } + + public function get_generated_routes(){ + return $this -> generated_routes; + } + + public function uri(){ + $this -> change_routing_meth('uri'); + return $this; + } + + public function get(){ + $this -> change_routing_meth('get'); + return $this; + } + + public function post(){ + $this -> change_routing_meth('post'); + return $this; + } +} \ No newline at end of file diff --git a/server/Fury/Modules/RoutesHelper/RoutesHelperImplementation.php b/server/Fury/Modules/RoutesHelper/RoutesHelperImplementation.php new file mode 100644 index 0000000..ca633ec --- /dev/null +++ b/server/Fury/Modules/RoutesHelper/RoutesHelperImplementation.php @@ -0,0 +1,96 @@ + getMethod($methname); + $src_params = $method -> getParameters(); + + $params = array_map(function($param){ + return $param -> name; + }, $src_params); + + $classname = strtolower($classname); + $methname = strtolower($methname); + + $classname = mb_substr($classname, 1, strlen($classname)); + + if($this -> router_meth == 'uri'){ + $final_route = $this -> gen_route_for_uri($classname, $methname, $params); + }elseif($this -> router_meth == 'get' or $this -> router_meth == 'post'){ + $final_route = $this -> gen_route_for_getpost($classname, $methname, $params); + } + + return $final_route; + } + + protected function gen_route_for_uri(String $classname, String $methname, Array $params){ + $classname = str_replace('\\', '-', $classname); + + $static_route = str_replace('_', '-', "/{$classname}/{$methname}"); + $params_route = ''; + foreach ($params as $param) { + $params_route .= '/' . $param . '/$' . $param; + } + + $final_route = $static_route . $params_route; + + return $final_route; + } + + protected function gen_route_for_getpost(String $classname, String $methname, Array $params){ + $vars = explode('\\', $classname); + $vars[] = $methname; + $final_route = array_merge($vars, $params); + return $final_route; + } + + protected function log_generated_route_from_array(Array $route_log){ + $this -> generated_routes[] = $route_log; + } + + protected function log_generated_route(String $classname, String $methname, $route){ + $route_log = [ + 'classname' => $classname, + 'methname' => $methname, + 'route' => $route + ]; + + $this -> log_generated_route_from_array($route_log); + } + + protected function log_generated_routes_array(Array $routes){ + foreach ($routes as $route) { + $this -> log_generated_route_from_array($route); + } + } + + protected function add_route($route, String $action_str){ + if(!$this -> router_meth or $this -> router_meth == ''){ + return false; + } + + $meth = $this -> router_meth; + $this -> router -> $meth($route, $action_str); + $this -> clear_routing_meth(); + } + + protected function change_routing_meth(String $routing_meth){ + $this -> router_meth = $routing_meth; + } + + protected function clear_routing_meth(){ + $this -> change_routing_meth(''); + } + +} \ No newline at end of file diff --git a/server/Fury/Modules/RoutesHelper/RoutesHelperInterface.php b/server/Fury/Modules/RoutesHelper/RoutesHelperInterface.php new file mode 100644 index 0000000..fe8ec5f --- /dev/null +++ b/server/Fury/Modules/RoutesHelper/RoutesHelperInterface.php @@ -0,0 +1,66 @@ + + * @version 0.1 + * Date: 29.01.2020 + */ + +interface DriverInterface{ + /** + * Generating event about create new template + * + * @method event_create_template_instance + * + * @param Object $template_instance Object of Template type + * + */ + public function event_create_template_instance($template_instance); + + /** + * Generating event about start making html template + * + * @method event_start_making + * + * @param String $template_name Path to html template file + * @param String $template_file [description] + * @param Array $inside_data Data in Array format + * @param Object $template_instance Object of Template type + * + */ + public function event_start_making(String $template_name, String $template_file, Array $inside_data, $template_instance); + + /** + * Event about ready template for printing + * + * @method event_ready_template + * + * @param String $template_name Name of template + * @param Object $template_instance Object of Template type + * + */ + public function event_ready_template(String $template_name, $template_instance); + + /** + * Event about joining part of template to main template + * + * @method event_start_joining + * + * @param String $child_template_name Child template name + * @param Array $inside_data Array with data + * + */ + public function event_start_joining(String $child_template_name, Array $inside_data); +} \ No newline at end of file diff --git a/server/Fury/Modules/Template/Template.php b/server/Fury/Modules/Template/Template.php new file mode 100644 index 0000000..29e64e7 --- /dev/null +++ b/server/Fury/Modules/Template/Template.php @@ -0,0 +1,156 @@ + + * @version 0.1 + * Date: 10.01.2020 + */ + +class Template implements TemplateInterface{ + protected static $driver = false; + + protected $parent; + protected $template_childs = []; + + public $templates_folder; + public $project_folder; + + protected $template_html; + public $template_name; + public $template_file; + public $template_content; + protected $template_extends = ['flag' => false, 'srcname' => '', 'name' => '', 'object' => NULL]; + public $was_drawn = false; + + protected $inside_data; + + public static $all_templates = []; + + public function __construct($project_folder, $templates_folder, $parent = NULL){ + $this -> project_folder = $project_folder; + $this -> templates_folder = $templates_folder; + $this -> parent = $parent; + self::$all_templates[] = $this; + + if(self::$driver){ + self::$driver -> event_create_template_instance($this); + } + } + + public static function set_driver($driver){ + self::$driver = $driver; + } + + public function make($template_name, $inside_data = []){ + $template = $this -> t_path($template_name); + + if(self::$driver){ + self::$driver -> event_start_making($template_name, $template, $inside_data, $this); + } + + $this -> inside_data = $inside_data; + $this -> heir_manipulation_run(); + + ob_start(); + extract($this -> inside_data); + include $template; + $html = ob_get_clean(); + + $this -> template_html = $html; + $this -> template_name = $template_name; + $this -> template_file = $template; + + if($this -> template_extends['flag']){ + $this -> template_extends['object'] -> set_content($html); + $this -> template_html = $this -> template_extends['object'] -> make($this -> template_extends['name']); + } + + $this -> was_drawn(); + return $this -> template_html; + } + + protected function t_path($template_name){ + if(strpos($template_name, '.php') === false){ + $template_name .= '.php'; + } + + return $this -> project_folder . '/' . $this -> templates_folder . '/' . $template_name; + } + + public function get_html(){ + return $this -> $template_html; + } + + public function join($child_template_name, array $inside_data = []){ + if(self::$driver){ + self::$driver -> event_start_joining($child_template_name, $inside_data); + } + list($child_template, $child_template_name) = $this -> create_template_object($child_template_name); + $this -> template_childs[$child_template_name] = $child_template; + return $child_template -> make($child_template_name, $inside_data); + } + + private function heir_manipulation_run(){ + $methname = 'heir_manipulation'; + if(method_exists($this, $methname)){ + $returned_data = $this -> $methname($this -> inside_data); + if(is_array($returned_data)) { + $this -> inside_data = $returned_data; + } + } + } + + protected function create_template_object($child_template_name){ + if(strpos($child_template_name, ':')){ + list($child_template_class, $child_template_name) = explode(':', $child_template_name); + } + + if(!isset($child_template_class)){ + $child_template = new Template($this -> project_folder, $this -> templates_folder, $this); + }else{ + $child_template = new $child_template_class($this -> project_folder, $this -> templates_folder, $this); + } + + return [$child_template, $child_template_name]; + } + + public function parent(){ + return $this -> parent; + } + + public function childs(){ + return $this -> template_childs; + } + + public function extends_from($extends_template_name){ + $this -> template_extends['flag'] = true; + $this -> template_extends['srcname'] = $extends_template_name; + list($child_template, $child_template_name) = $this -> create_template_object($extends_template_name); + $this -> template_extends['name'] = $child_template_name; + $this -> template_extends['object'] = $child_template; + $this -> template_childs[$this -> template_extends['srcname']] = $child_template; + } + + public function set_content($content){ + $this -> template_content = $content; + } + + public function content(){ + return $this -> template_content; + } + + public function get_inside_data(){ + return $this -> inside_data; + } + + private function was_drawn(){ + $this -> was_drawn = true; + + if(self::$driver){ + self::$driver -> event_ready_template($this -> template_name, $this); + } + } +} \ No newline at end of file diff --git a/server/Fury/Modules/Template/TemplateInterface.php b/server/Fury/Modules/Template/TemplateInterface.php new file mode 100644 index 0000000..59bb449 --- /dev/null +++ b/server/Fury/Modules/Template/TemplateInterface.php @@ -0,0 +1,87 @@ + + * @version 0.1 + * Date: 29.01.2020 + */ + +interface DriverInterface{ + /** + * Method for generated event about ready sql string to query + * + * @method event_ready_sql + * + * @param String $sql Ready sql string + * + */ + public function event_ready_sql(String $sql); + + /** + * Generating event about query to db. Called after query + * + * @method event_query + * + * @param String $sql SQL string + * @param [type] $result Result of query + */ + public function event_query(String $sql, $result); +} \ No newline at end of file diff --git a/server/Fury/Modules/ThinBuilder/History.php b/server/Fury/Modules/ThinBuilder/History.php new file mode 100644 index 0000000..b5206fa --- /dev/null +++ b/server/Fury/Modules/ThinBuilder/History.php @@ -0,0 +1,31 @@ + history[$sql] = $result; + } + + public function get_all_history(){ + return $this -> history; + } + + public function get(String $sql){ + if(isset($this -> history[$sql])){ + return $this -> history[$sql]; + } + + return NULL; + } +} \ No newline at end of file diff --git a/server/Fury/Modules/ThinBuilder/ThinBuilder.php b/server/Fury/Modules/ThinBuilder/ThinBuilder.php new file mode 100644 index 0000000..1d13d08 --- /dev/null +++ b/server/Fury/Modules/ThinBuilder/ThinBuilder.php @@ -0,0 +1,197 @@ + + * @version 0.1 + * Date: 22.01.2020 + * Update At: 18.08.2022 + */ + +namespace Fury\Modules\ThinBuilder; + +class ThinBuilder implements ThinBuilderInterface{ + + use ThinBuilderProcessing; + + public function query(String $sql, String $fetch_func = '', Int $fetch_func_param = NULL){ + if($this -> driver){ + $this -> driver -> event_ready_sql($sql); + } + + if($this -> gen_sql_only) { + $this -> gen_sql_only = false; + return $sql; + } + + // TODO: if result of query() == false - we have error about trying call fetch func + $response = $this -> pdo -> query($sql); + if(!$response) { + $result = null; + } else { + $result = $fetch_func ? $response -> $fetch_func($fetch_func_param) : $response; + } + + if($this -> history_enabled){ + $this -> history -> add($sql, $result); + } + + if($this -> driver){ + $this -> driver -> event_query($sql, $result); + } + + return $result; + } + + // $where = [ [], 'AND', [], 'OR', [] ] + public function select(String $tablename, $fields = [], $where = [], $order_fields = [], String $order_sort = 'DESC', $limit = []){ + list($fields, $where, $order_fields, $limit) = $this -> select_data_preprocessing($fields, $where, $order_fields, $limit); + + if($order_fields != ''){ + $order_fields .= " {$order_sort}"; + } + + $sql = "SELECT {$fields} FROM `{$tablename}` {$where} {$order_fields} {$limit}"; + return $this -> query($sql, 'fetchAll', \PDO::FETCH_ASSOC); + } + + public function insert(String $tablename, Array $data){ + $tablename = addslashes($tablename); + $data = $this -> escape_string_in_arr($data); + + $fields = '`' . implode('`,`', array_keys($data)) . '`'; + $values = "'" . implode("','", array_values($data)) . "'"; + $sql = "INSERT INTO `{$tablename}` ({$fields}) VALUES ($values)"; + + if($this -> query($sql)){ + $id = $this -> pdo -> lastInsertId(); + $this -> history -> add($sql, $id); + return $id; + } + + return false; + } + + public function update(String $tablename, Array $data, $where = []){ + $where = $this -> where_processing($where); + $data = $this -> escape_string_in_arr($data); + $tablename = addslashes($tablename); + + $pdata = []; + foreach ($data as $field => $value) { + $pdata[] = "`{$field}`='{$value}'"; + } + + $sql = "UPDATE `{$tablename}` SET " . implode(',', $pdata) . " {$where}"; + return $this -> query($sql); + } + + public function delete(String $tablename, $where = []){ + $tablename = addslashes($tablename); + $where = $this -> where_processing($where); + + $sql = "DELETE FROM `{$tablename}` {$where}"; + return $this -> query($sql); + } + + public function drop(String $tablename){ + $tablename = addslashes($tablename); + $sql = "DROP TABLE `{$tablename}`"; + return $this -> query($sql); + } + + public function truncate(String $tablename){ + $tablename = addslashes($tablename); + $sql = "TRUNCATE TABLE `{$tablename}`"; + return $this -> query($sql); + } + + public function create_table(String $tablename, Array $fields, String $primary_key, $engine = 'InnoDB'){ + /* $fields = [ + 'id' => [ + 'type' => 'INT', + 'length' => 11, + 'default' => 'NOT NULL', + 'auto_increment' => true, + 'can_be_null' => true + ], + 'option_key' => [ + 'type' => 'VARCHAR', + 'length' => 255, + 'default' => 'NOT NULL' + 'auto_increment' => false, + 'can_be_null' => false + ], + ] */ + + $tablename = addslashes($tablename); + $fields = $this -> escape_string_in_arr($fields); + $primary_key = addslashes($primary_key); + $engine = addslashes($engine); + + $fields_str_arr = []; + foreach ($fields as $name => $options) { + $length = (isset($options['length']) and !is_null($options['length'])) ? "({$options['length']})" : ''; + + if(isset($options['default'])){ + $default = ($options['default'] == 'NULL' or $options['default'] == 'CURRENT_TIMESTAMP') ? "DEFAULT {$options['default']}" : "DEFAULT '{$options['default']}'"; + }else{ + $default = ''; + } + + $auto_increment = (isset($options['auto_increment']) and $options['auto_increment']) ? 'AUTO_INCREMENT' : ''; + $can_be_null = (isset($options['can_be_null']) and $options['can_be_null']) ? 'NULL' : 'NOT NULL'; + + $fields_str_arr[] = "`{$name}` {$options['type']}{$length} {$can_be_null} {$default} {$auto_increment}"; + } + + $fields_string = implode(', ', $fields_str_arr); + $sql = "CREATE TABLE IF NOT EXISTS `{$tablename}` ({$fields_string}, PRIMARY KEY (`{$primary_key}`)) ENGINE = {$engine}"; + + return $this -> query($sql); + } + + public function table_fields(String $tablename){ + $tablename = addslashes($tablename); + + $sql = "SHOW COLUMNS FROM `{$tablename}`"; + $result = $this -> query($sql, 'fetchAll', \PDO::FETCH_NUM); + $fields = []; + foreach ($result as $raw_field) { + list($type, $length) = explode('(', $raw_field[1]); + $fields[$raw_field[0]] = ['type' => $type]; + $length = intval($length); + if($length){ + $fields[$raw_field[0]]['length'] = $length; + } + } + + return $fields; + } + + public function tables(){ + $sql = 'SHOW TABLES'; + $result = $this -> query($sql, 'fetchAll', \PDO::FETCH_ASSOC); + return array_map(function($val){ + $k = array_keys($val); + return $val[$k[0]]; + }, $result); + } + + public function count(String $tablename, $where = []){ + $tablename = addslashes($tablename); + $where = $this -> where_processing($where); + $sql = "SELECT COUNT(*) FROM `{$tablename}` {$where}"; + $result = $this -> query($sql, 'fetch', \PDO::FETCH_NUM); + return $result ? intval($result[0]) : 0; + } + + public function history(){ + return $this -> history; + } + + public function sql_for() { + $this -> gen_sql_only = true; + return $this; + } +} \ No newline at end of file diff --git a/server/Fury/Modules/ThinBuilder/ThinBuilderInterface.php b/server/Fury/Modules/ThinBuilder/ThinBuilderInterface.php new file mode 100644 index 0000000..1bbd965 --- /dev/null +++ b/server/Fury/Modules/ThinBuilder/ThinBuilderInterface.php @@ -0,0 +1,155 @@ + + * @version 0.1 + * Update at 18.08.2022 + */ + +trait ThinBuilderProcessing{ + protected $pdo; + protected $db_config; + protected $history; + protected $history_enabled; + protected $driver; + protected $gen_sql_only = false; + + public function __construct($db_config, $driver = null, $history_enabled = true){ + $this -> db_config = $db_config; + $this -> pdo = $this -> create_connect($this -> db_config); + $this -> driver = $driver; + $this -> history_enabled = $history_enabled; + if($this -> history_enabled){ + $this -> create_history_instance(); + } + } + + protected function create_history_instance(){ + $this -> history = new History(); + } + + protected function create_connect($db_conf){ + $dblib = "{$db_conf['dblib']}:host={$db_conf['host']};dbname={$db_conf['dbname']};charset={$db_conf['charset']}"; + $pdo = new \PDO($dblib, $db_conf['user'], $db_conf['password']); + return $pdo; + } + + protected function escape_string_in_arr($arr){ + $result = []; + foreach ($arr as $key => $value) { + if(!is_array($value)){ + $result[addslashes($key)] = addslashes($value); + }else{ + $result[addslashes($key)] = $this -> escape_string_in_arr($value); + } + } + return $result; + } + + protected function select_data_preprocessing($fields, $where, $order_fields, $limit){ + // FIELDS PREPROCESSING + if(count($fields)){ + $fields = $this -> escape_string_in_arr($fields); + $fields = '`' . implode('`,`', $fields) . '`'; + }else{ + $fields = '*'; + } + + // ORDER PREPROCESSING + if(count($order_fields)){ + $order_fields = $this -> escape_string_in_arr($order_fields); + $order_fields = 'ORDER BY `' . implode("`,`", $order_fields) . '`'; + }else{ + $order_fields = ''; + } + + // WHERE PREPROCESSING + $where = $this -> where_processing($where); + + // LIMIT PREPROCESSING + if(count($limit)){ + $limit = $this -> escape_string_in_arr($limit); + $limit = 'LIMIT ' . implode(',', $limit); + }else{ + $limit = ''; + } + + return [$fields, $where, $order_fields, $limit]; + } + + protected function where_processing($where){ + if(!count($where)){ + return ''; + } + + $where = $this -> escape_string_in_arr($where); + foreach ($where as $i => $w_item) { + if(is_array($w_item)){ + if(count($w_item) === 2){ + $w_item = [$w_item[0], '=', $w_item[1]]; + } + + $w_item[0] = "`{$w_item[0]}`"; + if($w_item[1] != 'IN'){ + $w_item[2] = "'{$w_item[2]}'"; + }else{ + $w_item[2] = '(\'' . implode("','", $w_item[2]) . '\')'; + } + + $where[$i] = implode(' ', $w_item); + } + } + + $where = 'WHERE ' . implode(' ', $where); + return $where; + } +} \ No newline at end of file diff --git a/server/Fury/__autoload.php b/server/Fury/__autoload.php new file mode 100644 index 0000000..7dd4375 --- /dev/null +++ b/server/Fury/__autoload.php @@ -0,0 +1,10 @@ + + .dd-container{ + width: 100%; + box-sizing: border-box; + height: auto; + padding: 20px 10px; + background-color: #333; + color: white; + } + + .dd-container *{ + font-family: Arial; + letter-spacing: .8px; + } + + .dd-line{ + padding: 5px 20px; + } + + .dd-margin{ + margin-left: 30px; + } + + .dd-arrow{ + font-weight: bold; + padding: 0 10px; + color: #D9CB04; + } + + .dd-key{ + font-weight: bold; + padding: 0 3px; + color: #038C8C; + } + + .dd-key-border{ + color: #026873; + } + + .dd-keyword{ + font-weight: bold; + color: #D9B504; + margin-right: 5px; + } + + .dd-brackets-content{ + font-weight: bold; + padding: 0 3px; + color: #F28D77; + } + + .dd-block{ + display: none; + } + + .dd-block.show{ + display: block; + } + + .dd-btn{ + display: inline-block; + color: #7ED955; + background: transparent; + padding: 0; + width: 20px; + height: 20px; + cursor: pointer; + text-align: center; + outline: none; + font-size: 16px; + line-height: 18px; + border: 2px solid #7ED955; + } + + .dd-block-show, + .dd-block-hide{ + margin-top: -29px; + position: relative; + left: -10px; + float: right; + } + + .dd-block-hide{ + line-height: 16px; + color: #F28D77; + border-color: #F28D77; + } + + '; + + // JAVASCRIPT + $js = ''; + + $lines = explode("\n", $dump); + $prev_lvl = 0; + $cur_lvl = 1; + foreach ($lines as $inx => $line) { + $two_space = ''; + $len = strlen($line); + $count_spaces = 0; + + for($i=0; $i<$len; $i++){ + if($line[$i] != " "){ + $count_spaces = $i; + break; + } + } + + $line = mb_substr($line, $count_spaces, $len); + for($i=0; $i<$count_spaces / 2; $i++){ + $line = $two_space . $line; + } + + $lines[$inx] = '
' . $line . '
'; + + $cur_lvl = $count_spaces / 2; + if($prev_lvl < $cur_lvl){ + $bid = uniqid('', $inx); + $lines[$inx] = ' + +
+ + ' . $lines[$inx]; + } + + if($prev_lvl > $cur_lvl){ + $lines[$inx] .= '
'; + } + + $prev_lvl = $cur_lvl; + + } + + $dump = implode('', $lines); + + $dump = str_replace('=>', '>>', $dump); + $dump = str_replace('["', '["', $dump); + $dump = str_replace('"]', '"]', $dump); + + // keywords + $keywords = ['array', 'string', 'int', 'float', 'double', 'object']; + $formating_keywords = array_map(function($item){ + return '' . $item . ''; + }, $keywords); + $dump = str_replace($keywords, $formating_keywords, $dump); + + // brackets content + $dump = str_replace('(', '(', $dump); + $dump = str_replace(')', ')', $dump); + + // Print data forming + $dump = $style . '
' . $dump; + $dump .= '
' . $js; + $json_var = json_encode(["var" => $var]); + $dump .= ""; + + echo $die_flag ? die($dump) : $dump; +} + +function ddjson($var, $die_flag = true) { + $die_flag ? die(json_encode([ "die" => $var ])) : print(json_encode([ "die" => $var ])); +} + +function app(){ + return AppContainer::app(); +} + +function bootstrap(){ + return AppContainer::bootstrap(); +} + +function events(){ + return AppContainer::events(); +} + +function logging(){ + return AppContainer::logging(); +} \ No newline at end of file diff --git a/server/SHServ/App.php b/server/SHServ/App.php new file mode 100644 index 0000000..7395017 --- /dev/null +++ b/server/SHServ/App.php @@ -0,0 +1,60 @@ + console_flag = isset($argv) ? true : false; + $this -> app_init(); + } + + public function app_init(){ + if(!$this -> console_flag) { + $this -> error_handlers = new ErrorHandler(); + } + + \Fury\Modules\Template\Template::set_driver(new \Fury\Drivers\TemplateDriver()); + + $this -> devtools = new DevTools(); + $this -> router = new Router(); + $this -> routes = new Routes($this -> router); + $this -> thin_builder = new ThinBuilder(FCONF['db'], new \Fury\Drivers\ThinBuilderDriver(bootstrap())); + $this -> events_handlers = new EventsHandlers(); + $this -> events_handlers -> handlers(); + + // CUSTOM + $this -> utils = new Utils(); + $this -> sessions = new Sessions(); + $this -> factory = new Factory(); + } + + public function root_folder(){ + list($root) = explode('SHServ', __DIR__); + return $root; + } + +} + +new App(); \ No newline at end of file diff --git a/server/SHServ/Controllers/Example_AuthController.php b/server/SHServ/Controllers/Example_AuthController.php new file mode 100644 index 0000000..a9482b3 --- /dev/null +++ b/server/SHServ/Controllers/Example_AuthController.php @@ -0,0 +1,118 @@ + sessions -> is_auth()) { + return $this -> utils() -> redirect( app() -> routes -> urlto("SearchController@search_page") ); + } + + return $this -> new_template() -> make("site/signup", [ + "page_title" => "Регистрация", + "page_alias" => "page signup" + ]); + } + + public function signin_page() { + if(app() -> sessions -> is_auth()) { + return $this -> utils() -> redirect( app() -> routes -> urlto("SearchController@search_page") ); + } + + return $this -> new_template() -> make("site/signin", [ + "page_title" => "Войти в систему", + "page_alias" => "page signin" + ]); + } + + public function signout_page($redirect_to) { + $auth = new Auth(); + $auth -> signout(); + return $this -> utils() -> redirect($redirect_to); + } + + public function signup($email, $password, $password_again) { + // TODO: generate event + + if(app() -> sessions -> is_auth()){ + return $this -> utils() -> response_error("already_logged"); + } + + $email = strtolower(trim(strip_tags($email))); + + if(strlen($email) < 4 or !strpos($email, "@") or !strpos($email, ".")) { + return $this -> utils() -> response_error("incorrect_email", [ "email" ]); + } + + if(strlen($password) < 8) { + return $this -> utils() -> response_error("too_short_password", [ "password" ]); + } + + if($password != $password_again) { + return $this -> utils() -> response_error("different_passwords", [ "password", "password_again" ]); + } + + if(User::is_exists_by("email", $email)) { + return $this -> utils() -> response_error("email_already_exists", [ "email" ]); + } + + $auth = new Auth(); + $user = $auth -> signup($email, $password); + + if(!$user) { + return $this -> utils() -> response_error("undefined_error", [ "email" ]); + } + + return $this -> utils() -> response_success([ + "redirect_url" => app() -> routes -> urlto("AuthController@signin_page"), + "redirect_delay" => 250 + ]); + } + + public function signin($email, $password) { + // TODO: generate event + if(app() -> sessions -> is_auth()){ + return $this -> utils() -> response_error("already_logged"); + } + + $email = strtolower(trim(strip_tags($email))); + + if(!strlen($email)) { + return $this -> utils() -> response_error("empty_field", [ "email" ]); + } + + if(!strlen($password)) { + return $this -> utils() -> response_error("empty_field", [ "password" ]); + } + + if(!User::is_exists_by("email", $email)) { + return $this -> utils() -> response_error("unregistered_email", [ "email" ]); + } + + $auth = new Auth(); + $token = $auth -> signin($email, $password); + + if(!$token){ + return $this -> utils() -> response_error("incorrect_password", [ "password" ]); + } + + return $this -> utils() -> response_success([ + "token" => $token, + "redirect_url" => "/", + "redirect_delay" => 250 + ]); + } + + public function signout() { + if(!app() -> sessions -> is_auth()){ + return $this -> utils() -> response_error("not_found_any_sessions"); + } + + $auth = new Auth(); + $auth -> signout(); + return $this -> utils() -> response_success(); + } +} \ No newline at end of file diff --git a/server/SHServ/DevTools.php b/server/SHServ/DevTools.php new file mode 100644 index 0000000..18d2317 --- /dev/null +++ b/server/SHServ/DevTools.php @@ -0,0 +1,135 @@ + root_template) { + $this -> root_template = $template; + } + + if(!isset($this -> templates_counter[$template_name])) { + $this -> templates_counter[$template_name] = 0; + $this -> total_uniq_template_parts++; + } + + if(!isset($this -> templates_timelog[$template_name])) { + $this -> templates_timelog[$template_name] = [ + "rendering_start" => 0, + "rendering_time" => 0 + ]; + } + + $this -> templates_timelog[$template_name]["rendering_start"] = microtime(true); + $this -> templates_counter[$template_name]++; + $this -> total_template_calls++; + } + + public function make_template_map(Array $templates) { + $arr = []; + foreach($templates as $template) { + $arr[$template -> template_name] = [ + "calls" => $this -> templates_counter[$template -> template_name], + "rendering_time" => $this -> templates_timelog[$template -> template_name]["rendering_time"], + "childs" => $this -> make_template_map($template -> childs()) + ]; + } + + return $arr; + } + + public function render_template_done(String $template_name) { + $render_time = microtime(true) - $this -> templates_timelog[$template_name]["rendering_start"]; + $this -> templates_timelog[$template_name]["rendering_time"] += $render_time; + } + + public function loging_action_call(String $action_name, String $action_type, Array $action_params) { + $this -> action_name = $action_name; + $this -> action_type = $action_type; + $this -> action_params = $action_params; + $this -> action_execute_time = microtime(true); + } + + public function loging_action_time() { + $this -> action_execute_time = microtime(true) - $this -> action_execute_time; + } + + public function using_model(String $model_name) { + $this -> models[] = $model_name; + } + + public function loging_sql_query(String $sql) { + $this -> sql_queries[] = [ + "query" => $sql, + "time" => microtime(true) + ]; + } + + public function loging_sql_query_result() { + $last_inx = count($this -> sql_queries) - 1; + $this -> sql_queries[$last_inx]["time"] = microtime(true) - $this -> sql_queries[$last_inx]["time"]; + $this -> sql_summary_time += $this -> sql_queries[$last_inx]["time"]; + } + + public function timelog_start(String $logname, String $title = "Untitled"): Void { + $this -> time_logs[$logname] = [ + "title" => $title, + "timestamp" => microtime(true), + ]; + } + + public function timelog_end(String $logname): Bool { + if(!isset($this -> time_logs[$logname])) { + return false; + } + + $this -> time_logs[$logname]["timestamp"] = microtime(true) - $this -> time_logs[$logname]["timestamp"]; + return true; + } + + public function show() { + if($this -> root_template) { + $this -> template_map = $this -> make_template_map([ $this -> root_template ]); + echo (new Template(PROJECT_FOLDER, FCONF["templates_folder"])) -> make("devtools/devtools-panel", [ + "template_map" => $this -> template_map, + "total_template_calls" => $this -> total_template_calls, + "total_uniq_template_parts" => $this -> total_uniq_template_parts, + + "action_name" => $this -> action_name, + "action_type" => $this -> action_type, + "action_params" => $this -> action_params, + "action_execute_time" => $this -> action_execute_time, + + "models" => $this -> models, + + "sql_queries" => $this -> sql_queries, + "sql_summary_time" => $this -> sql_summary_time, + + "time_logs" => $this -> time_logs, + ]); + } + } +} diff --git a/server/SHServ/Entities/Favorite.php b/server/SHServ/Entities/Favorite.php new file mode 100644 index 0000000..bf15da2 --- /dev/null +++ b/server/SHServ/Entities/Favorite.php @@ -0,0 +1,33 @@ + remove_entity()) { + return false; + } + + if($this -> assignment == "UAdPost") { + (new UAdPost($this -> ent_id)) -> statistics() -> in_favorites_decrease(); + } + + return true; + } +} \ No newline at end of file diff --git a/server/SHServ/Entities/Image.php b/server/SHServ/Entities/Image.php new file mode 100644 index 0000000..ee888af --- /dev/null +++ b/server/SHServ/Entities/Image.php @@ -0,0 +1,75 @@ + get_path_to_image($size)); + } + + public function get_url(String $size = "original") { + if(!isset(FCONF["image_resize_map"][$size])){ + // TODO: NORMAL ERR VIEW + dd("Error of size name `{$size}`"); + } + + $postfix = $size == "original" ? "" : "_{$size}"; + + if(!$this -> image_exists($size)) { + if($size == "original") { + return $this -> default_image(); + } else { + return $this -> get_url("original"); + } + } + + $img_name = "{$this -> alias}{$postfix}.jpg"; + + return app() -> routes -> urlto("ImgUploaderController@show_img", [ + "img_name" => $img_name + ]); + } + + public function get_path_to_image(String $size = "original") { + if(!isset(FCONF["image_resize_map"][$size])){ + // TODO: NORMAL ERR VIEW + dd("Error of size name `{$size}`"); + } + + $postfix = $size == "original" ? "" : "_{$size}"; + return FCONF["users_folder"] . "/{$this -> alias}{$postfix}.jpg"; + } + + protected function remove_files() { + foreach (FCONF["image_resize_map"] as $size => $props) { + if($this -> image_exists($size)) { + unlink($this -> get_path_to_image($size)); + } + } + + return true; + } + + public function remove() { + $this -> remove_files(); + return $this -> remove_entity(); + } + + public function default_image() { + return "/SHServ/Resources/img/default-product-img.png"; + } +} \ No newline at end of file diff --git a/server/SHServ/Entities/Meta.php b/server/SHServ/Entities/Meta.php new file mode 100644 index 0000000..c65ad8e --- /dev/null +++ b/server/SHServ/Entities/Meta.php @@ -0,0 +1,27 @@ + value . ""; + } + + public function remove() { + $this -> remove_entity(); + } +} \ No newline at end of file diff --git a/server/SHServ/Entities/NPDelivery.php b/server/SHServ/Entities/NPDelivery.php new file mode 100644 index 0000000..ed0ce11 --- /dev/null +++ b/server/SHServ/Entities/NPDelivery.php @@ -0,0 +1,22 @@ + np_department); + } +} \ No newline at end of file diff --git a/server/SHServ/Entities/Order.php b/server/SHServ/Entities/Order.php new file mode 100644 index 0000000..2ab5486 --- /dev/null +++ b/server/SHServ/Entities/Order.php @@ -0,0 +1,104 @@ + uadpost = new UAdPost($this -> uap_id); + $this -> seller = new User($this -> seller_id); + $this -> customer = new User($this -> customer_id); + } + + public function uadpost(): UAdPost { + if(!$this -> was_filled()) { + $this -> fill(); + } + + $this -> uadpost -> currency = $this -> currency; + $this -> uadpost -> price = $this -> price; + return $this -> uadpost; + } + + public function get_formatted_create_at() { + return app() -> utils -> formatted_timestamp($this -> create_at, true); + } + + public function get_delivery_method_text_name() { + $delivery_map = app() -> utils -> get_delivery_method_map(); + return isset($delivery_map[$this -> delivery_method]) ? $delivery_map[$this -> delivery_method] : ""; + } + + public function seller(): User { + if(!$this -> was_filled()) { + $this -> fill(); + } + + return $this -> seller; + } + + public function customer(): User { + if(!$this -> was_filled()) { + $this -> fill(); + } + + return $this -> customer; + } + + public function confirm() { + $this -> state = "confirmed"; + return $this -> update(); + } + + public function cancel() { + $this -> state = "canceled"; + return $this -> update(); + } + + public function complete() { + $this -> state = "completed"; + $this -> seller() -> statistics() -> total_saled_increase(); + $this -> uadpost() -> statistics() -> sales_increase(); + return $this -> update(); + } + + public function remove() { + $this -> remove_entity(); + } + + public function nova_poshta_delivery(): ?NPDelivery { + if($this -> delivery_method !== "1") { + return null; + } + + return $this -> get_pet_instance( + "NPDelivery", + fn() => (new NovaPoshta()) -> get_by_order_id($this -> id) + ); + } +} \ No newline at end of file diff --git a/server/SHServ/Entities/Profile.php b/server/SHServ/Entities/Profile.php new file mode 100644 index 0000000..8ff8e58 --- /dev/null +++ b/server/SHServ/Entities/Profile.php @@ -0,0 +1,33 @@ + imgs_container = new ImgsContainer($profile_id, "Profile"); + } + + public function userpic(): ?Image { + return $this -> imgs_container -> get_first_img(); + } + + public function userpic_url(String $size): String { + $userpic = $this -> userpic(); + return $userpic + ? $userpic -> get_url($size) + : "/SHServ/Resources/img/default-avatar-img.png"; + } +} \ No newline at end of file diff --git a/server/SHServ/Entities/Session.php b/server/SHServ/Entities/Session.php new file mode 100644 index 0000000..bd60230 --- /dev/null +++ b/server/SHServ/Entities/Session.php @@ -0,0 +1,19 @@ + last_using_at)) / 60; + } +} \ No newline at end of file diff --git a/server/SHServ/Entities/UAdPost.php b/server/SHServ/Entities/UAdPost.php new file mode 100644 index 0000000..4746885 --- /dev/null +++ b/server/SHServ/Entities/UAdPost.php @@ -0,0 +1,213 @@ + statistics = new UAdPostStatistics($id); + $this -> imgs_container = new ImgsContainer($id, "UAdPost"); + } + + public function get_images(): Array { + if(!$this -> imgs_container -> was_filled()) { + $this -> imgs_container -> fill_container(); + } + + return $this -> imgs_container -> get_imgs(); + } + + public function get_first_image(): ?Image { + return $this -> imgs_container -> get_first_img(); + } + + public function has_images(): Bool { + return count($this -> get_images()) != 0; + } + + public function fill(Array $data = []) { + parent::fill($data); + $this -> user = new User($this -> uid); + } + + public function user(): User { + if(!$this -> was_filled()) { + $this -> fill(); + } + + return $this -> user; + } + + public function statistics(): UAdPostStatistics { + return $this -> statistics; + } + + public function favorite(): ?Favorite { + if(app() -> sessions -> is_auth() and !$this -> favorite and is_null($this -> favorite_state_for_current_user)) { + $this -> favorite = (new Favourites()) -> get_one_by( + $this -> id(), + "UAdPost", + app() -> sessions -> auth_user() -> id() + ); + } + + $this -> set_favorite_state_for_current_user(!is_null($this -> favorite)); + + return $this -> favorite; + } + + public function set_favorite(Favorite $favorite): ?Favorite { + $this -> favorite = $favorite; + return $this -> favorite(); + } + + public function get_url(): String { + return app() -> routes -> urlto("UAdPostController@view_page", [ + "alias" => "{$this -> alias}.html" + ]); + } + + public function get_formatted_timestamp(): String { + return app() -> utils -> formatted_timestamp($this -> create_at); + } + + public function get_formatted_price(): String { + $price = number_format($this -> price, 2, ",", " "); + return $price; + } + + public function get_price_particles(): Array { + list($banknotes, $coins) = explode(",", $this -> get_formatted_price()) ; + return compact("banknotes", "coins"); + } + + public function get_single_price_particles(): Array { + list($banknotes, $coins) = explode(",", number_format($this -> single_price, 2, ",", " ")); + return compact("banknotes", "coins"); + } + + public function get_formatted_currency(String $currency = ""): String { + $t = [ + "UAH" => "грн", + "EUR" => "€", + "USD" => "$" + ]; + + return $t[ strlen($currency) ? $currency : $this -> currency ]; + } + + public function remove(): Void { + if($this -> has_images()) { + $imgs = $this -> get_images(); + foreach($imgs as $img) { + $img -> remove(); + } + } + + if($this -> state == "published") { + $this -> deactivate(); + } + + $this -> statistics() -> clear_all_fields(); + $this -> remove_entity(); + } + + public function make_removed(): Void { + if($this -> state == "published") { + $this -> deactivate(); + } + + $this -> state = "removed"; + $this -> update(); + } + + public function deactivate(): Void { + $this -> state = "unpublished"; + $this -> update(); + $this -> user() -> statistics() -> total_published_uadposts_decrease(); + $this -> remove_keywords(); + (new Favourites()) -> remove_for_assignment_unit($this -> id(), "UAdPost"); + } + + public function activate(): Void { + $this -> state = "published"; + $this -> update(); + $this -> user() -> statistics() -> total_published_uadposts_increase(); + $this -> refresh_keywords(); + } + + public function is_favorite_for_current_user(): Bool { + if(is_null($this -> favorite_state_for_current_user)) { + $this -> favorite(); + } + + return $this -> favorite_state_for_current_user; + } + + public function set_favorite_state_for_current_user(Bool $state): void { + $this -> favorite_state_for_current_user = $state; + } + + public function generate_keywords(): Array { + $keywords = (new Keywords) -> create_keywords_by_content( + "{$this -> title}", + $this -> id + ); + + $keywords_reload_url = FCONF["services"]["keywords"]["keywords_reload"]; + if($keywords_reload_url) { + @file_get_contents($keywords_reload_url); + } + return $keywords; + } + + public function remove_keywords(): Mixed { + $res = (new Keywords) -> remove_keywords_by_uap_id($this -> id); + $keywords_reload_url = FCONF["services"]["keywords"]["keywords_reload"]; + if($keywords_reload_url) { + @file_get_contents($keywords_reload_url); + } + + return $res; + } + + public function refresh_keywords(): Array { + if(!$this -> remove_keywords()) { + return []; + } + + return $this -> generate_keywords(); + } + + public function keywords(): KeywordsContainer { + return $this -> get_pet_instance( + "KeywordsContainer", + fn() => (new Keywords) -> get_keywords_by_uap_id($this -> id) + ); + } +} \ No newline at end of file diff --git a/server/SHServ/Entities/User.php b/server/SHServ/Entities/User.php new file mode 100644 index 0000000..a54b41a --- /dev/null +++ b/server/SHServ/Entities/User.php @@ -0,0 +1,116 @@ + statistics = new UserStatistics($uid); + // TODO: `uid` - is fail, need `profile_id` + $this -> profile = new Profile($uid); + UsersContainer::add_entity_item($this); + } + + public function profile(): Profile { + return $this -> profile; + } + + public function statistics(): UserStatistics { + return $this -> statistics; + } + + public function get_last_uadpost(): ?UAdPost { + $posts = app() -> factory -> getter() -> get_uadposts_by("uid", $this -> id(), 1); + return count($posts) ? $posts[0] : null; + } + + public function total_uadposts(String $state = "published"): Int { + return (new UAdPosts()) -> total_by_user($this -> id(), $state); + } + + public function get_uadposts(String $state = "published", Int $page_num = 1): Array { + $uadposts = (new UAdPosts()) -> get_by_user( + $this -> id(), + $state, + FCONF["profile_uadposts_per_page"], + $page_num, + "update_at" + ); + + UAdPostsContainer::fill(); + + return $uadposts; + } + + public function last_session(): ?\SHServ\Entities\Session { + return $this -> get_pet_instance("Session", function() { + return app() -> factory -> getter() -> get_session_by("uid", $this -> id()); + }); + } + + public function total_favourites_uadposts(): Int { + return (new Favourites()) -> total_by_user( $this -> id(), "UAdPost" ); + } + + public function get_orders(String $utype, Int $page_num = 1, Array $including_states = []): Array { + $sorting_cases = [ + "unconfirmed", + "confirmed", + "canceled", + "completed", + ]; + $order_by = "CASE "; + foreach($sorting_cases as $i => $case) { + $then = $i + 1; + $order_by .= "WHEN `state`='{$case}' THEN {$then} "; + } + $order_by .= " ELSE " . (count($sorting_cases) + 1) . " END"; + $order_by .= ", `create_at`"; + + $orders_model = new Orders(); + $orders = $orders_model -> get_by_user( + $utype, + $this -> id, FCONF["user_orders_per_page"], + $page_num, + $order_by, + "DESC", + $including_states + ); + return $orders; + } + + public function total_orders(String $utype, Array $including_states = []): Int { + return (new Orders()) -> total_by_user($utype, $this -> id, $including_states); + } + + public function role_is(String $role_name): Bool { + return $this -> role == $role_name; + } + + // Static methods + + public static function is_exists_by(String $field_name, String $field_value): Bool { + return app() -> utils -> table_row_is_exists( + app() -> thin_builder, + self::$table_name, + $field_name, + $field_value + ); + } +} \ No newline at end of file diff --git a/server/SHServ/EventsHandlers.php b/server/SHServ/EventsHandlers.php new file mode 100644 index 0000000..e56c415 --- /dev/null +++ b/server/SHServ/EventsHandlers.php @@ -0,0 +1,61 @@ + handler('kernel:Bootstrap.ready_app', function(Array $params) { + app() -> routes -> routes_init(); + if(!app() -> console_flag) { + app() -> router -> start_routing(); + } + }); + + events() -> handler('kernel:CallControl.no_calls', function(Array $params) { + if(!app() -> console_flag) { + echo "404 not found"; + } + }); + + if(FCONF["devmode"]) { + events() -> handler("module:Template.start_making", function(Array $params) { + app() -> devtools -> add_template_to_map( + $params["template_instance"], + $params["template_name"] + ); + }); + + events() -> handler("module:Template.ready_template", function(Array $params) { + app() -> devtools -> render_template_done( + $params["template_name"] + ); + }); + + events() -> handler("kernel:CallControl.leading_call", function(Array $params) { + app() -> devtools -> loging_action_call( + $params["action"], + $params["type"], + $params["params"] + ); + }); + + events() -> handler("kernel:CallControl.completed_call", function(Array $params) { + app() -> devtools -> loging_action_time(); + }); + + events() -> handler("kernel:Bootstrap.app_finished", function(Array $params){ + if(isset($_GET["devp"])) { + app() -> devtools -> show(); + } + }); + + events() -> handler("module:ThinBuilder.ready_sql", function(Array $params){ + app() -> devtools -> loging_sql_query($params["sql"]); + }); + + events() -> handler("module:ThinBuilder.query", function(Array $params){ + app() -> devtools -> loging_sql_query_result(); + }); + } + } +} \ No newline at end of file diff --git a/server/SHServ/Factory/Creator.php b/server/SHServ/Factory/Creator.php new file mode 100644 index 0000000..657529e --- /dev/null +++ b/server/SHServ/Factory/Creator.php @@ -0,0 +1,105 @@ + thin_builder -> insert(User::$table_name, [ + "alias" => $alias, + "email" => $email, + "password" => $password_hash, + "create_at" => date("Y-m-d H:i:s") + ]); + + return $uid ? new User($uid) : null; + } + + public function create_profile(Int $uid) { + $profile_id = app() -> thin_builder -> insert(Profile::$table_name, [ + "uid" => $uid, + "create_at" => date("Y-m-d H:i:s") + ]); + + return $profile_id ? new Profile($profile_id) : null; + } + + public function create_image(Int $uid, Int $ent_id, String $assignment, String $alias, Int $sequence = 0) { + $image_id = app() -> thin_builder -> insert(Image::$table_name, [ + "uid" => $uid, + "ent_id" => $ent_id, + "assignment" => $assignment, + "alias" => $alias, + "sequence" => $sequence, + "create_at" => date("Y-m-d H:i:s") + ]); + + return $image_id ? new Image($image_id) : null; + } + + public function create_uadpost( + Int $uid, String $title, String $content, Int $condition, Int $exchange_flag, + Float $price, String $currency, Float $lat, Float $lng, String $country_en, + String $country_ru, String $region_en, String $region_ru, String $city_en, + String $city_ru, Int $images_number, String $state = "published" + ) { + $uadpost_id = app() -> thin_builder -> insert(UAdPost::$table_name, [ + "uid" => $uid, + "alias" => app() -> utils -> gen_from_text_alias(uniqid() . "-" . $title), + "title" => $title, + "content" => $content, + "condition_used" => $condition, + "exchange_flag" => $exchange_flag, + "state" => $state, + "price" => $price, + "single_price" => app() -> utils -> convert_price_to_uah_from($currency, $price), + "currency" => $currency, + "location_lat" => $lat, + "location_lng" => $lng, + "country_en" => $country_en, + "country_ru" => $country_ru, + "region_en" => $region_en, + "region_ru" => $region_ru, + "city_en" => $city_en, + "city_ru" => $city_ru, + "factor" => 1, + "rating" => 0, + "images_number" => intval($images_number), + "update_at" => date("Y-m-d H:i:s") + ]); + + $uadpost = $uadpost_id ? new UAdPost($uadpost_id) : null; + if($uadpost) { + $uadpost -> generate_keywords(); + } + return $uadpost; + } + + public function create_meta(Int $ent_id, String $assignment, String $name, $value): Meta { + $data = [ + "ent_id" => $ent_id, + "assignment" => $assignment, + "name" => $name, + "value" => $value, + "create_at" => date("Y-m-d H:i:s") + ]; + + $meta_id = app() -> thin_builder -> insert(Meta::$table_name, $data); + + return new Meta($meta_id, array_merge( + [ + "id" => $meta_id, + "update_at" => $data["create_at"] + ], + $data + )); + } +} \ No newline at end of file diff --git a/server/SHServ/Factory/Factory.php b/server/SHServ/Factory/Factory.php new file mode 100644 index 0000000..c8b4c7c --- /dev/null +++ b/server/SHServ/Factory/Factory.php @@ -0,0 +1,24 @@ + creator_instance) { + $this -> creator_instance = new Creator(); + } + + return $this -> creator_instance; + } + + public function getter() { + if(!$this -> getter_instance) { + $this -> getter_instance = new Getter(); + } + + return $this -> getter_instance; + } +} \ No newline at end of file diff --git a/server/SHServ/Factory/Getter.php b/server/SHServ/Factory/Getter.php new file mode 100644 index 0000000..59dff3d --- /dev/null +++ b/server/SHServ/Factory/Getter.php @@ -0,0 +1,151 @@ + thin_builder -> select( + User::$table_name, User::get_fields(), [ [$field_name, "=", $field_value] ] + ); + + if(!$result) { + return null; + } + + return new User($result[0]["id"], $result[0]); + } + + public function get_profile_by(String $field_name, $field_value) { + $result = app() -> thin_builder -> select( + Profile::$table_name, + ["id"], + [ [$field_name, "=", $field_value] ], + [], "", + [0, 1] + ); + + if(!$result) { + return null; + } + + return new Profile($result[0]["id"]); + } + + public function get_images_by_entity(Int $ent_id, String $assignment, Int $amount = 10) { + $result = app() -> thin_builder -> select( + Image::$table_name, + [], + [ ["ent_id", "=", $ent_id], "AND", ["assignment", "=", $assignment] ], + [ "sequence" ], + "ASC", + [0, $amount] + ); + + if(!$result) { + return []; + } + + $images = []; + foreach($result as $item) { + $images[] = new Image($item["id"], $item); + } + + return $images; + } + + public function get_uadposts_by(String $field_name, $field_value, Int $amount = 10): Array { + $result = app() -> thin_builder -> select( + UAdPost::$table_name, + ["id"], + [[ $field_name, is_array($field_value) ? "IN" : "=", $field_value ]], + [ "id" ], + "DESC", + [0, $amount] + ); + + if(!$result) { + return []; + } + + $uadposts = []; + foreach($result as $item) { + $uadposts[] = new UAdPost($item["id"]); + } + + UAdPostsContainer::fill(); + + return $uadposts; + } + + public function get_session_by(String $field_name, $field_value): ?Session { + $result = app() -> thin_builder -> select( + Session::$table_name, + Session::get_fields(), + [[$field_name, "=", $field_value]], + ["last_using_at"], + "DESC", + [0, 1] + ); + + if(!$result) { + return null; + } + + return new Session(intval($result[0]["id"]), $result[0]); + } + + public function get_meta(Int $ent_id, String $assignment, Int $amount = 10): Array { + $result = app() -> thin_builder -> select( + Meta::$table_name, + Meta::get_fields(), + [ ["ent_id", "=", $ent_id], "AND", ["assignment", "=", $assignment] ], + [], + "", + [0, $amount] + ); + + if(!$result) { + return []; + } + + $meta = []; + foreach($result as $item) { + $meta[] = new Meta($item["id"], $item); + } + + return $meta; + } + + public function get_orders_by(String $field_name, $field_value, Int $amount = 10) : Array { + $result = app() -> thin_builder -> select( + Order::$table_name, + Order::get_fields(), + [ + [ $field_name, is_array($field_value) ? "IN" : "=", $field_value ] + ], + [ "id" ], + "DESC", + [0, $amount] + ); + + if(!$result) { + return []; + } + + $orders = []; + foreach($result as $item) { + $orders[] = new Order($item["id"], $item); + } + + return $orders; + } +} \ No newline at end of file diff --git a/server/SHServ/Helpers/Generator.php b/server/SHServ/Helpers/Generator.php new file mode 100644 index 0000000..50cfdda --- /dev/null +++ b/server/SHServ/Helpers/Generator.php @@ -0,0 +1,110 @@ + lorem_ipsum = new LoremIpsum(); + } + + public function generate_random_users(Int $amount = 10) { + for($i = 0; $i < $amount; $i++) { + $email = $this -> lorem_ipsum -> get_email(); + $password = "1111"; + $user = (new Auth()) -> signup($email, $password); + $user -> profile() -> first_name = $this -> lorem_ipsum -> get_name(); + $user -> profile() -> second_name = $this -> lorem_ipsum -> get_surname(); + $user -> profile() -> phone_number = $this -> lorem_ipsum -> get_phone_number(); + $user -> profile() -> location_lat = rand(400000, 600000) / 10000; + $user -> profile() -> location_lng = rand(220000, 300000) / 10000; + $user -> profile() -> update(); + echo "\n#{$i} {$user -> profile() -> first_name} {$email} {$password}"; + } + + echo "\nDone"; + } + + public function generate_random_uadpost(Int $amount = 10) { + for($i = 0; $i < $amount; $i++) { + $rand_user = app() -> factory -> getter() -> get_user_by("id", rand(1, 9999)); + $title = $this -> lorem_ipsum -> gen_paragraph(1); + + $uadpost = app() -> factory -> creator() -> create_uadpost( + $rand_user -> id(), + $title, + "

" . implode("

", $this -> lorem_ipsum -> gen_paragraphs(rand(1, 8), 1, 20)) . "

", + rand(1, 2), + rand(0, 1), + rand(0, 10000), + "UAH", + rand(400000, 600000) / 10000, + rand(220000, 300000) / 10000, + "Ukraine", + "Украина", + "TestRegion", + "ТестРегион", + "TestCity", + "ТестГород", + 0 + ); + + echo "\n#{$i} {$title}"; + } + + echo "\nDone"; + } + + public function generate_uadpost_from_json($json_data) { + $uadpost_data = json_decode($json_data); + + $rand_user = app() -> factory -> getter() -> get_user_by("id", rand(1, 9999)); + + $images = new Images(); + if($uadpost_data -> image and strlen($uadpost_data -> image)) { + echo "Upload image " . $uadpost_data -> image . "\n"; + $img = @file_get_contents($uadpost_data -> image); + if($img) { + $img = (new Utils()) -> convert_png_to_jpg($img); + $img = "data:image/jpeg;base64," . base64_encode($img); + $img_upload_result = $images -> upload($img); + } + } + + echo "Creating UAdPost\n"; + $uadpost = app() -> factory -> creator() -> create_uadpost( + $rand_user -> id(), + $uadpost_data -> title, + $uadpost_data -> description, + rand(1, 2), + rand(0, 1), + $uadpost_data -> price, + $uadpost_data -> currency, + rand(400000, 600000) / 10000, + rand(220000, 300000) / 10000, + "Ukraine", + "Украина", + "TestRegion", + "ТестРегион", + "TestCity", + "ТестГород", + $img_upload_result ? 1 : 0 + ); + + if($uadpost and $img_upload_result) { + $images -> create_from_aliases( + [ $img_upload_result["alias"] ], + $uadpost, + $rand_user -> id() + ); + } + + echo "Was created \n"; + } +} \ No newline at end of file diff --git a/server/SHServ/Helpers/GetSetImplementation.php b/server/SHServ/Helpers/GetSetImplementation.php new file mode 100644 index 0000000..966d22c --- /dev/null +++ b/server/SHServ/Helpers/GetSetImplementation.php @@ -0,0 +1,39 @@ + data; + } + + public function get(String $field_name) { + if(in_array($field_name, static::$fields)) { + return $this -> data[$field_name]; + } + + throw new \Exception("Error of GET, field `{$field_name}` not found"); + } + + public function set(String $field_name, $field_val) { + if(!in_array($field_name, static::$fields)){ + throw new \Exception("Error of SET, field `{$field_name}` not found"); + } + + $this -> data[$field_name] = $field_val; + $this -> modified_fields[$field_name] = $field_val; + return $this; + } + + public function __get($field_name) { + return $this -> get($field_name); + } + + public function __set($field_name, $field_val) { + return $this -> set($field_name, $field_val); + } +} diff --git a/server/SHServ/Helpers/PetInstancesImplementation.php b/server/SHServ/Helpers/PetInstancesImplementation.php new file mode 100644 index 0000000..180986a --- /dev/null +++ b/server/SHServ/Helpers/PetInstancesImplementation.php @@ -0,0 +1,23 @@ + pet_instances[$instance_name])) { + $this -> pet_instances[$instance_name] = $callback(); + } + + return $this -> pet_instances[$instance_name]; + } + + public function forward_instance_init(String $instance_name, $instance) { + $this -> pet_instances[$instance_name] = $instance; + } + + public function get_existing_pet_list() { + return array_keys($this -> pet_instances); + } +} diff --git a/server/SHServ/Helpers/Validator.php b/server/SHServ/Helpers/Validator.php new file mode 100644 index 0000000..369c23f --- /dev/null +++ b/server/SHServ/Helpers/Validator.php @@ -0,0 +1,7 @@ + utils; + } +} \ No newline at end of file diff --git a/server/SHServ/Middleware/Entity.php b/server/SHServ/Middleware/Entity.php new file mode 100644 index 0000000..3746160 --- /dev/null +++ b/server/SHServ/Middleware/Entity.php @@ -0,0 +1,91 @@ + entity_tablename = $entity_tablename; + $this -> entity_id = $entity_id; + + if(count($data)) { + $this -> fill($data); + } + } + + public function fill(Array $data = []) { + if(count($data)) { + $this -> data = $data; + } else { + $this -> select_from_db(); + } + + $this -> was_filled = true; + } + + protected function select_from_db() { + list($this -> data) = $this -> thin_builder() -> select( + $this -> entity_tablename, + [], + ['id', '=', $this -> entity_id], + ['id'], + 'DESC', + [0, 1] + ); + } + + public function get(String $field_name): Mixed { + if(!$this -> was_filled()) { + $this -> fill(); + } + + return $this -> parent_get($field_name); + } + + public function was_filled(): Bool { + return $this -> was_filled; + } + + public function thin_builder(): \Fury\Modules\ThinBuilder\ThinBuilder { + return app() -> thin_builder; + } + + public function id(): Int { + return $this -> entity_id; + } + + public function update(): Array|Bool { + if(!count($this -> modified_fields)){ + return []; + } + + $where = [ ["id", "=", $this -> entity_id] ]; + $this -> modified_fields[$this -> field_name_of_update_at] = date("Y-m-d H:i:s"); + + if(!$this -> thin_builder() -> update($this -> entity_tablename, $this -> modified_fields, $where)) { + return false; + } + + $result = $this -> modified_fields; + $this -> modified_fields = []; + return $result; + } + + public static function get_fields(): Array { + return static::$fields; + } + + protected function remove_entity() { + return app() -> thin_builder -> delete(static::$table_name, [ "id", "=", $this -> id() ]); + } +} \ No newline at end of file diff --git a/server/SHServ/Middleware/Model.php b/server/SHServ/Middleware/Model.php new file mode 100644 index 0000000..7b9b777 --- /dev/null +++ b/server/SHServ/Middleware/Model.php @@ -0,0 +1,26 @@ + devtools -> using_model(get_class($this)); + } + + public function utils(){ + if(!$this -> utils_ins){ + $this -> utils_ins = new \SHServ\Utils(); + } + + return $this -> utils_ins; + } + + public function thin_builder(){ + return app() -> thin_builder; + } +} \ No newline at end of file diff --git a/server/SHServ/Models/Example_Auth.php b/server/SHServ/Models/Example_Auth.php new file mode 100644 index 0000000..5a54fb3 --- /dev/null +++ b/server/SHServ/Models/Example_Auth.php @@ -0,0 +1,41 @@ + factory -> creator() -> create_user( + app() -> utils -> gen_alias_from_email($email), + $email, + $password + ); + + if($user) { + $profile = app() -> factory -> creator() -> create_profile($user -> id()); + } + + return $user; + } + + public function signin(String $email, String $password) { + $password = sha1($password); + $user = app() -> factory -> getter() -> get_user_by("email", $email); + + if(!$user or $user -> get("password") != $password) { + return false; + } + + return app() -> sessions -> init_session($user -> id()); + } + + public function signout() { + return app() -> sessions -> close_current_session(); + } + + public function remove_session(Int $uid) { + + } +} \ No newline at end of file diff --git a/server/SHServ/Routes.php b/server/SHServ/Routes.php new file mode 100644 index 0000000..8a5e7c6 --- /dev/null +++ b/server/SHServ/Routes.php @@ -0,0 +1,120 @@ + router = $router; + $this -> cf = FCONF["controllers_folder"]; + $this -> cn = "\\" . FCONF["app_name"] . "\\" . FCONF["controllers_folder"]; + } + + public function routes_init() { + $this -> uri_routes(); + $this -> get_routes(); + $this -> post_routes(); + } + + protected function uri_routes() { + // pages + // $this -> router -> uri("/", "{$this -> cn}\\SearchController@search_page"); + // $this -> router -> uri('/not-found.html', "{$this -> cn}\\InfoPagesController@not_found_page"); + + // $this -> router -> uri('/uadpost/$alias', "{$this -> cn}\\UAdPostController@view_page"); + + + // $this -> router -> uri( + // '/profile/orders/$utype/exclude-states', + // function($args) { + // return app() -> utils -> redirect( + // app() -> routes -> urlto("OrderController@orders_cur_user_page", ["utype" => $args["utype"]]) + // ); + // } + // ); + + } + + protected function get_routes() { + // $this -> router -> get( + // [ "uadpost_id", "state" ], + // "{$this -> cn}\\UAdPostController@change_uadpost_state", + // "/profile/uadposts/change-state.html" + // ); + } + + protected function post_routes() { + $this -> router -> post( + [ "email", "password", "password_again" ], + "{$this -> cn}\\AuthController@signup", + "/auth/signup" + ); + } + + /** + * urlto, get url by action name [with arguments] + * @var String $action_name - Action name, like a "HelloController@world_action" + * @var Array $url_args - Assoc array with arguments (ONLY FOR REQUEST TYPE GET) + * + * @return String Url to action, ready for use + */ + public function urlto(String $action_name, Array $url_args = []) { + $routes_map = $this -> router -> get_routes_map(); + $desired_action = "{$this -> cn}\\{$action_name}"; + foreach($routes_map["uri"] as $url => $action) { + if($action == $desired_action) { + foreach($url_args as $arg_name => $arg_val) { + $url = str_replace("\${$arg_name}", $arg_val, $url); + } + + return $url; + } + } + + foreach($routes_map["post"] as $url_pattern => $action){ + if($action == $desired_action) { + list($url) = explode("?", $url_pattern); + return $url; + } + } + + foreach($routes_map["get"] as $url_pattern => $action){ + if($action == $desired_action) { + list($url, $param_names) = explode("?", $url_pattern); + if(count($url_args)) { + $param_names = explode("&", $param_names); + $params = array_flip($param_names); + foreach($params as $key => $val) { + if(!isset($url_args[$key])) { + continue; + } + + $params[$key] = $url_args[$key]; + } + }else{ + $params = []; + } + + return $url . "?" . http_build_query($params); + } + } + } +} \ No newline at end of file diff --git a/server/SHServ/Sessions.php b/server/SHServ/Sessions.php new file mode 100644 index 0000000..a877800 --- /dev/null +++ b/server/SHServ/Sessions.php @@ -0,0 +1,109 @@ + thin_builder -> insert($this -> table_name, [ + "uid" => $uid, + "token" => $token, + "create_at" => date("Y-m-d H:i:s") + ]); + + return $result ? $token : false; + } + + public function close($token) { + $session = $this -> get_session_by_token($token); + if(!$session) { + return false; + } + + $session -> set("state", 2); + return $session -> update(); + } + + public function close_current_session() { + return $this -> close($this -> get_auth_token()); + } + + public function set_session(String $token) { + setcookie("auth_token", $token, time() + 3600 * 24 * 30, "/"); + } + + public function init_session(Int $uid) { + $token = $this -> create($uid); + + if($token){ + $this -> set_session($token); + } + + return $token; + } + + public function is_auth() { + return $this -> get_current_session() ? true : false; + } + + public function get_auth_token() { + return isset($_COOKIE["auth_token"]) ? $_COOKIE["auth_token"] : null; + } + + public function auth_user() { + if(!$this -> get_auth_token()) { + return null; + } + + if(!$this -> auth_user_instance) { + $session = $this -> get_current_session(); + if(!$session){ + return null; + } + + $this -> auth_user_instance = new User($session -> get("uid")); + } + + return $this -> auth_user_instance; + } + + public function get_current_session() { + if(!$this -> current_session_instance){ + $token = $this -> get_auth_token(); + if(!$token) { + return null; + } + + $this -> current_session_instance = $this -> get_session_by_token($token); + if($this -> current_session_instance){ + $this -> current_session_instance -> set("last_using_at", date("Y-m-d H:i:s")) -> update(); + } + } + + return $this -> current_session_instance; + } + + public function get_session_by_token(String $token) { + $result = app() -> thin_builder -> select( + Session::$table_name, + Session::get_fields(), + [ + ["token", "=", $token], + "AND", + ["state", "=", 1] + ] + ); + + if(!$result) { + return null; + } + + return new Session(intval($result[0]["id"]), $result[0]); + } +} \ No newline at end of file diff --git a/server/SHServ/Utils.php b/server/SHServ/Utils.php new file mode 100644 index 0000000..6d8fe4d --- /dev/null +++ b/server/SHServ/Utils.php @@ -0,0 +1,230 @@ + count($tablename, [ [$field_name, "=", $value] ]) ? true : false; + } + + public function response_error(String $error_alias, Array $failed_fields = [], Array $extra = []) { + return json_encode(array_merge([ + "status" => false, + "error_alias" => $error_alias, + "failed_fields" => $failed_fields, + "msg" => $this -> get_msg_by_alias($error_alias) + ], $extra)); + } + + public function response_success(Array $resp_data = []) { + return json_encode([ + "status" => true, + "data" => $resp_data + ]); + } + + public function get_msg_by_alias(String $alias){ + return FCONF['text_msgs'][$alias]; + } + + public function compress_image(String $source, String $destination, Int $quality) { + $info = getimagesize($source); + + if ($info['mime'] == 'image/jpeg') + $image = imagecreatefromjpeg($source); + elseif ($info['mime'] == 'image/gif') + $image = imagecreatefromgif($source); + elseif ($info['mime'] == 'image/png') + $image = imagecreatefrompng($source); + + imagejpeg($image, $destination, $quality); + + return $destination; + } + + public function image_resize(String $file_name, String $output, Int $quality, Int $width, $height = 0) { + list($wid, $ht) = \getimagesize($file_name); + $r = $wid / $ht; + $height = $height ? $height : $width / $r; + + if ($width / $height > $r) { + $new_width = $height * $r; + $new_height = $height; + } else { + $new_height = $width / $r; + $new_width = $width; + } + + $source = \imagecreatefromjpeg($file_name); + $dst = \imagecreatetruecolor($new_width, $new_height); + \imagecopyresampled($dst, $source, 0, 0, 0, 0, $new_width, $new_height, $wid, $ht); + \imagejpeg($dst, $output, $quality); + } + + public function transliterate_cyr_lat(String $str) { + $cyr = [ + 'Љ','Њ','Џ','џ','ш','ђ','ч','ћ','ж','љ','њ','Ш','Ђ','Ч','Ћ', + 'Ж','Ц','ц','а','б','в','г','д','е','ё','ж','з','и','й','к','л','м','н', + 'о','п', 'р','с','т','у','ф','х','ц','ч','ш','щ','ъ','ы','ь','э','ю','я', + 'А','Б','В','Г','Д','Е','Ё','Ж','З','И','Й','К','Л','М','Н','О','П','Р', + 'С','Т','У','Ф','Х','Ц','Ч','Ш','Щ','Ъ','Ы','Ь','Э','Ю','Я','Є','є', + 'Ї','ї','і' + ]; + + $lat = [ + 'Lj','Nj','Dž','dž','š','đ','č','ć','ž','lj','nj','Š','Đ','Č','Ć','Ž','C','c', + 'a','b','v','g','d','e','io','zh','z','i','y','k','l','m','n','o','p', 'r','s', + 't','u','f','h','ts','ch','sh','sht','a','i','y','e','yu','ya', 'A','B','V', + 'G','D','E','Io','Zh','Z','I','Y','K','L','M','N','O','P','R','S','T','U','F', + 'H','Ts','Ch','Sh','Sht','A','I','Y','e','Yu','Ya','Ye','ye','Yi','yi','i', + ]; + + return str_replace($cyr, $lat, $str); + } + + public function gen_from_text_alias(String $str) { + return str_replace( + [" ", ".", ",", "@", "!", "#", '$', "%", "^", "&", "?", "*", "(", ")", "+", "[", "]", "{", "}", ":", ";", "/", "<", ">", "\\"], + ["-", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""], + $this -> transliterate_cyr_lat(strtolower($str)) + ); + } + + public function get_current_page_num(): Int { + return max(1, intval(isset($_GET["pn"]) ? $_GET["pn"] : 0)); + } + + public function get_limits_for_select_query(Int $per_page): Array { + $current_page = $this -> get_current_page_num(); + $from = ($current_page - 1) * $per_page; + return [$from, $per_page]; + } + + public function lang_mistake_flip(String $str) { + $str = str_replace( + ["{", "}", "!", "@", "#", '$', "%", "^", "&", "*", "(", ")"], + ["", "", "", "", "", '$', "", "", "", "", "", ""], + $str + ); + $vocabluary_lat = "`qwertyuiop[]asdfghjkl;'zxcvbnm,. {}<>-+_1234567890"; + $vocabluary_cyr = "ёйцукенгшщзхъфывапролджэячсмитьбю хъбю-+_1234567890"; + + $len = mb_strlen($str); + $new_str = ""; + for($i = 0; $i < $len; $i++) { + $in_lat = mb_strpos($vocabluary_lat, mb_substr($str, $i, 1)); + $in_cyr = mb_strpos($vocabluary_cyr, mb_substr($str, $i, 1)); + + if($in_lat !== false) { + $new_str .= mb_substr($vocabluary_cyr, $in_lat, 1); + continue; + } + + if($in_cyr !== false) { + $new_str .= mb_substr($vocabluary_lat, $in_cyr, 1); + continue; + } + + $new_str .= $str[$i]; + } + + return $new_str; + } + + public function get_default_val_for_type(String $type) { + $default_val = null; + $types_default_vals = [ + "Int" => 0, + "String" => "", + "JSON" => "{}", + "Float" => 0 + ]; + + return $types_default_vals[$type]; + } + + public function link_is_active(String $action, Array $params = []) { + return app() -> routes -> urlto($action, $params) == app() -> router -> uri; + } + + public function formatted_timestamp(String $timestamp, $with_clock = false): String { + if($with_clock) { + return date("d.m.Y H:i", strtotime($timestamp)); + } + + return date("d.m.Y", strtotime($timestamp)); + } + + public function get_delivery_method_map() { + return [ + 1 => "Новая почта", + 2 => "Укр почта", + 3 => "Самовивоз", + 4 => "Другое", + ]; + } + + public function convert_price_to_uah_from(String $currency, Float $price): Float { + if($currency == "UAH") { + return $price; + } + + $timestamp = date("Ymd"); + $api_request = "https://bank.gov.ua/NBUStatService/v1/statdirectory/exchange?valcode={$currency}&date={$timestamp}&json"; + $api_resp = file_get_contents($api_request); + if(!$api_resp) { + return 0; + } + + $resp = json_decode($api_resp, true); + return $resp[0]["rate"] * $price; + } + + public function convert_png_to_jpg($png_data) { + $png_image = imagecreatefromstring($png_data); + $jpg_image = imagecreatetruecolor(imagesx($png_image), imagesy($png_image)); + + imagecopy($jpg_image, $png_image, 0, 0, 0, 0, imagesx($png_image), imagesy($png_image)); + + ob_start(); + imagejpeg($jpg_image, NULL, 100); + $jpg_data = ob_get_contents(); + ob_end_clean(); + + imagedestroy($png_image); + imagedestroy($jpg_image); + + return $jpg_data; + } + + public function dayname_translate(String $dayname, String $lang = "ru"): ?String { + $days = [ + ["en" => "monday", "ru" => "понедельник", "uk" => "понеділок"], + ["en" => "tuesday", "ru" => "вторник", "uk" => "вівторок"], + ["en" => "wednesday", "ru" => "среда", "uk" => "середа"], + ["en" => "thursday", "ru" => "четверг", "uk" => "четвер"], + ["en" => "friday", "ru" => "пятница", "uk" => "п'ятниця"], + ["en" => "saturday", "ru" => "суббота", "uk" => "субота"], + ["en" => "sunday", "ru" => "воскресенье", "uk" => "неділя"] + ]; + + $dayname = strtolower($dayname); + + foreach($days as $day) { + if($day["en"] == $dayname) { + return $day[$lang]; + } + } + + return null; + } +} \ No newline at end of file diff --git a/server/SHServ/config.php b/server/SHServ/config.php new file mode 100644 index 0000000..1d7ddb6 --- /dev/null +++ b/server/SHServ/config.php @@ -0,0 +1,27 @@ + "SHServ", + "debug" => true, + "default_db_wrap" => false, + "db" => [ + "dblib" => "mysql", + "host" => "localhost", + "dbname" => "smart-home-serv", + "charset" => "utf8", + "user" => "eugene", + "password" => "root" + ], + "app_file" => "App.php", + "templates_folder" => "Templates", + "logs_enable" => true, + "logs_folder" => "SHServ/Logs", + "devmode" => true, + + "controllers_folder" => "Controllers", + "text_msgs" => require_once("SHServ/text-msgs.php"), + + "error_handler" => [ + "important_errors" => ["E_WARNING", "E_ERROR", "E_CORE_ERROR", "EXCEPTION"] + ], +]; \ No newline at end of file diff --git a/server/SHServ/text-msgs.php b/server/SHServ/text-msgs.php new file mode 100644 index 0000000..8e28078 --- /dev/null +++ b/server/SHServ/text-msgs.php @@ -0,0 +1,60 @@ + text message + */ + +return [ + // Errors + "incorrect_email" => "Не корректный E-mail", + "too_short_password" => "Слишком короткий пароль", + "different_passwords" => "Пароли не совпадают", + "email_already_exists" => "Такой E-mail уже существует", + "undefined_error" => "Ой, что-то пошло не так", + "empty_field" => "Поле не может быть пустым", + "unregistered_email" => "Такой E-mail не зарегистрирован", + "incorrect_password" => "Не верный пароль", + "not_found_any_sessions" => "Требуется войти в систему", + "already_logged" => "Вы уже в системе", + "error_of_img_upload" => "Ошибка загрузки изображения", + "title_too_short" => "Слишком короткое название", + "price_not_specified" => "Укажите цену", + "location_not_specified" => "Укажите местоположение, не обязательно точное", + "disagree_with_rules" => "Если Вы не согласны с правилами публикации, Вы не можете добавить объявление", + "empty_first_name" => "Укажите Ваше имя, это обязательно", + "empty_second_name" => "Укажите Вашу имя, это обязательно", + "empty_patronymic" => "Укажите Ваше отчество", + "empty_phone_number" => "Укажите контактный номер телефона", + "unlogged_user" => "Сначала нужно войти", + "fail_creating_uadpost" => "Ошибка создания объявления", + "textfield_too_large" => "Превышен лимит количества символов", + "fail_publishing_uadpost" => "Ошибка публикации объявления", + "uadpost_not_exist" => "Такое объявление не существует", + "server_not_available" => "Сервер не доступен", + "uadpost_no_available" => "Объявление больше не доступно", + "price_was_changed" => "Цена была изменена", + "fail_creating_order" => "Не удалось создать запрос покупки", + "fail_access_to_order" => "Не удалось получить доступ к заказу", + "selected_state_not_exists" => "Выбраного состояния не существует", + "uncorrect_delivery_method" => "Укажите метод доставки", + "np_fail_of_department_number" => "Укажите номер отделения или поштомата", + "np_fail_of_selected_city" => "Выберите населённый пункт", + + // Other + "accept_removing" => "Подтвердите удаление", + "remove" => "Удалить", + "cancel" => "Отмена", + "deactivate_selected_uadpost" => "Деактивировать выбранное объявление?", + "deactivate" => "Деактивировать", + "publishing_selected_uadpost" => "Опубликовать выбранное объявление?", + "publishing" => "Опубликовать", + "terms_of_use_not_selected" => "Пользовательское соглашение не выбрано", + "success_and_redirecting" => "Успешно! Перенаправление...", + "accept" => "Принять", + "confirm" => "Подтвердить", + "confirmed" => "Успешно подтверждено", + "canceled" => "Успешно отменено", + "cancel_order" => "Отменить заказ", + "completed" => "Успешно завершено", + "complete_order" => "Заказ выполнен", +]; \ No newline at end of file diff --git a/server/console.php b/server/console.php new file mode 100644 index 0000000..78e6806 --- /dev/null +++ b/server/console.php @@ -0,0 +1,30 @@ + generate_random_users($argv[2]); + break; + case "generator.uadposts": + (new Generator()) -> generate_random_uadpost($argv[2]); + break; + case "create.uadpost": + (new Generator()) -> generate_uadpost_from_json($argv[2]); + break; + case "get.config": + echo json_encode(FCONF); + break; + default: echo "\nNo command"; + } + + echo "\n"; +} + +console(); \ No newline at end of file diff --git a/server/index.php b/server/index.php new file mode 100644 index 0000000..d2b918f --- /dev/null +++ b/server/index.php @@ -0,0 +1,5 @@ +