Initial commit
This commit is contained in:
110
engine/Main/trait.admin.php
Normal file
110
engine/Main/trait.admin.php
Normal file
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
defined('ROOT_DIR') || exit;
|
||||
|
||||
trait Admin {
|
||||
public array $admin_pages = array();
|
||||
public array $admin_pages_list = array();
|
||||
|
||||
public function admin_page_set($parent_slug, $page_slug, $page_title, $params, $function, $hide = false, $icon = false)
|
||||
{
|
||||
if(!isset($params)) $params = array();
|
||||
$params["page"] = $page_slug;
|
||||
if($parent_slug && isset($this->admin_pages_list[$parent_slug])) {
|
||||
$parent = $this->admin_pages_list[$parent_slug];
|
||||
$link = &$parent["link"];
|
||||
$parent_level = $link["level"];
|
||||
|
||||
$page_info = array(
|
||||
"level" => $parent_level + 1,
|
||||
"title" => $page_title,
|
||||
"function" => $function,
|
||||
"pages" => array(),
|
||||
"params" => $params,
|
||||
"hide" => $hide,
|
||||
"icon" => $icon,
|
||||
"slug" => $page_slug
|
||||
);
|
||||
|
||||
$link["pages"][$page_slug] = $page_info;
|
||||
$page_info["link"] = &$link["pages"][$page_slug];
|
||||
} else {
|
||||
$page_info = array(
|
||||
"level" => 0,
|
||||
"title" => $page_title,
|
||||
"function" => $function,
|
||||
"pages" => array(),
|
||||
"params" => $params,
|
||||
"hide" => $hide,
|
||||
"icon" => $icon,
|
||||
"slug" => $page_slug
|
||||
);
|
||||
$this->admin_pages[$page_slug] = $page_info;
|
||||
$page_info["link"] = &$this->admin_pages[$page_slug];
|
||||
}
|
||||
$this->admin_pages_list[$page_slug] = $page_info;
|
||||
}
|
||||
|
||||
// Проверить есть ли у страницы дочерний элемент по с нужной подстрокой
|
||||
public function admin_page_has_search($parent_slug, $search): bool
|
||||
{
|
||||
if(!$search) return true;
|
||||
$parent_page = $this->admin_pages_list[$parent_slug];
|
||||
$parent_page_link = &$parent_page["link"];
|
||||
if(mb_strpos(mb_strtolower($parent_page["title"]), mb_strtolower($search)) !== false) return true;
|
||||
return $this->admin_page_has_search_recurse($parent_page_link["pages"], $search);
|
||||
}
|
||||
|
||||
// Рекурсивная функция проверки дочерних элементов для поиска подстроки
|
||||
private function admin_page_has_search_recurse($pages, $search): bool
|
||||
{
|
||||
foreach ($pages as $key => $page) {
|
||||
if(mb_strpos(mb_strtolower($page["title"]), mb_strtolower($search)) !== false) return true;
|
||||
$result = $this->admin_page_has_child_recurse($page["pages"], $search);
|
||||
if($result === true) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Проверить есть ли у страницы дочерний элемент
|
||||
public function admin_page_has_child($parent_slug, $page_slug): bool
|
||||
{
|
||||
$parent_page = &$this->admin_pages_list[$parent_slug]["link"];
|
||||
if($parent_slug == $page_slug) return true;
|
||||
return $this->admin_page_has_child_recurse($parent_page["pages"], $page_slug);
|
||||
}
|
||||
|
||||
// Рекурсивная функция проверки дочерних элементов
|
||||
private function admin_page_has_child_recurse($pages, $page_slug): bool
|
||||
{
|
||||
foreach ($pages as $key => $page) {
|
||||
if($key === $page_slug) return true;
|
||||
$result = $this->admin_page_has_child_recurse($page["pages"], $page_slug);
|
||||
if($result === true) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Отрисовка страницы в админ панели
|
||||
public function admin_page_render($slug, $dive = false): bool
|
||||
{
|
||||
$page = $this->admin_pages_list[$slug];
|
||||
if(!isset($page)) return false;
|
||||
|
||||
$link = &$page["link"];
|
||||
|
||||
if($page["level"] == 0 && $dive === true)
|
||||
return $this->admin_page_render(array_shift($link["pages"])["slug"]);
|
||||
|
||||
if(is_callable($page["function"])) {
|
||||
$page["function"]();
|
||||
return true;
|
||||
}
|
||||
|
||||
$func_name = $page["function"];
|
||||
$func_body = $GLOBALS[$func_name];
|
||||
$func_body();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
50
engine/Main/trait.alerts.php
Normal file
50
engine/Main/trait.alerts.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
// Работа с уведомлениями
|
||||
|
||||
defined('ROOT_DIR') || exit;
|
||||
|
||||
trait Alerts {
|
||||
// Получить список уведомлений
|
||||
public function alerts_get_list($group = "main")
|
||||
{
|
||||
$session_name = $this->session_get_name();
|
||||
$alerts = $this->db_query("SELECT * FROM `bive_alerts` WHERE `session` = ? AND `is_read` = 0 AND `group` = ?", array($session_name, $group), false);
|
||||
if (!count($alerts)) return false;
|
||||
$this->alerts_read_all($group);
|
||||
return $alerts;
|
||||
}
|
||||
|
||||
// Добавить уведомление
|
||||
public function alerts_add($message, $type = "notice", $group = "main")
|
||||
{
|
||||
$session_name = $this->session_get_name();
|
||||
$this->db_query("INSERT INTO `bive_alerts`(`session`, `message`, `type`, `group`) VALUES (?, ?, ?, ?)", array($session_name, $message, $type, $group), true);
|
||||
}
|
||||
|
||||
// Пометить все уведомления как прочитанные
|
||||
public function alerts_read_all($group = "main")
|
||||
{
|
||||
$session_name = $this->session_get_name();
|
||||
$this->db_query("UPDATE `bive_alerts` SET `is_read` = 1 WHERE `session` = ? AND `is_read` = 0 AND `group` = ?", array($session_name, $group), true);
|
||||
}
|
||||
|
||||
public function alerts_view($group = "main"): bool
|
||||
{
|
||||
$alerts = $this->alerts_get_list($group);
|
||||
if(!$alerts) return false;
|
||||
|
||||
$alerts_dom = new DOM("div");
|
||||
$alerts_dom->setAttribute("class", "b_alerts");
|
||||
|
||||
foreach ($alerts as $key => $alert) {
|
||||
$alert_dom = new DOM("div");
|
||||
$alert_dom->setAttribute("class", "b_alert " . $alert["type"]);
|
||||
$alert_dom->prepend($alert["message"]);
|
||||
$alerts_dom->prepend($alert_dom);
|
||||
}
|
||||
|
||||
$alerts_dom->view();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
133
engine/Main/trait.content.php
Normal file
133
engine/Main/trait.content.php
Normal file
@@ -0,0 +1,133 @@
|
||||
<?php
|
||||
|
||||
// Работа с выводом содержимого пользователям
|
||||
|
||||
defined('ROOT_DIR') || exit;
|
||||
|
||||
trait Content {
|
||||
private array $meta_tags = array();
|
||||
private array $link_tags = array();
|
||||
private array $script_tags = array();
|
||||
private string $title = "Bive Engine";
|
||||
|
||||
// олучить значение заголовка
|
||||
public function title_get(): string
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
// Поместить значение заголовка
|
||||
public function title_set($title): bool
|
||||
{
|
||||
$this->title = $title;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Добавить мета тег
|
||||
public function meta_add($name, $content): bool
|
||||
{
|
||||
$this->meta_tags[$name] = $content;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Удалить значение
|
||||
public function meta_remove($name): bool
|
||||
{
|
||||
if(!isset($this->meta_tags[$name])) return false;
|
||||
unlink($this->meta_tags[$name]);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Рендер мета тегов
|
||||
public function meta_render()
|
||||
{
|
||||
$meta_dom = new DOM("meta", false);
|
||||
$meta_dom->setAttribute("charset", "utf-8");
|
||||
echo $meta_dom->render();
|
||||
|
||||
foreach ($this->meta_tags as $key => $value) {
|
||||
$meta_dom = new DOM("meta", false);
|
||||
$meta_dom->setAttribute("name", $key);
|
||||
$meta_dom->setAttribute("content", $this->get_view($value));
|
||||
echo $meta_dom->render();
|
||||
}
|
||||
}
|
||||
|
||||
// Добавить тег Link
|
||||
public function link_add($params): bool
|
||||
{
|
||||
if(gettype($params) != "array") return false;
|
||||
$this->link_tags[] = $params;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Отрендерить теги типа Link
|
||||
public function link_render()
|
||||
{
|
||||
foreach ($this->link_tags as $key => $link_params) {
|
||||
$link_dom = new DOM("link", false);
|
||||
foreach ($link_params as $index => $value){
|
||||
$link_dom->setAttribute($index, $value);
|
||||
}
|
||||
echo $link_dom->render();
|
||||
}
|
||||
}
|
||||
|
||||
// Подключить CSS скрипт
|
||||
public function css_style_connect($path): bool
|
||||
{
|
||||
if(!$path) return false;
|
||||
$this->link_add(array("rel" => "stylesheet", "href" => $path));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Добавить тег Script
|
||||
public function script_add($params): bool
|
||||
{
|
||||
if(gettype($params) != "array") return false;
|
||||
$this->script_tags[] = $params;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Отрендерить теги типа Script
|
||||
public function script_render()
|
||||
{
|
||||
foreach ($this->script_tags as $key => $scripts_params) {
|
||||
$scripts_dom = new DOM("script");
|
||||
foreach ($scripts_params as $index => $value){
|
||||
$scripts_dom->setAttribute($index, $value);
|
||||
}
|
||||
echo $scripts_dom->render();
|
||||
}
|
||||
}
|
||||
|
||||
// Безопасно выводит информацию
|
||||
function view($text): bool
|
||||
{
|
||||
echo $this->get_view($text);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Получить строку для безопасного использования
|
||||
function get_view($text): string
|
||||
{
|
||||
return htmlspecialchars($text);
|
||||
}
|
||||
|
||||
// Склонение слова в зависимости от числа
|
||||
function pluralize($num, $words) {
|
||||
$num = abs($num) % 100;
|
||||
if ($num > 10 && $num < 20) {
|
||||
return $words[2];
|
||||
} else {
|
||||
$i = $num % 10;
|
||||
if ($i == 1) {
|
||||
return $words[0];
|
||||
} elseif (in_array($i, array(2, 3, 4))) {
|
||||
return $words[1];
|
||||
} else {
|
||||
return $words[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
34
engine/Main/trait.database.php
Normal file
34
engine/Main/trait.database.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
// Работа с базой данных
|
||||
|
||||
defined('ROOT_DIR') || exit;
|
||||
|
||||
require ENGINE_DIR . SLASH . "class.database.php";
|
||||
|
||||
trait Database {
|
||||
private DB $db_class;
|
||||
|
||||
public function db_touch()
|
||||
{
|
||||
$this->db_class = new DB(DB_HOST, DB_NAME, DB_USER, DB_PASS);
|
||||
$this->db_class->connect();
|
||||
}
|
||||
|
||||
// Запрос на получение информации из базы
|
||||
public function db_query($query, $params = array(), $new = false)
|
||||
{
|
||||
$ls_key = $query . json_encode($params);
|
||||
if(!$new && $this->ls_has_key($ls_key))
|
||||
return $this->ls_get_key($ls_key);
|
||||
$result = $this->db_class->query($query, $params);
|
||||
$this->ls_set_key($ls_key, $result);
|
||||
return $result;
|
||||
}
|
||||
|
||||
// Запрос на сохранение данных в базу
|
||||
public function db_insert($query, $params)
|
||||
{
|
||||
return $this->db_class->insert($query, $params);
|
||||
}
|
||||
}
|
||||
54
engine/Main/trait.datatypes.php
Normal file
54
engine/Main/trait.datatypes.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
// Работа с типами данных
|
||||
|
||||
defined('ROOT_DIR') || exit;
|
||||
|
||||
trait Datatypes {
|
||||
public array $datatypes = array();
|
||||
|
||||
// Загрузка типов данных
|
||||
public function datatype_load(): bool
|
||||
{
|
||||
$datatypes_dir = PLAYAREA_DIR_NAME . SLASH . "datatypes";
|
||||
$datatypes = scandir($datatypes_dir, SCANDIR_SORT_DESCENDING);
|
||||
if(!count($datatypes)) return false;
|
||||
foreach ($datatypes as $key => $value) {
|
||||
$name = $this->datatype_convert_name($value);
|
||||
if(!$name) continue;
|
||||
require_once $datatypes_dir . SLASH . $value;
|
||||
$classes = get_declared_classes();
|
||||
$class_name = end($classes);
|
||||
$this->datatypes[] = $class_name;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private function datatype_convert_name($name): string
|
||||
{
|
||||
$parts = explode('.', $name);
|
||||
if($parts[0] != "datatype" || !isset($parts[1])) return false;
|
||||
return trim($parts[1]);
|
||||
}
|
||||
|
||||
// Получить Объект по ID
|
||||
public function get_item_by_id($item_id)
|
||||
{
|
||||
$search = new Search(array(
|
||||
"limit" => 1,
|
||||
"terms" => array("item_id" => $item_id)
|
||||
));
|
||||
$items = $search->collect();
|
||||
if(!count($items)) return false;
|
||||
list($item) = $items;
|
||||
return $item;
|
||||
}
|
||||
|
||||
// Получить Class объекта по ID
|
||||
public function get_item_class_by_id($item_id)
|
||||
{
|
||||
$item = $this->get_item_by_id($item_id);
|
||||
if($item === false) return false;
|
||||
return $item->get_class_name();
|
||||
}
|
||||
}
|
||||
90
engine/Main/trait.events.php
Normal file
90
engine/Main/trait.events.php
Normal file
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
// Работа с отслеживанием данных
|
||||
|
||||
defined('ROOT_DIR') || exit;
|
||||
|
||||
trait Events {
|
||||
private array $events = array();
|
||||
|
||||
// Добавить функцию в Событие
|
||||
public function event_add($name, $function_name, $weight = 10, $lock = false): bool
|
||||
{
|
||||
$event_name = strtolower($name);
|
||||
$this->event_create($name);
|
||||
|
||||
if($this->events[$event_name]["lock"]) return false;
|
||||
if($lock) $this->events[$event_name]["events"] = array();
|
||||
|
||||
$this->events[$event_name]["events"][] = array("function_name" => $function_name, "weight" => $weight);
|
||||
$this->events[$event_name]["lock"] = $lock;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Запустить Событие
|
||||
public function event_start($name, $args = array())
|
||||
{
|
||||
$event_name = strtolower($name);
|
||||
$this->event_create($name);
|
||||
|
||||
if(!is_array($this->events[$event_name])) return false;
|
||||
$events = $this->events[$event_name]["events"];
|
||||
|
||||
if($this->events[$event_name]["lock"]) {
|
||||
$func_name = $events[0]["function_name"];
|
||||
if(is_callable($func_name)) {
|
||||
return $func_name(...$args);
|
||||
}
|
||||
if(!function_exists($func_name)) return false;
|
||||
return $func_name(...$args);
|
||||
}
|
||||
|
||||
$results = array();
|
||||
foreach ($events as $key => $event) {
|
||||
$func_name = $event["function_name"];
|
||||
if(is_callable($func_name)) {
|
||||
$results[] = $func_name(...$args);
|
||||
} else {
|
||||
if(!function_exists($func_name)) return false;
|
||||
$results[] = $func_name(...$args);
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
// Поймать Событие
|
||||
public function event_form_capture(): bool
|
||||
{
|
||||
$values = array_replace_recursive($_GET, $_POST);
|
||||
foreach ($_FILES as $key => $field) {
|
||||
$file = $this->files_field_formate($key);
|
||||
if($file === false) continue;
|
||||
if(isset($values[$key])) $values[$key] = array_replace_recursive($values[$key], $file);
|
||||
else $values[$key] = $file;
|
||||
}
|
||||
if(!isset($values["b_event"])) return false;
|
||||
$this->event_start($values["b_event"], array($values));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Создать Событие для формы
|
||||
public function event_form($name)
|
||||
{
|
||||
$meta_dom = new DOM("input", false);
|
||||
$meta_dom->setAttribute("type", "hidden");
|
||||
$meta_dom->setAttribute("name", "b_event");
|
||||
$meta_dom->setAttribute("value", $name);
|
||||
echo $meta_dom->render();
|
||||
}
|
||||
|
||||
// Создать Событие
|
||||
private function event_create($name): bool
|
||||
{
|
||||
if(isset($this->events[$name])) return true;
|
||||
$this->events[$name] = array();
|
||||
$this->events[$name]["events"] = array();
|
||||
return true;
|
||||
|
||||
}
|
||||
}
|
||||
48
engine/Main/trait.fields.php
Normal file
48
engine/Main/trait.fields.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
// Работа с полями для данных
|
||||
|
||||
defined('ROOT_DIR') || exit;
|
||||
|
||||
trait Fields {
|
||||
public array $fields = array();
|
||||
|
||||
public function field_register($key, $field): bool
|
||||
{
|
||||
$this->fields[$key] = $field;
|
||||
return true;
|
||||
}
|
||||
|
||||
public function field_render_edit($key, $name, $content = "")
|
||||
{
|
||||
$field = $this->field_get($key);
|
||||
if(!$field) return "";
|
||||
$field->set_name($name);
|
||||
$field->set_content($content);
|
||||
$field->render_edit();
|
||||
}
|
||||
|
||||
public function field_render_value($key, $name, $content = "")
|
||||
{
|
||||
$field = $this->field_get($key);
|
||||
if(!$field) return "";
|
||||
$field->set_name($name);
|
||||
$field->set_content($content);
|
||||
return $field->render_value();
|
||||
}
|
||||
|
||||
public function field_render_db_value($key, $name, $value = "", $old_value = "")
|
||||
{
|
||||
$field = $this->field_get($key);
|
||||
if(!$field) return "";
|
||||
$field->set_name($name);
|
||||
$field->set_content($value);
|
||||
return $field->render_db_value($old_value);
|
||||
}
|
||||
|
||||
public function field_get($name)
|
||||
{
|
||||
if(!isset($this->fields[$name])) return false;
|
||||
return $this->fields[$name];
|
||||
}
|
||||
}
|
||||
121
engine/Main/trait.files.php
Normal file
121
engine/Main/trait.files.php
Normal file
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
|
||||
// Работа с файлами
|
||||
|
||||
defined('ROOT_DIR') || exit;
|
||||
|
||||
trait Files {
|
||||
// Получить файл по ID
|
||||
public function file_get($file_id)
|
||||
{
|
||||
$keys = $this->db_query("SELECT * FROM `bive_files` WHERE `id` = ?", array($file_id), true);
|
||||
if(!count($keys)) return false;
|
||||
return $keys[0];
|
||||
}
|
||||
|
||||
// Удалить файл по его ID
|
||||
public function file_delete($file_id): bool
|
||||
{
|
||||
$db_file = $this->file_get($file_id);
|
||||
if(!$db_file) return false;
|
||||
$file_path = ROOT_DIR . SLASH . STORAGE_DIR_NAME . "/files/" . $db_file["file_name"];
|
||||
unlink($file_path);
|
||||
$this->db_query("DELETE FROM `bive_files` WHERE `id` = ?", array($file_id), true);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Проверить есть ли файл по его ID
|
||||
public function file_has($file_id): bool
|
||||
{
|
||||
$db_file = $this->file_get($file_id);
|
||||
if($db_file === false) return false;
|
||||
$file_path = ROOT_DIR . SLASH . STORAGE_DIR_NAME . "/files/" . $db_file["file_name"];
|
||||
return file_exists($file_path);
|
||||
}
|
||||
|
||||
// Получить ссылку на файл по ID
|
||||
public function file_get_link($file_id)
|
||||
{
|
||||
$db_file = $this->file_get($file_id);
|
||||
if($db_file === false) return false;
|
||||
return $this->router_get_root_uri() . "/" . STORAGE_DIR_NAME . "/files/" . $db_file["file_name"];
|
||||
}
|
||||
|
||||
// Сохранить файл и вернуть его ID в базе
|
||||
public function files_save($file, $max_size = 5242880, $file_types = array("jpeg", "jpg", "png", "svg", "webp"))
|
||||
{
|
||||
if(!isset($file) || $file["error"] != 0) return false;
|
||||
|
||||
$storage = $this->get_storage_dir();
|
||||
$file_name = $file["name"];
|
||||
$file_size = $file["size"];
|
||||
$file_type = strtolower(pathinfo(SLASH . $file_name, PATHINFO_EXTENSION));
|
||||
|
||||
if ($file_size > $max_size) {
|
||||
$this->alerts_add("Размер файла превышает минимально допустимый.", "error", "file");
|
||||
return false;
|
||||
}
|
||||
|
||||
$db_name = $this->files_create_name() . "." . $file_type;
|
||||
|
||||
$targetDir = $storage . SLASH . "files" . SLASH; // Директория для сохранения загруженных файлов
|
||||
$targetFile = $targetDir . basename($db_name); // Полный путь до файла
|
||||
|
||||
if(!in_array($file_type, $file_types)) {
|
||||
$this->alerts_add("Недопустимое расширение файла.", "error", "file");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (move_uploaded_file($file["tmp_name"], $targetFile)) {
|
||||
return $this->files_db_add($db_name, $file_size, $file_type, $targetFile);
|
||||
} else {
|
||||
$this->alerts_add("Произошла ошибка при загрузке файла.", "error", "file");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Сохранить файлы по имени поля
|
||||
public function files_save_by_name($name, $max_size = 5242880, $file_types = array("jpeg", "jpg", "png", "svg", "webp"))
|
||||
{
|
||||
if(!isset($_FILES[$name]) || $_FILES[$name]["error"] != 0) return false;
|
||||
return $this->files_save($_FILES[$name], $max_size, $file_types);
|
||||
}
|
||||
|
||||
// Добавление файла в базу данных
|
||||
public function files_db_add($file_name, $weight, $mime_type, $path)
|
||||
{
|
||||
global $b;
|
||||
return $b->db_insert("INSERT INTO `bive_files`(`path`, `mime_type`, `weight`, `file_name`) VALUES (?, ?, ?, ?)", array($path, $mime_type, $weight, $file_name));
|
||||
}
|
||||
|
||||
// Форматирование файлов под поле
|
||||
public function files_field_formate($name)
|
||||
{
|
||||
$file = $_FILES[$name];
|
||||
if(!isset($file)) return false;
|
||||
if(is_string($file["name"])) return $file;
|
||||
|
||||
$fields = array();
|
||||
$this->files_field_formate_recurse($file, $fields);
|
||||
return $fields;
|
||||
}
|
||||
|
||||
// Рекурсивная функция форматирования файлов
|
||||
private function files_field_formate_recurse($files_array, &$fields, $prop = null)
|
||||
{
|
||||
foreach ($files_array as $prop_key => $prop_value) {
|
||||
if(is_array($prop_value)){
|
||||
if($prop) $this->files_field_formate_recurse($prop_value,$fields[$prop_key], $prop);
|
||||
else $this->files_field_formate_recurse($prop_value,$fields, $prop_key);
|
||||
} else {
|
||||
$fields[$prop_key][$prop] = $prop_value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Рандомное название для файла
|
||||
public function files_create_name()
|
||||
{
|
||||
return bin2hex(random_bytes(16));
|
||||
}
|
||||
}
|
||||
41
engine/Main/trait.localstorage.php
Normal file
41
engine/Main/trait.localstorage.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
defined('ROOT_DIR') || exit;
|
||||
|
||||
// Локальное хранилище
|
||||
// Служит для хранения информации в рамках одного запроса
|
||||
|
||||
trait LocalStorage {
|
||||
private array $key_value = array();
|
||||
|
||||
// Получить значение по ключу
|
||||
public function ls_get_key($key)
|
||||
{
|
||||
return $this->key_value[md5($key)];
|
||||
}
|
||||
|
||||
// Записать значение по ключу
|
||||
public function ls_set_key($key, $value)
|
||||
{
|
||||
return $this->key_value[md5($key)] = $value;
|
||||
}
|
||||
|
||||
// Есть ли ключ
|
||||
public function ls_has_key($key): bool
|
||||
{
|
||||
return isset($this->key_value[md5($key)]);
|
||||
}
|
||||
|
||||
// Получить длину локального хранилища
|
||||
public function ls_get_size(): int
|
||||
{
|
||||
return count($this->key_value);
|
||||
}
|
||||
|
||||
// Удалить ключ
|
||||
public function ls_remove_key($key): bool
|
||||
{
|
||||
unlink($this->key_value[md5($key)]);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
39
engine/Main/trait.paths.php
Normal file
39
engine/Main/trait.paths.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
// Работа с путями
|
||||
|
||||
defined('ROOT_DIR') || exit;
|
||||
|
||||
trait Path
|
||||
{
|
||||
// Получить директорию движка
|
||||
public function get_engine_path(): string
|
||||
{
|
||||
$r = $this->get_root_dir();
|
||||
return $r . "/" .ENGINE_DIR_NAME;
|
||||
}
|
||||
|
||||
// Получить корневую директорию
|
||||
function get_root_dir(): string
|
||||
{
|
||||
return ROOT_DIR;
|
||||
}
|
||||
|
||||
// Получить директорию хранилища
|
||||
function get_storage_dir(): string
|
||||
{
|
||||
return ROOT_DIR . SLASH . STORAGE_DIR_NAME;
|
||||
}
|
||||
|
||||
// Получить директорию пользовательского контента
|
||||
function get_playarea_dir(): string
|
||||
{
|
||||
return ROOT_DIR . SLASH . PLAYAREA_DIR_NAME;
|
||||
}
|
||||
|
||||
// Получить директорию со скриптами
|
||||
function get_scripts_dir(): string
|
||||
{
|
||||
return $this->get_playarea_dir() . SLASH . "scripts";
|
||||
}
|
||||
}
|
||||
17
engine/Main/trait.playarea.php
Normal file
17
engine/Main/trait.playarea.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
// Работа с песочницей
|
||||
|
||||
defined('ROOT_DIR') || exit;
|
||||
|
||||
trait PlayArea {
|
||||
public function pa_connect_main(){
|
||||
require_once $this->get_playarea_dir() . SLASH . "main.php";
|
||||
}
|
||||
|
||||
// Получить папку с ассетами
|
||||
public function pa_get_assets_dir(){
|
||||
$root = $this->router_get_root_uri();
|
||||
return $root . SLASH . PLAYAREA_DIR_NAME . SLASH . 'assets' . SLASH;
|
||||
}
|
||||
}
|
||||
90
engine/Main/trait.plugins.php
Normal file
90
engine/Main/trait.plugins.php
Normal file
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
// Работа с плагинами
|
||||
|
||||
defined('ROOT_DIR') || exit;
|
||||
|
||||
trait Plugins {
|
||||
public array $plugins = array();
|
||||
|
||||
// Загрузка всех плагинов системы
|
||||
public function plugins_load(): bool
|
||||
{
|
||||
$plugins_dir = $this->plugins_get_dir();
|
||||
$plugins = scandir($plugins_dir, SCANDIR_SORT_DESCENDING);
|
||||
if(!count($plugins)) return false;
|
||||
foreach ($plugins as $key => $plugin_dir)
|
||||
$this->plugin_load($plugin_dir);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Загрузка плагина
|
||||
public function plugin_load($plugin_dir){
|
||||
global $b;
|
||||
if($this->plugins[$plugin_dir]) return;
|
||||
$plugins_dir = $this->plugins_get_dir();
|
||||
$plugin_path = $plugins_dir . SLASH . $plugin_dir . SLASH;
|
||||
$plugin_manifest_path = $plugin_path . "manifest.yaml";
|
||||
|
||||
// Проверка наличия manifest.yaml
|
||||
if(!file_exists($plugin_manifest_path)) return;
|
||||
$manifest = $this->yaml_read($plugin_manifest_path);
|
||||
$plugin_script_path = $plugin_path . $manifest["plugin_script"];
|
||||
|
||||
// Подгрузка зависимостей
|
||||
if(isset($manifest["plugin_dependencies"])) {
|
||||
foreach ($manifest["plugin_dependencies"] as $key => $plugin_dependence) {
|
||||
$this->plugin_load($plugin_dependence);
|
||||
}
|
||||
}
|
||||
|
||||
// Проверка наличия исполняемого скрипта
|
||||
if(!file_exists($plugin_script_path)) return;
|
||||
$plugin_class = $this->plugin_register($manifest, $plugin_dir);
|
||||
$this->ls_set_key("plugin", $plugin_class);
|
||||
require_once $plugin_script_path;
|
||||
}
|
||||
|
||||
// Получить текущий плагин
|
||||
public function plugin_get()
|
||||
{
|
||||
return $this->ls_get_key("plugin");
|
||||
}
|
||||
|
||||
// Регистрация плагина
|
||||
public function plugin_register($manifest, $plugin_dir)
|
||||
{
|
||||
$plugin_class = $this->plugin_create_class($manifest, $plugin_dir);
|
||||
$this->plugins[$plugin_dir] = array(
|
||||
"plugin_name" => $manifest["plugin_name"],
|
||||
"plugin_description" => $manifest["plugin_description"],
|
||||
"plugin_author" => $manifest["plugin_author"],
|
||||
"plugin_version" => $manifest["plugin_version"],
|
||||
"class" => $plugin_class
|
||||
);
|
||||
return $plugin_class;
|
||||
}
|
||||
|
||||
// Функция создания класса плагина
|
||||
public function plugin_create_class($manifest, $plugin_dir)
|
||||
{
|
||||
$plugin_class_name = $manifest["plugin_class"] ?? "Plugin";
|
||||
return new $plugin_class_name($plugin_dir);
|
||||
}
|
||||
|
||||
// Получить папку где хранятся плагины
|
||||
public function plugins_get_dir(): string
|
||||
{
|
||||
return ROOT_DIR . SLASH . PLAYAREA_DIR_NAME . SLASH . "plugins";
|
||||
}
|
||||
|
||||
// Прочитать конфигурацию YAML
|
||||
public function yaml_read($filePath)
|
||||
{
|
||||
if (!extension_loaded('yaml'))
|
||||
die('Расширение YAML не установлено.');
|
||||
|
||||
$ymlContent = file_get_contents($filePath);
|
||||
return yaml_parse($ymlContent);
|
||||
}
|
||||
}
|
||||
194
engine/Main/trait.router.php
Normal file
194
engine/Main/trait.router.php
Normal file
@@ -0,0 +1,194 @@
|
||||
<?php
|
||||
|
||||
// Работа с адресной строкой
|
||||
|
||||
defined('ROOT_DIR') || exit;
|
||||
|
||||
trait Router {
|
||||
private array $routerList = array();
|
||||
private $fun_not_found;
|
||||
|
||||
// Получить адрес
|
||||
public function router_get_uri()
|
||||
{
|
||||
return parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
|
||||
}
|
||||
|
||||
// Регистрация роутера
|
||||
public function router_add($page, $func, $methods = array("GET", "POST"))
|
||||
{
|
||||
$type = 'function';
|
||||
if(gettype($func) == 'string') {
|
||||
if(substr($func, strlen($func) - strlen(".php")) == ".php") $type = 'template';
|
||||
else $type = 'function_name';
|
||||
}
|
||||
$this->routerList[] = array(
|
||||
"page" => $page,
|
||||
"func" => &$func,
|
||||
"type" => $type,
|
||||
"methods" => $methods
|
||||
);
|
||||
}
|
||||
|
||||
// Получение списка роутов
|
||||
private function router_get_list(): array
|
||||
{
|
||||
return $this->routerList;
|
||||
}
|
||||
|
||||
// Получить сегменты адреса
|
||||
public function router_get_segments()
|
||||
{
|
||||
$uri = $this->router_get_uri();
|
||||
return explode('/', trim($uri, '/'));
|
||||
}
|
||||
|
||||
// Получить сегмент адреса
|
||||
public function router_get_segment($segment_num)
|
||||
{
|
||||
$segments = $this->router_get_segments();
|
||||
return $segments[$segment_num];
|
||||
}
|
||||
|
||||
// Определить функцию не найденной страницы
|
||||
public function router_set_not_found($fun)
|
||||
{
|
||||
$this->fun_not_found = &$fun;
|
||||
}
|
||||
|
||||
// Когда страница не найдена
|
||||
private function router_not_found()
|
||||
{
|
||||
http_response_code(404);
|
||||
if(empty($this->fun_not_found))
|
||||
echo "not found";
|
||||
$fun = $this->fun_not_found;
|
||||
$fun();
|
||||
}
|
||||
|
||||
// Запускаем сканирование
|
||||
public function router_init()
|
||||
{
|
||||
$uri = $this->router_get_uri();
|
||||
$list = $this->router_get_list();
|
||||
$method = $this->router_get_method();
|
||||
foreach ($list as $key => $item) {
|
||||
if(!in_array($method, $item["methods"], true)) continue;
|
||||
if(substr($item["page"], -1) == "%"){
|
||||
$clear_uri = substr($item["page"], 0, -1);
|
||||
if(strpos($uri, $clear_uri) === 0) {
|
||||
$this->event_start(base64_encode($item["page"]));
|
||||
if($item["type"] == "template")
|
||||
return $this->template_load($item["func"], array(), true);
|
||||
return $item["func"]();
|
||||
}
|
||||
} else if ($uri == $item["page"]) {
|
||||
$this->event_start(base64_encode($item["page"]));
|
||||
if($item["type"] == "template")
|
||||
return $this->template_load($item["func"], array(), true);
|
||||
return $item["func"]();
|
||||
}
|
||||
}
|
||||
$this->router_not_found();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Получить метод запроса
|
||||
public function router_get_method(): string
|
||||
{
|
||||
return strtolower($_SERVER['REQUEST_METHOD']);
|
||||
}
|
||||
|
||||
// Получить текущий протокол
|
||||
public function router_get_protocol(): string
|
||||
{
|
||||
if (
|
||||
isset($_SERVER['REQUEST_SCHEME']) &&
|
||||
($_SERVER['HTTPS'] == 'on' || $_SERVER['HTTPS'] == 1) ||
|
||||
isset($_SERVER['HTTP_X_FORWARDED_PROTO']) &&
|
||||
$_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https' ||
|
||||
isset($_SERVER['REQUEST_SCHEME']) &&
|
||||
$_SERVER['REQUEST_SCHEME'] == "https"
|
||||
) return "https";
|
||||
return "http";
|
||||
}
|
||||
|
||||
// Получить корневой адрес сайта
|
||||
public function router_get_root_uri(): string
|
||||
{
|
||||
$protocol = $this->router_get_protocol();
|
||||
return $protocol . "://" . $_SERVER['HTTP_HOST'];
|
||||
}
|
||||
|
||||
// Получить канонический адрес
|
||||
public function router_get_canonical_uri(): string
|
||||
{
|
||||
$canonical_link = parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH);
|
||||
return $this->router_get_root_uri() . $canonical_link;
|
||||
}
|
||||
|
||||
// Получить полный адрес
|
||||
public function router_get_full_uri(): string
|
||||
{
|
||||
return ((!empty($_SERVER['HTTPS'])) ? 'https' : 'http') . '://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
|
||||
}
|
||||
|
||||
// Выполнить редирект на другой адрес
|
||||
public function router_redirect($path)
|
||||
{
|
||||
$root = $this->router_get_root_uri();
|
||||
header("Location: $root$path");
|
||||
}
|
||||
|
||||
// Выполнить редирект на эту же страницу чтобы очистить POST
|
||||
public function router_refresh()
|
||||
{
|
||||
$current_path = $_SERVER['REQUEST_URI'];
|
||||
$this->router_redirect($current_path);
|
||||
}
|
||||
|
||||
// Получить ссылку с измененными GET параметрами
|
||||
public function router_format_get_params($params, $split = false, $exclude = array()): string
|
||||
{
|
||||
$string = "?";
|
||||
$params_list = array();
|
||||
$params = $this->router_edit_get_params($params, $split, $exclude);
|
||||
foreach ($params as $key => $value)
|
||||
$params_list[] = $key . "=" . $value;
|
||||
return $string . implode("&", $params_list);
|
||||
}
|
||||
|
||||
// Соединить GET параметры со значениями массива
|
||||
public function router_edit_get_params($params, $split = false, $exclude = array())
|
||||
{
|
||||
$current_params = $_GET;
|
||||
if($split) $params = array_merge($current_params, $params);
|
||||
foreach ($exclude as $key => $value) unset($params[$value]);
|
||||
return $params;
|
||||
}
|
||||
|
||||
// Заполнить форму невидимыми полями по переданному массиву
|
||||
public function router_params_to_form($params, $exclude = array(), $prefix = "", $nested = false)
|
||||
{
|
||||
foreach ($params as $key => $value) {
|
||||
if(in_array($key, $exclude)) continue;
|
||||
if(is_array($value)) {
|
||||
if($nested) $this->router_params_to_form($value, $exclude = array(), $prefix . "[". $key . "]", true);
|
||||
else $this->router_params_to_form($value, $exclude = array(), $prefix . $key, true);
|
||||
continue;
|
||||
}
|
||||
if($nested) $this->router_get_hidden_input($prefix . "[" . $key . "]", $value, $prefix);
|
||||
else $this->router_get_hidden_input($prefix . $key, $value, $prefix);
|
||||
}
|
||||
}
|
||||
|
||||
// Добавить невидимое поле на страницу
|
||||
private function router_get_hidden_input($name, $value, $prefix = "")
|
||||
{
|
||||
$meta_dom = new DOM("input", false);
|
||||
$meta_dom->setAttribute("type", "hidden");
|
||||
$meta_dom->setAttribute("name", $prefix . $this->get_view($name));
|
||||
$meta_dom->setAttribute("value", $this->get_view($value));
|
||||
$meta_dom->view();
|
||||
}
|
||||
}
|
||||
43
engine/Main/trait.sessions.php
Normal file
43
engine/Main/trait.sessions.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
// Работа с сессиями
|
||||
|
||||
defined('ROOT_DIR') || exit;
|
||||
|
||||
trait Sessions {
|
||||
// Создать сессию для посетителя
|
||||
public function session_create()
|
||||
{
|
||||
if(isset($_COOKIE["b_session"])) return true;
|
||||
$session_id = bin2hex(random_bytes(16));
|
||||
setcookie('b_session', $session_id, time() + (30 * 24 * 60 * 60), '/', '', SESSION_SECURE, true);
|
||||
$_COOKIE["b_session"] = $session_id;
|
||||
return $session_id;
|
||||
}
|
||||
|
||||
// Получить значение сессии
|
||||
public function session_get_name()
|
||||
{
|
||||
$session_name = $_COOKIE["b_session"];
|
||||
if(!isset($session_name)) return $this->session_create();
|
||||
return $session_name;
|
||||
}
|
||||
|
||||
// Получить значение сессии
|
||||
public function session_get($key)
|
||||
{
|
||||
$session_name = $this->session_get_name();
|
||||
$keys = $this->db_query("SELECT * FROM `bive_session` WHERE `session` = ? AND `key` = ?", array($session_name, $key), true);
|
||||
if(!isset($keys[0])) return false;
|
||||
return $keys[0]["value"];
|
||||
}
|
||||
|
||||
// Сохранить значение сессии
|
||||
public function session_set($key, $value)
|
||||
{
|
||||
$session_name = $this->session_get_name();
|
||||
$old_value = $this->session_get($key);
|
||||
if($old_value === false) return $this->db_query("INSERT INTO `bive_session`(`session`, `key`, `value`) VALUES (?, ?, ?)", array($session_name, $key, $value), true);
|
||||
return $this->db_query("UPDATE `bive_session` SET `value` = ? WHERE `session` = ? AND `key` = ?", array($value, $session_name, $key), true);
|
||||
}
|
||||
}
|
||||
63
engine/Main/trait.settings.php
Normal file
63
engine/Main/trait.settings.php
Normal file
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
// Работа с настройками BiveEngine
|
||||
|
||||
defined('ROOT_DIR') || exit;
|
||||
|
||||
trait Settings {
|
||||
public array $setting_list = array();
|
||||
|
||||
public function setting_register($setting_key, $title, $field_key): string
|
||||
{
|
||||
$this->setting_list[$setting_key] = array("title" => $title, "field_key" => $field_key);
|
||||
return $setting_key;
|
||||
}
|
||||
|
||||
public function setting_get($setting_key)
|
||||
{
|
||||
$keys = $this->db_query("SELECT `value` FROM `bive_settings` WHERE `setting_key` = ?", array($setting_key), true);
|
||||
if(!isset($keys[0])) return false;
|
||||
return $keys[0]["value"];
|
||||
}
|
||||
|
||||
public function setting_get_value($setting_key)
|
||||
{
|
||||
$value = $this->setting_get($setting_key);
|
||||
if($value === false) return false;
|
||||
|
||||
$field_key = $this->setting_get_field($setting_key);
|
||||
if($field_key === false) return false;
|
||||
|
||||
return $this->field_render_value($field_key, $setting_key, $value);
|
||||
}
|
||||
|
||||
public function setting_get_field($setting_key)
|
||||
{
|
||||
if(!isset($this->setting_list[$setting_key])) return false;
|
||||
return $this->setting_list[$setting_key]["field_key"];
|
||||
}
|
||||
|
||||
public function setting_get_title($setting_key)
|
||||
{
|
||||
if(!isset($this->setting_list[$setting_key])) return false;
|
||||
return $this->setting_list[$setting_key]["title"];
|
||||
}
|
||||
|
||||
public function setting_set($setting_key, $value): bool
|
||||
{
|
||||
$field_key = $this->setting_get_field($setting_key);
|
||||
if(!$field_key) return false;
|
||||
|
||||
$old_value = $this->setting_get($setting_key);
|
||||
$value = $this->field_render_db_value($field_key, $setting_key, $value, $old_value);
|
||||
|
||||
if($old_value === false) return $this->setting_add($setting_key, $value);
|
||||
$this->db_query("UPDATE `bive_settings` SET `value` = ? WHERE `setting_key` = ?", array($value, $setting_key), true);
|
||||
return true;
|
||||
}
|
||||
|
||||
public function setting_add($setting_key, $value)
|
||||
{
|
||||
return $this->db_insert("INSERT INTO `bive_settings`(`setting_key`, `value`) VALUES (?, ?)", array($setting_key, $value), true);
|
||||
}
|
||||
}
|
||||
119
engine/Main/trait.templates.php
Normal file
119
engine/Main/trait.templates.php
Normal file
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
// Работа с шаблонами
|
||||
|
||||
defined('ROOT_DIR') || exit;
|
||||
|
||||
trait Templates {
|
||||
// Загрузка шаблона
|
||||
public function template_load($name, $variables = array(), $new = true)
|
||||
{
|
||||
$template_path = $this->template_touch_name($name);
|
||||
return $this->template_load_by_path($template_path, $variables, $new);
|
||||
}
|
||||
|
||||
private function template_touch_name($name): string
|
||||
{
|
||||
$template_path = ROOT_DIR . SLASH . PLAYAREA_DIR_NAME . SLASH . "templates" . SLASH . $name;
|
||||
if(file_exists($template_path)) return $template_path;
|
||||
$plugin_template_path = ROOT_DIR . SLASH . PLAYAREA_DIR_NAME . SLASH . "plugins" . SLASH . $name;
|
||||
if(file_exists($plugin_template_path)) return $plugin_template_path;
|
||||
return $template_path;
|
||||
}
|
||||
|
||||
// Загрузка шаблона по его адресу
|
||||
public function template_load_by_path($path, $variables = array(), $new = true)
|
||||
{
|
||||
// Загрузка шаблона из буффера
|
||||
$cache = $this->template_cache_load($path, $variables, $new);
|
||||
if($cache) return $cache;
|
||||
|
||||
// Начало буффера
|
||||
ob_start();
|
||||
global $b;
|
||||
require $path;
|
||||
$buffer = ob_get_contents();
|
||||
ob_end_clean();
|
||||
// Конец буффера
|
||||
|
||||
// Сохранение буффера в кеш
|
||||
$rendered = $this->template_variables_apply($buffer, $variables);
|
||||
if($new === false) $this->template_cache_save($path, $variables, $rendered);
|
||||
|
||||
// Вывод буффера пользователю
|
||||
echo $rendered;
|
||||
return $buffer;
|
||||
}
|
||||
|
||||
// Применить значения переменных
|
||||
private function template_variables_apply($content, $variables)
|
||||
{
|
||||
return preg_replace_callback('/{{\s*(\w+)\s*}}/', function($match) use ($variables) {
|
||||
$key = $match[1];
|
||||
return $variables[$key] ?? $match[0];
|
||||
}, $content);
|
||||
}
|
||||
|
||||
// Сохранить шаблон
|
||||
private function template_cache_save($name, $variables, $content): bool
|
||||
{
|
||||
if(!TEMPLATE_CACHE) return false;
|
||||
$path = $this->template_cache_get_full_path($name, $variables);
|
||||
file_put_contents($path, $content);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Есть ли шаблон с таким именем
|
||||
private function template_cache_has($name, $variables): bool
|
||||
{
|
||||
$path = $this->template_cache_get_full_path($name, $variables);
|
||||
return file_exists($path);
|
||||
}
|
||||
|
||||
// Загрузка кешированного шаблона
|
||||
private function template_cache_load($name, $variables, $new = false)
|
||||
{
|
||||
if(!TEMPLATE_CACHE) return false;
|
||||
if(!$new && $this->template_cache_has($name, $variables)) {
|
||||
$cache = $this->template_cache_get($name, $variables);
|
||||
echo $cache;
|
||||
return $cache;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Получить кеш шаблона
|
||||
private function template_cache_get($name, $variables): string
|
||||
{
|
||||
$path = $this->template_cache_get_full_path($name, $variables);
|
||||
return file_get_contents($path);
|
||||
}
|
||||
|
||||
// Получить полный путь до кеша шаблона
|
||||
private function template_cache_get_full_path($name, $variables): string
|
||||
{
|
||||
$path = $this->get_storage_dir() . SLASH . "temp" . SLASH . "templates" . SLASH;
|
||||
return $path . md5($name . json_encode($variables)) . ".php";
|
||||
}
|
||||
|
||||
// Количество кешированных шаблонов
|
||||
public function template_cache_count(): int
|
||||
{
|
||||
$path = $this->get_storage_dir() . SLASH . "temp" . SLASH . "templates" . SLASH;
|
||||
$files = glob($path."/*");
|
||||
return count($files);
|
||||
}
|
||||
|
||||
// Удалить весь кеш шаблонов
|
||||
public function template_cache_remove(){
|
||||
$path = $this->get_storage_dir() . SLASH . "temp" . SLASH . "templates" . SLASH;
|
||||
$files = glob($path."/*");
|
||||
if (count($files) > 0) {
|
||||
foreach ($files as $file) {
|
||||
if (file_exists($file)) {
|
||||
unlink($file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
39
engine/class.bive.php
Normal file
39
engine/class.bive.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
defined('ROOT_DIR') || exit;
|
||||
|
||||
require ENGINE_DIR . SLASH . "Main" . SLASH . "trait.router.php";
|
||||
require ENGINE_DIR . SLASH . "Main" . SLASH . "trait.content.php";
|
||||
require ENGINE_DIR . SLASH . "Main" . SLASH . "trait.paths.php";
|
||||
require ENGINE_DIR . SLASH . "Main" . SLASH . "trait.database.php";
|
||||
require ENGINE_DIR . SLASH . "Main" . SLASH . "trait.localstorage.php";
|
||||
require ENGINE_DIR . SLASH . "Main" . SLASH . "trait.templates.php";
|
||||
require ENGINE_DIR . SLASH . "Main" . SLASH . "trait.playarea.php";
|
||||
require ENGINE_DIR . SLASH . "Main" . SLASH . "trait.datatypes.php";
|
||||
require ENGINE_DIR . SLASH . "Main" . SLASH . "trait.events.php";
|
||||
require ENGINE_DIR . SLASH . "Main" . SLASH . "trait.sessions.php";
|
||||
require ENGINE_DIR . SLASH . "Main" . SLASH . "trait.admin.php";
|
||||
require ENGINE_DIR . SLASH . "Main" . SLASH . "trait.alerts.php";
|
||||
require ENGINE_DIR . SLASH . "Main" . SLASH . "trait.fields.php";
|
||||
require ENGINE_DIR . SLASH . "Main" . SLASH . "trait.settings.php";
|
||||
require ENGINE_DIR . SLASH . "Main" . SLASH . "trait.files.php";
|
||||
require ENGINE_DIR . SLASH . "Main" . SLASH . "trait.plugins.php";
|
||||
|
||||
class Bive {
|
||||
use Router;
|
||||
use Content;
|
||||
use Path;
|
||||
use Database;
|
||||
use LocalStorage;
|
||||
use Templates;
|
||||
use PlayArea;
|
||||
use Datatypes;
|
||||
use Events;
|
||||
use Sessions;
|
||||
use Admin;
|
||||
use Alerts;
|
||||
use Fields;
|
||||
use Settings;
|
||||
use Files;
|
||||
use Plugins;
|
||||
}
|
||||
47
engine/class.database.php
Normal file
47
engine/class.database.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
defined('ROOT_DIR') || exit;
|
||||
|
||||
class DB {
|
||||
public string $db_host;
|
||||
public string $db_name;
|
||||
public string $db_user;
|
||||
public string $db_password;
|
||||
|
||||
private PDO $dbh;
|
||||
|
||||
public function __construct($db_host, $db_name, $db_user, $db_password)
|
||||
{
|
||||
$this->db_host = $db_host;
|
||||
$this->db_name = $db_name;
|
||||
$this->db_user = $db_user;
|
||||
$this->db_password = $db_password;
|
||||
}
|
||||
|
||||
public function connect(): DB
|
||||
{
|
||||
$this->dbh = new PDO("mysql:host=". $this->db_host. ";dbname=" . $this->db_name . ";charset=utf8mb4", $this->db_user, $this->db_password);
|
||||
return $this;
|
||||
}
|
||||
|
||||
// Запрос на получение информации из базы
|
||||
public function query($query, $params)
|
||||
{
|
||||
log_message("Начало запроса");
|
||||
log_message($query);
|
||||
log_message(json_encode($params));
|
||||
log_message("Конец запроса");
|
||||
|
||||
$sth = $this->dbh->prepare($query);
|
||||
$sth->execute($params);
|
||||
return $sth->fetchAll();
|
||||
}
|
||||
|
||||
// Запрос на сохранение данных в базу
|
||||
public function insert($query, $params)
|
||||
{
|
||||
$sth = $this->dbh->prepare($query);
|
||||
$sth->execute($params);
|
||||
return $this->dbh->lastInsertId();
|
||||
}
|
||||
}
|
||||
85
engine/class.dom.php
Normal file
85
engine/class.dom.php
Normal file
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
defined('ROOT_DIR') || exit;
|
||||
|
||||
class DOM {
|
||||
private string $tag_name;
|
||||
private bool $end_tag;
|
||||
private array $attributes = array();
|
||||
public array $child_nodes = array();
|
||||
|
||||
public function __construct($tag_name, $end_tag = true)
|
||||
{
|
||||
$this->tag_name = $tag_name ?? "div";
|
||||
$this->end_tag = $end_tag;
|
||||
}
|
||||
|
||||
// Добавить атрибут
|
||||
public function setAttribute($name, $value): bool
|
||||
{
|
||||
$this->attributes[$name] = $value;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Вывести на экран DOM
|
||||
public function view(): bool
|
||||
{
|
||||
echo $this->render();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Отрендерить DOM
|
||||
public function render(): string
|
||||
{
|
||||
$string_dom = "<" . $this->tag_name . $this->render_attributes() . ">";
|
||||
if($this->end_tag) $string_dom .= $this->render_child();
|
||||
if($this->end_tag) $string_dom .= "</". $this->tag_name . ">";
|
||||
return $string_dom;
|
||||
}
|
||||
|
||||
// Отрендерить Атрибуты DOM
|
||||
private function render_attributes(): string
|
||||
{
|
||||
$string_attributes = "";
|
||||
foreach ($this->attributes as $key => $value)
|
||||
$string_attributes .= " $key='$value'";
|
||||
return $string_attributes;
|
||||
}
|
||||
|
||||
// Отрендерить дочерние DOM
|
||||
private function render_child(): string
|
||||
{
|
||||
$string_child = "";
|
||||
foreach ($this->child_nodes as $key => $value) {
|
||||
if(gettype($value) == "string") {
|
||||
$string_child .= $value;
|
||||
continue;
|
||||
}
|
||||
$string_child .= $value->render();
|
||||
}
|
||||
return $string_child;
|
||||
}
|
||||
|
||||
// Добавить дочерний DOM в конец
|
||||
public function append($content): bool
|
||||
{
|
||||
if(gettype($content) != "string" && gettype($content) != "object") return false;
|
||||
$this->child_nodes[] = $content;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Добавить дочерний DOM в начало
|
||||
public function prepend($content): bool
|
||||
{
|
||||
if(gettype($content) != "string" && gettype($content) != "object") return false;
|
||||
array_unshift($this->child_nodes, $content);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Очистить все дочерние DOM
|
||||
public function clear(): bool
|
||||
{
|
||||
$this->child_nodes = array();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
319
engine/class.item.php
Normal file
319
engine/class.item.php
Normal file
@@ -0,0 +1,319 @@
|
||||
<?php
|
||||
|
||||
defined('ROOT_DIR') || exit;
|
||||
|
||||
class Item {
|
||||
public string $item_id;
|
||||
public int $parent_id = 0;
|
||||
public Item $parent;
|
||||
public string $parent_class = "";
|
||||
public array $props;
|
||||
public array $props_values;
|
||||
public string $title = "Объект";
|
||||
public string $description = "Описание объекта не задано.";
|
||||
public bool $admin_menu = true;
|
||||
public bool $visible_content = true;
|
||||
public string $icon = "format_list_bulleted";
|
||||
|
||||
public function __construct($item_id = 0)
|
||||
{
|
||||
$this->item_id = $item_id;
|
||||
}
|
||||
|
||||
// Создать Item
|
||||
public function create($name, $content): bool
|
||||
{
|
||||
global $b;
|
||||
if($this->item_id) return false;
|
||||
if($content === null) $content = "";
|
||||
if($name === null) $name = "";
|
||||
$this->item_id = $b->db_insert("INSERT INTO `bive_items`(`item_name`, `item_content`, `item_class`) VALUES (?, ?, ?)", array($name, $content, $this->get_class_name()));
|
||||
$this->create_props();
|
||||
return true;
|
||||
}
|
||||
|
||||
public function delete(): bool
|
||||
{
|
||||
global $b;
|
||||
if(!$this->item_id) return false;
|
||||
foreach ($this->props as $key => $prop) $prop->delete();
|
||||
$this->clear_parents();
|
||||
$this->clear_childs();
|
||||
$b->db_query("DELETE FROM `bive_items` WHERE `item_id` = ?", array($this->item_id), true);
|
||||
return true;
|
||||
}
|
||||
|
||||
private function create_props(): bool
|
||||
{
|
||||
foreach ($this->props as $key => $prop) $prop->add(null);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Получить Объект
|
||||
public function get_item($new = false)
|
||||
{
|
||||
global $b;
|
||||
$result = $b->db_query("SELECT * FROM `bive_items` WHERE `item_id` = ?", [ $this->item_id ], $new);
|
||||
if(!isset($result[0])) return false;
|
||||
$this->fill_main_parent($result[0]["item_parent"]);
|
||||
return $result[0];
|
||||
}
|
||||
|
||||
// Получить класс Объекта
|
||||
public function get_class_name(): string
|
||||
{
|
||||
return static::class;
|
||||
}
|
||||
|
||||
// Получить ID объекта
|
||||
public function get_item_id($new = false)
|
||||
{
|
||||
return $this->get_field("item_id", $new);
|
||||
}
|
||||
|
||||
// Получить ID объекта (алиас)
|
||||
public function get_id($new = false)
|
||||
{
|
||||
return $this->get_item_id($new);
|
||||
}
|
||||
|
||||
// Получить Имя объекта
|
||||
public function get_item_name($new = false)
|
||||
{
|
||||
return $this->get_field("item_name", $new);
|
||||
}
|
||||
|
||||
// Получить Имя объекта (алиас)
|
||||
public function get_name($new = false)
|
||||
{
|
||||
return $this->get_item_name($new);
|
||||
}
|
||||
|
||||
// Получить Ярлык объекта
|
||||
public function get_item_slug($new = false)
|
||||
{
|
||||
return $this->get_field("item_slug", $new);
|
||||
}
|
||||
|
||||
// Получить Ярлык объекта
|
||||
public function get_slug($new = false)
|
||||
{
|
||||
return $this->get_item_slug($new);
|
||||
}
|
||||
|
||||
// Получить Контент объекта
|
||||
public function get_item_content($new = false)
|
||||
{
|
||||
return $this->get_field("item_content", $new);
|
||||
}
|
||||
|
||||
// Получить Контент объекта
|
||||
public function get_content($new = false)
|
||||
{
|
||||
return $this->get_item_content($new);
|
||||
}
|
||||
|
||||
// Получить Родительский объект
|
||||
public function get_item_parent($new = false)
|
||||
{
|
||||
return $this->get_field("item_parent", $new);
|
||||
}
|
||||
|
||||
// Получить родительский объект
|
||||
public function get_parent()
|
||||
{
|
||||
global $b;
|
||||
$result = $b->db_query("SELECT * FROM `bive_items` WHERE `item_id` = ? LIMIT 1", [ $this->parent_id ], true);
|
||||
if(!count($result)) return false;
|
||||
$parent = new $result[0]["item_class"]($result[0]["item_id"]);
|
||||
return $parent;
|
||||
}
|
||||
|
||||
// Задать Родительский ID
|
||||
public function set_parent($parent_id, $main = false): bool
|
||||
{
|
||||
if($this->item_id == $parent_id) return false;
|
||||
if($main) {
|
||||
$this->fill_main_parent($parent_id);
|
||||
$this->set_field("item_parent", $parent_id);
|
||||
$this->get_parent();
|
||||
}
|
||||
$this->set_second_parent($parent_id);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Очистить все родительские Объекты
|
||||
public function clear_parents($class_name = ""): bool
|
||||
{
|
||||
global $b;
|
||||
$where = "";
|
||||
if($class_name != "") $where = " AND `items_class` LIKE '" . $class_name . "-". $this->get_class_name() ."%'";
|
||||
$b->db_query("DELETE FROM `bive_items_links` WHERE `child_item_id` = ?" . $where, array($this->item_id), true);
|
||||
$b->db_query("DELETE FROM `bive_items_intermediate` WHERE `child_item_id` = ?" . $where, array($this->item_id), true);
|
||||
$this->set_field("item_parent", 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
public function clear_childs($class_name = ""): bool
|
||||
{
|
||||
global $b;
|
||||
$where = "";
|
||||
if($class_name != "") $where = " AND `items_class` LIKE '%-" . $class_name . "'";
|
||||
$b->db_query("DELETE FROM `bive_items_links` WHERE `parent_item_id` = ?" . $where, array($this->item_id), true);
|
||||
$b->db_query("DELETE FROM `bive_items_intermediate` WHERE `parent_item_id` = ?" . $where, array($this->item_id), true);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Удалить родительский Объект
|
||||
public function clear_parent($parent_id)
|
||||
{
|
||||
global $b;
|
||||
$b->db_query("DELETE FROM `bive_items_links` WHERE `child_item_id` = ? AND `parent_item_id` = ?", array($this->item_id, $parent_id), true);
|
||||
$b->db_query("DELETE FROM `bive_items_intermediate` WHERE `child_item_id` = ? AND `parent_item_id` = ?", array($this->item_id, $parent_id), true);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Задать родительский ID без сохранения
|
||||
public function fill_main_parent($parent_id)
|
||||
{
|
||||
if($this->item_id == $parent_id) return false;
|
||||
$this->parent_id = $parent_id;
|
||||
return $parent_id;
|
||||
}
|
||||
|
||||
// Занести Родительский Объект
|
||||
public function set_second_parent($parent_id): bool
|
||||
{
|
||||
global $b;
|
||||
if($this->item_id == $parent_id) return false;
|
||||
$result = $b->db_query("SELECT * FROM `bive_items_links` WHERE `parent_item_id` = ? AND `child_item_id` = ? LIMIT 1", [ $parent_id, $this->item_id ], true);
|
||||
if(count($result)) return false;
|
||||
$items_class = $b->get_item_class_by_id($parent_id) . "-" . $this->get_class_name();
|
||||
$b->db_insert("INSERT INTO `bive_items_links`(`parent_item_id`, `child_item_id`, `items_class`) VALUES (?, ?, ?)", array($parent_id, $this->item_id, $items_class));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Занести связи в таблицу с оптимизацией
|
||||
public function set_optimize()
|
||||
{
|
||||
global $b;
|
||||
$parent_list = $this->get_parent_items();
|
||||
$child_list = $this->get_child_items();
|
||||
foreach ($parent_list as $key => $item_parent) {
|
||||
foreach ($child_list as $k => $item_child) {
|
||||
$items_class = $item_parent->get_class_name() . "-" . $this->get_class_name() . "-" . $item_child->get_class_name();
|
||||
$b->db_insert("INSERT IGNORE INTO `bive_items_intermediate` (`parent_item_id`, `child_item_id`, `germ_item_id`, `items_class`) VALUES (?, ?, ?, ?)", [ $item_parent->item_id, $this->item_id, $item_child->item_id, $items_class], true);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Получить все родительские Объекты
|
||||
public function get_parent_items($class_name = ""): array
|
||||
{
|
||||
global $b;
|
||||
$parents = array();
|
||||
$main_parent = $this->get_parent();
|
||||
if($main_parent && $main_parent->get_class_name() == $class_name) $parents[] = $main_parent;
|
||||
|
||||
$class_condition = "";
|
||||
if($class_name != "") $class_condition = " AND i.`item_class` = '" . $class_name . "' ";
|
||||
|
||||
$query = "SELECT * FROM `bive_items_links` as l JOIN `bive_items` as i ON i.`item_id` = l.`parent_item_id` " . $class_condition . " WHERE l.`child_item_id` = ?";
|
||||
$parent_list = $b->db_query($query, [ $this->item_id ], true);
|
||||
if(!count($parent_list)) return $parents;
|
||||
|
||||
foreach ($parent_list as $key => $db_parent) {
|
||||
$parent = new $db_parent["item_class"]($db_parent["item_id"]);
|
||||
$parent->set_parent($db_parent["item_parent"], true);
|
||||
$parents[] = $parent;
|
||||
}
|
||||
|
||||
return $parents;
|
||||
}
|
||||
|
||||
// Получить Дочерние Объекты
|
||||
public function get_child_items($class_name = "")
|
||||
{
|
||||
global $b;
|
||||
$childs = array();
|
||||
|
||||
$class_condition = "";
|
||||
if($class_name != "") $class_condition = " AND i.`item_class` = '" . $class_name . "' ";
|
||||
|
||||
$query = "SELECT * FROM `bive_items_links` as l JOIN `bive_items` as i ON i.`item_id` = l.`child_item_id` " . $class_condition . " WHERE l.`parent_item_id` = ?";
|
||||
$child_list = $b->db_query($query, [ $this->item_id ], true);
|
||||
if(!count($child_list)) return $childs;
|
||||
|
||||
foreach ($child_list as $key => $db_child) {
|
||||
$child = new $db_child["item_class"]($db_child["item_id"]);
|
||||
$child->set_parent($db_child["item_parent"], true);
|
||||
$childs[] = $child;
|
||||
}
|
||||
|
||||
return $childs;
|
||||
}
|
||||
|
||||
public function get_main_child_items($class_name = "")
|
||||
{
|
||||
global $b;
|
||||
$childs = array();
|
||||
|
||||
$class_condition = "";
|
||||
if($class_name != "") $class_condition = " AND i.`item_class` = '" . $class_name . "' ";
|
||||
|
||||
$query = "SELECT * FROM `bive_items` as i WHERE i.`item_parent` = ? " . $class_condition;
|
||||
$child_list = $b->db_query($query, [ $this->item_id ], true);
|
||||
if(!count($child_list)) return $childs;
|
||||
|
||||
foreach ($child_list as $key => $db_child) {
|
||||
$child = new $db_child["item_class"]($db_child["item_id"]);
|
||||
$child->set_parent($db_child["item_parent"], true);
|
||||
$childs[] = $child;
|
||||
}
|
||||
|
||||
return $childs;
|
||||
}
|
||||
|
||||
// Получить поле из базы
|
||||
public function get_field($field_name, $new = false)
|
||||
{
|
||||
$item = $this->get_item($new);
|
||||
if(!$item) return false;
|
||||
$response = $item[$field_name];
|
||||
if(!$response) return false;
|
||||
return $response;
|
||||
}
|
||||
|
||||
// Обновить поле
|
||||
public function set_field($field_name, $new_value): bool
|
||||
{
|
||||
global $b;
|
||||
$b->db_query("UPDATE `bive_items` SET `" . $field_name . "` = ? WHERE `item_id` = ?", array($new_value, $this->item_id), true);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Добавить Свойство Объекту
|
||||
public function set_prop($key, $value): bool
|
||||
{
|
||||
if(!$this->props[$key]) return false;
|
||||
$prop = $this->props[$key];
|
||||
return $prop->add($value);
|
||||
}
|
||||
|
||||
// Получить Свойство Объекта
|
||||
public function get_prop($key, $new = true)
|
||||
{
|
||||
if(!$this->props[$key]) return false;
|
||||
$prop = $this->props[$key];
|
||||
return $prop->get($new);
|
||||
}
|
||||
|
||||
// Получить Свойство Объекта для вывода
|
||||
public function get_prop_render_value($key, $new = true)
|
||||
{
|
||||
if(!$this->props[$key]) return false;
|
||||
$prop = $this->props[$key];
|
||||
return $prop->render_value($new);
|
||||
}
|
||||
}
|
||||
36
engine/class.plugin.php
Normal file
36
engine/class.plugin.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
class Plugin {
|
||||
public string $plugin_path; // Полный путь до плагина
|
||||
public string $plugin_dir; // Папка плагина
|
||||
|
||||
public function __construct($plugin_dir)
|
||||
{
|
||||
$this->plugin_dir = $plugin_dir;
|
||||
$this->plugin_path = $this->get_plugins_dir() . SLASH . $this->plugin_dir . SLASH;
|
||||
}
|
||||
|
||||
public function get_plugins_dir() {
|
||||
return ROOT_DIR . SLASH . PLAYAREA_DIR_NAME . SLASH . "plugins";
|
||||
}
|
||||
|
||||
public function template_load($name, $variables = array(), $new = true)
|
||||
{
|
||||
global $b;
|
||||
$plugin_template_path = $this->plugin_path . "template" . $name;
|
||||
return $b->template_load_by_path($plugin_template_path, $variables, $new);
|
||||
}
|
||||
|
||||
public function router_add($page, $func, $methods = array("GET", "POST"))
|
||||
{
|
||||
global $b;
|
||||
$b->router_add($page, function () use ($func) { $func(); }, $methods);
|
||||
}
|
||||
|
||||
public function get_assets_path(): string
|
||||
{
|
||||
global $b;
|
||||
$root = $b->router_get_root_uri();
|
||||
return $root . SLASH . PLAYAREA_DIR_NAME . SLASH . 'plugins' . SLASH . $this->plugin_dir . SLASH . "assets". SLASH;
|
||||
}
|
||||
}
|
||||
124
engine/class.prop.php
Normal file
124
engine/class.prop.php
Normal file
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
|
||||
defined('ROOT_DIR') || exit;
|
||||
|
||||
class Prop {
|
||||
private Item $item;
|
||||
public string $prop_key;
|
||||
public string $default_value = "";
|
||||
public string $title = "Prop name";
|
||||
public bool $table_view = true;
|
||||
public bool $index = false;
|
||||
public $field;
|
||||
|
||||
public function __construct($item, $prop_key, $default_value, $title, $table_view = true, $field, $index = false)
|
||||
{
|
||||
$this->item = $item;
|
||||
$this->prop_key = $prop_key;
|
||||
$this->default_value = $default_value;
|
||||
$this->title = $title;
|
||||
$this->table_view = $table_view;
|
||||
$this->field = $field;
|
||||
$this->index = $index;
|
||||
}
|
||||
|
||||
public function render_value()
|
||||
{
|
||||
global $b;
|
||||
$field = $this->field;
|
||||
$value = $this->get();
|
||||
if(!isset($field)) return $value;
|
||||
return $b->field_render_value($field, $this->prop_key, $value);
|
||||
}
|
||||
|
||||
// Получить значение
|
||||
public function get($new = true)
|
||||
{
|
||||
global $b;
|
||||
$item_id = $this->item->get_item_id();
|
||||
$props = $b->db_query("SELECT * FROM `bive_items_props` WHERE `item_id` = ? AND `prop_key` = ?", array($item_id, $this->prop_key), $new);
|
||||
if(!count($props)) return $this->default_value;
|
||||
return $props[0]["prop_value"];
|
||||
}
|
||||
|
||||
public function add($value): bool
|
||||
{
|
||||
global $b;
|
||||
$has = $this->has();
|
||||
$field = $this->field;
|
||||
$old_value = $this->get(true);
|
||||
if(isset($field)) $value = $b->field_render_db_value($field, $this->prop_key, $value, $old_value);
|
||||
if(gettype($value) == "NULL") $value = $this->default_value;
|
||||
if($has) return $this->update($value);
|
||||
return $this->create($value);
|
||||
}
|
||||
|
||||
public function has(): bool
|
||||
{
|
||||
global $b;
|
||||
$item_id = $this->item->get_item_id();
|
||||
$props = $b->db_query("SELECT * FROM `bive_items_props` WHERE `item_id` = ? AND `prop_key` = ? LIMIT 1", array($item_id, $this->prop_key), true);
|
||||
if(!count($props)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public function update($value): bool
|
||||
{
|
||||
global $b;
|
||||
$item_id = $this->item->get_item_id();
|
||||
$b->db_query("UPDATE `bive_items_props` SET `prop_value` = ? WHERE `item_id` = ? AND `prop_key` = ?", array($value, $item_id, $this->prop_key), true);
|
||||
if($this->index) $this->index_update($value);
|
||||
return true;
|
||||
}
|
||||
|
||||
private function create($value): bool
|
||||
{
|
||||
global $b;
|
||||
$item_id = $this->item->get_item_id();
|
||||
$b->db_query("INSERT INTO `bive_items_props`(`prop_key`, `prop_value`, `item_id`) VALUES (?, ?, ?)", array($this->prop_key, $value, $item_id), true);
|
||||
if($this->index) $this->index_create($value);
|
||||
return true;
|
||||
}
|
||||
|
||||
public function delete(): bool
|
||||
{
|
||||
global $b;
|
||||
$item_id = $this->item->get_item_id();
|
||||
$b->db_query("DELETE FROM `bive_items_props` WHERE `item_id` = ? AND `prop_key` = ?", array($item_id, $this->prop_key), true);
|
||||
if($this->index) $this->index_delete();
|
||||
return true;
|
||||
}
|
||||
|
||||
public function index_get()
|
||||
{
|
||||
global $b;
|
||||
$item_id = $this->item->get_item_id();
|
||||
return $b->db_query("SELECT * FROM `bive_items_index` WHERE `item_id` = ? AND `prop_key` = ? LIMIT 1", array($item_id, $this->prop_key), true);
|
||||
}
|
||||
|
||||
public function index_update($value): bool
|
||||
{
|
||||
global $b;
|
||||
$item_id = $this->item->get_item_id();
|
||||
$index = $this->index_get();
|
||||
if(!count($index)) $this->index_create($value);
|
||||
$b->db_query("UPDATE `bive_items_index` SET `prop_value` = ? WHERE `item_id` = ? AND `prop_key` = ?", array($value, $item_id, $this->prop_key), true);
|
||||
return true;
|
||||
}
|
||||
|
||||
private function index_create($value): bool
|
||||
{
|
||||
global $b;
|
||||
$item_id = $this->item->get_item_id();
|
||||
$b->db_query("INSERT INTO `bive_items_index`(`prop_key`, `prop_value`, `item_id`) VALUES (?, ?, ?)", array($this->prop_key, $value, $item_id), true);
|
||||
return true;
|
||||
}
|
||||
|
||||
public function index_delete(): bool
|
||||
{
|
||||
global $b;
|
||||
$item_id = $this->item->get_item_id();
|
||||
$b->db_query("DELETE FROM `bive_items_index` WHERE `item_id` = ? AND `prop_key` = ?", array($item_id, $this->prop_key), true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
223
engine/class.search.php
Normal file
223
engine/class.search.php
Normal file
@@ -0,0 +1,223 @@
|
||||
<?php
|
||||
|
||||
defined('ROOT_DIR') || exit;
|
||||
|
||||
class Search {
|
||||
public array $query;
|
||||
private string $sql_query = "";
|
||||
private array $variables = array();
|
||||
public int $count = 0;
|
||||
|
||||
public function __construct($query)
|
||||
{
|
||||
$this->query = $query;
|
||||
list($sql_query, $variables) = $this->query_construct();
|
||||
$this->sql_query = $sql_query;
|
||||
$this->variables = $variables;
|
||||
}
|
||||
|
||||
// Извлечение данных из базы
|
||||
public function collect($fill = true, $new = true)
|
||||
{
|
||||
global $b;
|
||||
$result = $b->db_query($this->sql_query, $this->variables, $new);
|
||||
if(!$fill) return $result;
|
||||
|
||||
$this->get_count();
|
||||
|
||||
$items = array();
|
||||
foreach ($result as $key => $item) {
|
||||
$class = new $item["item_class"]($item["item_id"]);
|
||||
$class->fill_main_parent($item["item_parent"]);
|
||||
$class->props_values = $this->search_props($item, $class);
|
||||
$items[] = $class;
|
||||
}
|
||||
return $items;
|
||||
}
|
||||
|
||||
// Поиск параметров
|
||||
private function search_props($item, $class): array
|
||||
{
|
||||
$props = explode("-+-", $item["properties"]);
|
||||
$values = explode("-+-", $item["values"]);
|
||||
$result = array();
|
||||
|
||||
for ($i = 0; $i < count($props); $i++) {
|
||||
$prop = $props[$i];
|
||||
$value = $values[$i];
|
||||
if(!isset($class->props[$prop])) continue;
|
||||
$result[$prop] = $value;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
// Получить количество элементов
|
||||
public function get_count(){
|
||||
global $b;
|
||||
list($query_count, $variables) = $this->query_construct(true);
|
||||
$result = $b->db_query($query_count, $variables, false);
|
||||
$this->count = $result[0]["count"];
|
||||
return $this;
|
||||
}
|
||||
|
||||
// Создание запроса
|
||||
private function query_construct($count = false): array
|
||||
{
|
||||
$variables = array();
|
||||
|
||||
$props = $this->query["props"];
|
||||
$params = $this->query["terms"] ?? array();
|
||||
|
||||
if(isset($this->query["class"])) $params["item_class"] = $this->query["class"];
|
||||
if(isset($this->query["parent_id"])) $params["item_parent"] = $this->query["parent_id"];
|
||||
|
||||
$limit = intval($this->query["limit"] ?? 184467440737);
|
||||
$offset = intval($this->query["offset"] ?? 0);
|
||||
|
||||
$order = $this->query_order($this->query["order"]);
|
||||
$order_inner = $this->query_order_inner($this->query["order"]);
|
||||
|
||||
$where_list = array();
|
||||
|
||||
list($where_props, $props_var) = $this->query_props($props);
|
||||
$variables = array_merge($variables, $props_var);
|
||||
|
||||
list($where, $where_var) = $this->query_where($params);
|
||||
$variables = array_merge($variables, $where_var);
|
||||
|
||||
if($where_props) $where_list[] = $where_props;
|
||||
if($where) $where_list[] = $where;
|
||||
|
||||
$return_fields = "COUNT(*) as count";
|
||||
$group_by = $join_props = $limit_query = $offset_query = "";
|
||||
|
||||
if(!$count){
|
||||
$return_fields = " `bive_items`.* ";
|
||||
$group_by = " GROUP BY `bive_items`.`item_id` ";
|
||||
$limit_query = " LIMIT $limit ";
|
||||
$offset_query = " OFFSET $offset ";
|
||||
|
||||
if(isset($params["item_class"])) {
|
||||
$return_fields = "`bive_items`.*, GROUP_CONCAT(p.prop_key SEPARATOR '-+-') AS properties, GROUP_CONCAT(p.prop_value SEPARATOR '-+-') AS `values`";
|
||||
$join_props = " JOIN `bive_items_props` p ON `bive_items`.`item_id` = p.`item_id` ";
|
||||
|
||||
$class = new $params["item_class"](0);
|
||||
if(!count($class->props)) {
|
||||
$return_fields = "`bive_items`.*";
|
||||
$group_by = "";
|
||||
$join_props = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$where_collect = $this->query_binary($where_list);
|
||||
$result_query = "SELECT $return_fields FROM `bive_items` $join_props $order_inner WHERE $where_collect $group_by $order $limit_query $offset_query;";
|
||||
return array($result_query, $variables);
|
||||
}
|
||||
|
||||
// Соединение таблиц для сортировки
|
||||
private function query_order_inner($order): string
|
||||
{
|
||||
if(!isset($order) || !isset($order["key"])) return "";
|
||||
$key = $order["key"];
|
||||
return "INNER JOIN `bive_items_props` AS `bip` ON `bive_items`.`item_id` = `bip`.`item_id` AND `bip`.`prop_key` = '$key'";
|
||||
}
|
||||
|
||||
// Сортировка
|
||||
private function query_order($order): string
|
||||
{
|
||||
if(!isset($order) || !isset($order["key"])) return "";
|
||||
$direction = $order["direction"] ?? "ASC";
|
||||
$type = $order["direction"] == "numeric" ? "CAST(`bip`.`prop_value` AS SIGNED)" : "`bip`.`prop_value`";
|
||||
return " ORDER BY $type $direction ";
|
||||
}
|
||||
|
||||
private function query_binary($array, $operator = "AND"): string
|
||||
{
|
||||
return "(" . implode(" " . mb_strtoupper($operator) . " ", $array) . ")";
|
||||
}
|
||||
|
||||
// Условия поиска по свойствам
|
||||
private function query_props($props, $operator = "and"): array
|
||||
{
|
||||
if(!$props || !count($props)) return array("", array());
|
||||
$sql = array();
|
||||
$variables = array();
|
||||
foreach ($props as $key => $prop) {
|
||||
if($key == "and") {
|
||||
list($query, $var) = $this->query_props($prop, "and");
|
||||
$sql[] = $query;
|
||||
$variables[] = $var;
|
||||
}else if($key == "or") {
|
||||
list($query, $var) = $this->query_props($prop, "or");
|
||||
$sql[] = $query;
|
||||
$variables[] = $var;
|
||||
} else {
|
||||
list($key, $prop, $not, $op) = $this->query_operators($key, $prop);
|
||||
$sql[] = $not . "EXISTS (
|
||||
SELECT 1
|
||||
FROM bive_items_props
|
||||
WHERE bive_items.item_id = bive_items_props.item_id
|
||||
AND bive_items_props.prop_key = '$key' AND bive_items_props.prop_value $op ?
|
||||
)";
|
||||
$variables[] = $prop;
|
||||
}
|
||||
}
|
||||
$string_query = $this->query_binary($sql, $operator);
|
||||
return array($string_query, $variables);
|
||||
}
|
||||
|
||||
// Условия поиска по предметам
|
||||
private function query_where($where, $operator = "and"): array
|
||||
{
|
||||
if(!$where || !count($where)) return array("", array());
|
||||
$sql = array();
|
||||
$variables = array();
|
||||
foreach ($where as $key => $prop) {
|
||||
if($key == "and") {
|
||||
list($query, $var) = $this->query_where($prop, "and");
|
||||
$sql[] = $query;
|
||||
$variables = array(...$variables, ...$var);
|
||||
}else if($key == "or") {
|
||||
list($query, $var) = $this->query_where($prop, "or");
|
||||
$sql[] = $query;
|
||||
$variables[] = array(...$variables, ...$var);
|
||||
} else {
|
||||
list($key, $prop, $not, $op) = $this->query_operators($key, $prop);
|
||||
$sql[] = "$not`bive_items`.`$key` $op ?";
|
||||
$variables[] = $prop;
|
||||
}
|
||||
}
|
||||
$string_query = $this->query_binary($sql, $operator);
|
||||
return array($string_query, $variables);
|
||||
}
|
||||
|
||||
private function query_operators($key, $prop): array
|
||||
{
|
||||
$not = "";
|
||||
$op = " = ";
|
||||
|
||||
// Добавление отрицания
|
||||
if(mb_substr($key, 0, 1) == "!") {
|
||||
$key = substr($key, 1);
|
||||
$not = " NOT ";
|
||||
}
|
||||
|
||||
// Добавление конструкции LIKE
|
||||
if(mb_substr($key, 0, 1) == "%") {
|
||||
$prop = "%" . $prop;
|
||||
$key = substr($key, 1);
|
||||
$op = " LIKE ";
|
||||
}
|
||||
|
||||
// Добавление конструкции LIKE
|
||||
if(mb_substr($key, -1) == "%") {
|
||||
$prop = $prop . "%";
|
||||
$key = substr($key, 0, -1);
|
||||
$op = " LIKE ";
|
||||
}
|
||||
|
||||
return array($key, $prop, $not, $op);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user