generated from mirzaev/pot
	начало работы над сессиями и там дохуя чего ещё
This commit is contained in:
		| @@ -1,169 +1,169 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace mirzaev\site\account\models; | ||||
|  | ||||
| // Файлы проекта | ||||
| use mirzaev\site\account\models\vk_model as vk; | ||||
|  | ||||
| // Фреймворк ArangoDB | ||||
| use mirzaev\arangodb\collection, | ||||
|     mirzaev\arangodb\document; | ||||
|  | ||||
| // Библиотека для ArangoDB | ||||
| use ArangoDBClient\Document as _document; | ||||
|  | ||||
| // Встроенные библиотеки | ||||
| use exception; | ||||
|  | ||||
| /** | ||||
|  * Модель регистрации, аутентификации и авторизации | ||||
|  * | ||||
|  * @package mirzaev\site\account\models | ||||
|  * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy> | ||||
|  */ | ||||
| final class account_model extends core | ||||
| { | ||||
|     /** | ||||
|      * Коллекция | ||||
|      */ | ||||
|     public const COLLECTION = 'account'; | ||||
|  | ||||
|     /** | ||||
|      * Создать | ||||
|      * | ||||
|      * @param array &$errors Журнал ошибок | ||||
|      * | ||||
|      * @return ?_document Инстанция аккаунта, если удалось создать | ||||
|      */ | ||||
|     public static function create(array &$errors = []): ?_document | ||||
|     { | ||||
|         try { | ||||
|             if (collection::init(static::$db->session, self::COLLECTION)) { | ||||
|                 // Инициализирована коллекция | ||||
|  | ||||
|                 // Запись аккаунта в базу данных | ||||
|                 $_id = document::write(static::$db->session, self::COLLECTION); | ||||
|  | ||||
|                 if ($account = collection::search(static::$db->session, sprintf( | ||||
|                     <<<AQL | ||||
|                             FOR d IN %s | ||||
|                             FILTER d._id == '$_id' | ||||
|                             RETURN d | ||||
|                         AQL, | ||||
|                     self::COLLECTION | ||||
|                 ))) { | ||||
|                     // Найден созданный аккаунт | ||||
|  | ||||
|                     return $account; | ||||
|                 } else throw new exception('Не удалось создать аккаунт'); | ||||
|             } else throw new exception('Не удалось инициализировать коллекцию'); | ||||
|         } catch (exception $e) { | ||||
|             // Запись в журнал ошибок | ||||
|             $errors[] = [ | ||||
|                 'text' => $e->getMessage(), | ||||
|                 'file' => $e->getFile(), | ||||
|                 'line' => $e->getLine(), | ||||
|                 'stack' => $e->getTrace() | ||||
|             ]; | ||||
|         } | ||||
|  | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Связь аккаунта с аккаунтом ВКонтакте | ||||
|      * | ||||
|      * @param _document $account Инстанция аккаунта | ||||
|      * @param _document $vk Инстанция аккаунта ВКонтакте | ||||
|      * @param array &$errors Журнал ошибок | ||||
|      * | ||||
|      * @return bool Статус выполнения | ||||
|      */ | ||||
|     public static function connect(_document $account, _document $vk, array &$errors = []): bool | ||||
|     { | ||||
|         try { | ||||
|             if ( | ||||
|                 collection::init(static::$db->session, self::COLLECTION) | ||||
|                 && collection::init(static::$db->session, vk::COLLECTION) | ||||
|                 && collection::init(static::$db->session, self::COLLECTION . '_edge_' . vk::COLLECTION, true) | ||||
|             ) { | ||||
|                 // Инициализированы коллекции | ||||
|  | ||||
|                 if (document::write(static::$db->session, self::COLLECTION . '_edge_' . vk::COLLECTION, [ | ||||
|                     '_from' => $account->getId(), | ||||
|                     '_to' => $vk->getId() | ||||
|                 ])) { | ||||
|                     // Создано ребро: account -> vk | ||||
|  | ||||
|                     return true; | ||||
|                 } else throw new exception('Не удалось создать ребро: account -> vk'); | ||||
|             } else throw new exception('Не удалось инициализировать коллекцию'); | ||||
|         } catch (exception $e) { | ||||
|             // Запись в журнал ошибок | ||||
|             $errors[] = [ | ||||
|                 'text' => $e->getMessage(), | ||||
|                 'file' => $e->getFile(), | ||||
|                 'line' => $e->getLine(), | ||||
|                 'stack' => $e->getTrace() | ||||
|             ]; | ||||
|         } | ||||
|  | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Поиск связанного аккаунта ВКонтакте | ||||
|      * | ||||
|      * @param _document $account Инстанция аккаунта | ||||
|      * @param array &$errors Журнал ошибок | ||||
|      * | ||||
|      * @return ?_document Инстанция аккаунта, если удалось найти | ||||
|      */ | ||||
|     public static function vk(_document $account, array &$errors = []): ?_document | ||||
|     { | ||||
|         try { | ||||
|             if ( | ||||
|                 collection::init(static::$db->session, self::COLLECTION) | ||||
|                 && collection::init(static::$db->session, vk::COLLECTION) | ||||
|                 && collection::init(static::$db->session, self::COLLECTION . '_edge_' . vk::COLLECTION, true) | ||||
|             ) { | ||||
|                 // Инициализирована коллекция | ||||
|  | ||||
|                 if ($vk = collection::search(static::$db->session, sprintf( | ||||
|                     <<<AQL | ||||
|                         FOR document IN %s | ||||
|                         LET edge = ( | ||||
|                             FOR edge IN %s | ||||
|                             FILTER edge._from == '%s' | ||||
|                             SORT edge._key DESC | ||||
|                             LIMIT 1 | ||||
|                             RETURN edge | ||||
|                         ) | ||||
|                         FILTER document._id == edge[0]._to | ||||
|                         LIMIT 1 | ||||
|                         RETURN document | ||||
|                     AQL, | ||||
|                     vk::COLLECTION, | ||||
|                     self::COLLECTION . '_edge_' . vk::COLLECTION, | ||||
|                     $account->getId() | ||||
|                 ))) { | ||||
|                     // Найден аккаунт ВКонтакте | ||||
|  | ||||
|                     return $vk; | ||||
|                 } else throw new exception('Не удалось найти аккаунт ВКонтакте'); | ||||
|             } else throw new exception('Не удалось инициализировать коллекцию'); | ||||
|         } catch (exception $e) { | ||||
|             // Запись в журнал ошибок | ||||
|             $errors[] = [ | ||||
|                 'text' => $e->getMessage(), | ||||
|                 'file' => $e->getFile(), | ||||
|                 'line' => $e->getLine(), | ||||
|                 'stack' => $e->getTrace() | ||||
|             ]; | ||||
|         } | ||||
|  | ||||
|         return null; | ||||
|     } | ||||
| } | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace mirzaev\site\account\models; | ||||
|  | ||||
| // Файлы проекта | ||||
| use mirzaev\site\account\models\vk_model as vk; | ||||
|  | ||||
| // Фреймворк ArangoDB | ||||
| use mirzaev\arangodb\collection, | ||||
|     mirzaev\arangodb\document; | ||||
|  | ||||
| // Библиотека для ArangoDB | ||||
| use ArangoDBClient\Document as _document; | ||||
|  | ||||
| // Встроенные библиотеки | ||||
| use exception; | ||||
|  | ||||
| /** | ||||
|  * Модель регистрации, аутентификации и авторизации | ||||
|  * | ||||
|  * @package mirzaev\site\account\models | ||||
|  * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy> | ||||
|  */ | ||||
| final class account_model extends core | ||||
| { | ||||
|     /** | ||||
|      * Коллекция | ||||
|      */ | ||||
|     public const COLLECTION = 'account'; | ||||
|  | ||||
|     /** | ||||
|      * Создать | ||||
|      * | ||||
|      * @param array &$errors Журнал ошибок | ||||
|      * | ||||
|      * @return ?_document Инстанция аккаунта, если удалось создать | ||||
|      */ | ||||
|     public static function create(array &$errors = []): ?_document | ||||
|     { | ||||
|         try { | ||||
|             if (collection::init(static::$db->session, self::COLLECTION)) { | ||||
|                 // Инициализирована коллекция | ||||
|  | ||||
|                 // Запись аккаунта в базу данных | ||||
|                 $_id = document::write(static::$db->session, self::COLLECTION); | ||||
|  | ||||
|                 if ($account = collection::search(static::$db->session, sprintf( | ||||
|                     <<<AQL | ||||
|                             FOR d IN %s | ||||
|                             FILTER d._id == '$_id' | ||||
|                             RETURN d | ||||
|                         AQL, | ||||
|                     self::COLLECTION | ||||
|                 ))) { | ||||
|                     // Найден созданный аккаунт | ||||
|  | ||||
|                     return $account; | ||||
|                 } else throw new exception('Не удалось создать аккаунт'); | ||||
|             } else throw new exception('Не удалось инициализировать коллекцию'); | ||||
|         } catch (exception $e) { | ||||
|             // Запись в журнал ошибок | ||||
|             $errors[] = [ | ||||
|                 'text' => $e->getMessage(), | ||||
|                 'file' => $e->getFile(), | ||||
|                 'line' => $e->getLine(), | ||||
|                 'stack' => $e->getTrace() | ||||
|             ]; | ||||
|         } | ||||
|  | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Связь аккаунта с аккаунтом ВКонтакте | ||||
|      * | ||||
|      * @param _document $account Инстанция аккаунта | ||||
|      * @param _document $vk Инстанция аккаунта ВКонтакте | ||||
|      * @param array &$errors Журнал ошибок | ||||
|      * | ||||
|      * @return bool Статус выполнения | ||||
|      */ | ||||
|     public static function connect(_document $account, _document $vk, array &$errors = []): bool | ||||
|     { | ||||
|         try { | ||||
|             if ( | ||||
|                 collection::init(static::$db->session, self::COLLECTION) | ||||
|                 && collection::init(static::$db->session, vk::COLLECTION) | ||||
|                 && collection::init(static::$db->session, self::COLLECTION . '_edge_' . vk::COLLECTION, true) | ||||
|             ) { | ||||
|                 // Инициализированы коллекции | ||||
|  | ||||
|                 if (document::write(static::$db->session, self::COLLECTION . '_edge_' . vk::COLLECTION, [ | ||||
|                     '_from' => $account->getId(), | ||||
|                     '_to' => $vk->getId() | ||||
|                 ])) { | ||||
|                     // Создано ребро: account -> vk | ||||
|  | ||||
|                     return true; | ||||
|                 } else throw new exception('Не удалось создать ребро: account -> vk'); | ||||
|             } else throw new exception('Не удалось инициализировать коллекцию'); | ||||
|         } catch (exception $e) { | ||||
|             // Запись в журнал ошибок | ||||
|             $errors[] = [ | ||||
|                 'text' => $e->getMessage(), | ||||
|                 'file' => $e->getFile(), | ||||
|                 'line' => $e->getLine(), | ||||
|                 'stack' => $e->getTrace() | ||||
|             ]; | ||||
|         } | ||||
|  | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Поиск связанного аккаунта ВКонтакте | ||||
|      * | ||||
|      * @param _document $account Инстанция аккаунта | ||||
|      * @param array &$errors Журнал ошибок | ||||
|      * | ||||
|      * @return ?_document Инстанция аккаунта, если удалось найти | ||||
|      */ | ||||
|     public static function vk(_document $account, array &$errors = []): ?_document | ||||
|     { | ||||
|         try { | ||||
|             if ( | ||||
|                 collection::init(static::$db->session, self::COLLECTION) | ||||
|                 && collection::init(static::$db->session, vk::COLLECTION) | ||||
|                 && collection::init(static::$db->session, self::COLLECTION . '_edge_' . vk::COLLECTION, true) | ||||
|             ) { | ||||
|                 // Инициализирована коллекция | ||||
|  | ||||
|                 if ($vk = collection::search(static::$db->session, sprintf( | ||||
|                     <<<AQL | ||||
|                         FOR document IN %s | ||||
|                         LET edge = ( | ||||
|                             FOR edge IN %s | ||||
|                             FILTER edge._from == '%s' | ||||
|                             SORT edge._key DESC | ||||
|                             LIMIT 1 | ||||
|                             RETURN edge | ||||
|                         ) | ||||
|                         FILTER document._id == edge[0]._to | ||||
|                         LIMIT 1 | ||||
|                         RETURN document | ||||
|                     AQL, | ||||
|                     vk::COLLECTION, | ||||
|                     self::COLLECTION . '_edge_' . vk::COLLECTION, | ||||
|                     $account->getId() | ||||
|                 ))) { | ||||
|                     // Найден аккаунт ВКонтакте | ||||
|  | ||||
|                     return $vk; | ||||
|                 } else throw new exception('Не удалось найти аккаунт ВКонтакте'); | ||||
|             } else throw new exception('Не удалось инициализировать коллекцию'); | ||||
|         } catch (exception $e) { | ||||
|             // Запись в журнал ошибок | ||||
|             $errors[] = [ | ||||
|                 'text' => $e->getMessage(), | ||||
|                 'file' => $e->getFile(), | ||||
|                 'line' => $e->getLine(), | ||||
|                 'stack' => $e->getTrace() | ||||
|             ]; | ||||
|         } | ||||
|  | ||||
|         return null; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,143 +1,143 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace mirzaev\site\account\models; | ||||
|  | ||||
| use mirzaev\minimal\model; | ||||
|  | ||||
| use mirzaev\arangodb\connection; | ||||
|  | ||||
| use exception; | ||||
|  | ||||
| /** | ||||
|  * Ядро моделей | ||||
|  * | ||||
|  * @package mirzaev\site\account\models | ||||
|  * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy> | ||||
|  */ | ||||
| class core extends model | ||||
| { | ||||
|     /** | ||||
|      * Коллекция в которой хранятся аккаунты | ||||
|      */ | ||||
|     public const SETTINGS = '../settings/arangodb.php'; | ||||
|  | ||||
|     /** | ||||
|      * Соединение с базой данных | ||||
|      */ | ||||
|     protected static connection $db; | ||||
|  | ||||
|     public function __construct(connection $db = null) | ||||
|     { | ||||
|         if (isset($db)) { | ||||
|             // Получена инстанция соединения с базой данных | ||||
|  | ||||
|             // Запись и инициализация соединения с базой данных | ||||
|             $this->__set('db', $db); | ||||
|         } else { | ||||
|             // Не получена инстанция соединения с базой данных | ||||
|  | ||||
|             // Инициализация соединения с базой данных по умолчанию | ||||
|             $this->__get('db'); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Записать свойство | ||||
|      * | ||||
|      * @param string $name Название | ||||
|      * @param mixed $value Значение | ||||
|      */ | ||||
|     public function __set(string $name, mixed $value = null): void | ||||
|     { | ||||
|         match ($name) { | ||||
|             'db' => (function () use ($value) { | ||||
|                 if ($this->__isset('db')) { | ||||
|                     // Свойство уже было инициализировано | ||||
|  | ||||
|                     // Выброс исключения (неудача) | ||||
|                     throw new exception('Запрещено реинициализировать соединение с базой данных ($this->db)', 500); | ||||
|                 } else { | ||||
|                     // Свойство ещё не было инициализировано | ||||
|  | ||||
|                     if ($value instanceof connection) { | ||||
|                         // Передано подходящее значение | ||||
|  | ||||
|                         // Запись свойства (успех) | ||||
|                         self::$db = $value; | ||||
|                     } else { | ||||
|                         // Передано неподходящее значение | ||||
|  | ||||
|                         // Выброс исключения (неудача) | ||||
|                         throw new exception('Соединение с базой данных ($this->db) должен быть инстанцией mirzaev\arangodb\connection', 500); | ||||
|                     } | ||||
|                 } | ||||
|             })(), | ||||
|             default => parent::__set($name, $value) | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Прочитать свойство | ||||
|      * | ||||
|      * @param string $name Название | ||||
|      * | ||||
|      * @return mixed Содержимое | ||||
|      */ | ||||
|     public function __get(string $name): mixed | ||||
|     { | ||||
|         return match ($name) { | ||||
|             'db' => (function () { | ||||
|                 if (!$this->__isset('db')) { | ||||
|                     // Свойство не инициализировано | ||||
|  | ||||
|                     // Инициализация значения по умолчанию исходя из настроек | ||||
|                     $this->__set('db', new connection(require static::SETTINGS)); | ||||
|                 } | ||||
|  | ||||
|                 return self::$db; | ||||
|             })(), | ||||
|             default => parent::__get($name) | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Проверить свойство на инициализированность | ||||
|      * | ||||
|      * @param string $name Название | ||||
|      */ | ||||
|     public function __isset(string $name): bool | ||||
|     { | ||||
|         return match ($name) { | ||||
|             default => parent::__isset($name) | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Удалить свойство | ||||
|      * | ||||
|      * @param string $name Название | ||||
|      */ | ||||
|     public function __unset(string $name): void | ||||
|     { | ||||
|         match ($name) { | ||||
|             default => parent::__isset($name) | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Статический вызов | ||||
|      * | ||||
|      * @param string $name Название | ||||
|      * @param array $arguments Параметры | ||||
|      */ | ||||
|     public static function __callStatic(string $name, array $arguments): mixed | ||||
|     { | ||||
|         match ($name) { | ||||
|             'db' => (new static)->__get('db'), | ||||
|             default => throw new exception("Не найдено свойство или функция: $name", 500) | ||||
|         }; | ||||
|     } | ||||
| } | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace mirzaev\site\account\models; | ||||
|  | ||||
| use mirzaev\minimal\model; | ||||
|  | ||||
| use mirzaev\arangodb\connection; | ||||
|  | ||||
| use exception; | ||||
|  | ||||
| /** | ||||
|  * Ядро моделей | ||||
|  * | ||||
|  * @package mirzaev\site\account\models | ||||
|  * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy> | ||||
|  */ | ||||
| class core extends model | ||||
| { | ||||
|     /** | ||||
|      * Коллекция в которой хранятся аккаунты | ||||
|      */ | ||||
|     public const SETTINGS = '../settings/arangodb.php'; | ||||
|  | ||||
|     /** | ||||
|      * Соединение с базой данных | ||||
|      */ | ||||
|     protected static connection $db; | ||||
|  | ||||
|     public function __construct(connection $db = null) | ||||
|     { | ||||
|         if (isset($db)) { | ||||
|             // Получена инстанция соединения с базой данных | ||||
|  | ||||
|             // Запись и инициализация соединения с базой данных | ||||
|             $this->__set('db', $db); | ||||
|         } else { | ||||
|             // Не получена инстанция соединения с базой данных | ||||
|  | ||||
|             // Инициализация соединения с базой данных по умолчанию | ||||
|             $this->__get('db'); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Записать свойство | ||||
|      * | ||||
|      * @param string $name Название | ||||
|      * @param mixed $value Значение | ||||
|      */ | ||||
|     public function __set(string $name, mixed $value = null): void | ||||
|     { | ||||
|         match ($name) { | ||||
|             'db' => (function () use ($value) { | ||||
|                 if ($this->__isset('db')) { | ||||
|                     // Свойство уже было инициализировано | ||||
|  | ||||
|                     // Выброс исключения (неудача) | ||||
|                     throw new exception('Запрещено реинициализировать соединение с базой данных ($this->db)', 500); | ||||
|                 } else { | ||||
|                     // Свойство ещё не было инициализировано | ||||
|  | ||||
|                     if ($value instanceof connection) { | ||||
|                         // Передано подходящее значение | ||||
|  | ||||
|                         // Запись свойства (успех) | ||||
|                         self::$db = $value; | ||||
|                     } else { | ||||
|                         // Передано неподходящее значение | ||||
|  | ||||
|                         // Выброс исключения (неудача) | ||||
|                         throw new exception('Соединение с базой данных ($this->db) должен быть инстанцией mirzaev\arangodb\connection', 500); | ||||
|                     } | ||||
|                 } | ||||
|             })(), | ||||
|             default => parent::__set($name, $value) | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Прочитать свойство | ||||
|      * | ||||
|      * @param string $name Название | ||||
|      * | ||||
|      * @return mixed Содержимое | ||||
|      */ | ||||
|     public function __get(string $name): mixed | ||||
|     { | ||||
|         return match ($name) { | ||||
|             'db' => (function () { | ||||
|                 if (!$this->__isset('db')) { | ||||
|                     // Свойство не инициализировано | ||||
|  | ||||
|                     // Инициализация значения по умолчанию исходя из настроек | ||||
|                     $this->__set('db', new connection(require static::SETTINGS)); | ||||
|                 } | ||||
|  | ||||
|                 return self::$db; | ||||
|             })(), | ||||
|             default => parent::__get($name) | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Проверить свойство на инициализированность | ||||
|      * | ||||
|      * @param string $name Название | ||||
|      */ | ||||
|     public function __isset(string $name): bool | ||||
|     { | ||||
|         return match ($name) { | ||||
|             default => parent::__isset($name) | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Удалить свойство | ||||
|      * | ||||
|      * @param string $name Название | ||||
|      */ | ||||
|     public function __unset(string $name): void | ||||
|     { | ||||
|         match ($name) { | ||||
|             default => parent::__isset($name) | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Статический вызов | ||||
|      * | ||||
|      * @param string $name Название | ||||
|      * @param array $arguments Параметры | ||||
|      */ | ||||
|     public static function __callStatic(string $name, array $arguments): mixed | ||||
|     { | ||||
|         match ($name) { | ||||
|             'db' => (new static)->__get('db'), | ||||
|             default => throw new exception("Не найдено свойство или функция: $name", 500) | ||||
|         }; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,213 +1,249 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace mirzaev\site\account\models; | ||||
|  | ||||
| // Файлы проекта | ||||
| use mirzaev\site\account\models\account_model as account; | ||||
|  | ||||
| // Фреймворк ArangoDB | ||||
| use mirzaev\arangodb\collection, | ||||
|     mirzaev\arangodb\document; | ||||
|  | ||||
| // Библиотека для ArangoDB | ||||
| use ArangoDBClient\Document as _document; | ||||
|  | ||||
| // Встроенные библиотеки | ||||
| use exception; | ||||
|  | ||||
| /** | ||||
|  * Модель сессий | ||||
|  * | ||||
|  * @package mirzaev\site\account\models | ||||
|  * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy> | ||||
|  */ | ||||
| final class session_model extends core | ||||
| { | ||||
|     /** | ||||
|      * Коллекция | ||||
|      */ | ||||
|     public const COLLECTION = 'session'; | ||||
|  | ||||
|     /** | ||||
|      * Инициализация | ||||
|      * | ||||
|      * @param ?string $hash Хеш сессии в базе данных | ||||
|      * @param ?int $expires Дата окончания работы сессии (используется при создании новой сессии) | ||||
|      * @param array &$errors Журнал ошибок | ||||
|      * | ||||
|      * @return ?_document Инстанция сессии, если удалось найти или создать | ||||
|      */ | ||||
|     public static function initialization(?string $hash = null, ?int $expires = null, array &$errors = []): ?_document | ||||
|     { | ||||
|         try { | ||||
|             if (collection::init(static::$db->session, self::COLLECTION)) { | ||||
|                 // Инициализирована коллекция | ||||
|  | ||||
|                 if (isset($hash) && $session = collection::search(static::$db->session, sprintf( | ||||
|                     <<<AQL | ||||
|                         FOR d IN %s | ||||
|                         FILTER d.hash == '$hash' && d.expires > %d | ||||
|                         RETURN d | ||||
|                     AQL, | ||||
|                     self::COLLECTION, | ||||
|                     time() | ||||
|                 ))) { | ||||
|                     // Найдена сессия по хешу | ||||
|  | ||||
|                     // Возврат сессии | ||||
|                     return $session; | ||||
|                 } else if ($session = collection::search(static::$db->session, sprintf( | ||||
|                     <<<AQL | ||||
|                         FOR d IN %s | ||||
|                         FILTER d.ip == '%s' && d.expires > %d | ||||
|                         RETURN d | ||||
|                     AQL, | ||||
|                     self::COLLECTION, | ||||
|                     $_SERVER['REMOTE_ADDR'], | ||||
|                     time() | ||||
|                 ))) { | ||||
|                     // Найдена сессия по данным пользователя | ||||
|  | ||||
|                     // Возврат сессии | ||||
|                     return $session; | ||||
|                 } else { | ||||
|                     // Не найдена сессия | ||||
|  | ||||
|                     // Запись сессии в базу данных | ||||
|                     $_id = document::write(static::$db->session, self::COLLECTION, [ | ||||
|                         'ip' => $_SERVER['REMOTE_ADDR'], | ||||
|                         'expires' => $expires ?? time() + 604800 | ||||
|                     ]); | ||||
|  | ||||
|                     if ($session = collection::search(static::$db->session, sprintf( | ||||
|                         <<<AQL | ||||
|                             FOR d IN %s | ||||
|                             FILTER d._id == '$_id' && d.expires > %d | ||||
|                             RETURN d | ||||
|                         AQL, | ||||
|                         self::COLLECTION, | ||||
|                         time() | ||||
|                     ))) { | ||||
|                         // Найдена созданная сессия | ||||
|  | ||||
|                         // Запись хеша | ||||
|                         $session->hash = sodium_bin2hex(sodium_crypto_generichash($_id)); | ||||
|  | ||||
|                         if (document::update(static::$db->session, $session)) { | ||||
|                             // Записано обновление | ||||
|  | ||||
|                             return $session; | ||||
|                         } else throw new exception('Не удалось записать данные сессии'); | ||||
|                     } else throw new exception('Не удалось создать или найти созданную сессию'); | ||||
|                 } | ||||
|             } else throw new exception('Не удалось инициализировать коллекцию'); | ||||
|         } catch (exception $e) { | ||||
|             // Запись в журнал ошибок | ||||
|             $errors[] = [ | ||||
|                 'text' => $e->getMessage(), | ||||
|                 'file' => $e->getFile(), | ||||
|                 'line' => $e->getLine(), | ||||
|                 'stack' => $e->getTrace() | ||||
|             ]; | ||||
|         } | ||||
|  | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Связь сессии с аккаунтом | ||||
|      * | ||||
|      * @param _document $session Инстанция сессии | ||||
|      * @param _document $account Инстанция аккаунта | ||||
|      * @param array &$errors Журнал ошибок | ||||
|      * | ||||
|      * @return bool Статус выполнения | ||||
|      */ | ||||
|     public static function connect(_document $session, _document $account, array &$errors = []): bool | ||||
|     { | ||||
|         try { | ||||
|             if ( | ||||
|                 collection::init(static::$db->session, self::COLLECTION) | ||||
|                 && collection::init(static::$db->session, account::COLLECTION) | ||||
|                 && collection::init(static::$db->session, self::COLLECTION . '_edge_' . account::COLLECTION, true) | ||||
|             ) { | ||||
|                 // Инициализирована коллекция | ||||
|  | ||||
|                 if (document::write(static::$db->session, self::COLLECTION . '_edge_' . account::COLLECTION, [ | ||||
|                     '_from' => $session->getId(), | ||||
|                     '_to' => $account->getId() | ||||
|                 ])) { | ||||
|                     // Создано ребро: session -> account | ||||
|  | ||||
|                     return true; | ||||
|                 } else throw new exception('Не удалось создать ребро: session -> account'); | ||||
|             } else throw new exception('Не удалось инициализировать коллекцию'); | ||||
|         } catch (exception $e) { | ||||
|             // Запись в журнал ошибок | ||||
|             $errors[] = [ | ||||
|                 'text' => $e->getMessage(), | ||||
|                 'file' => $e->getFile(), | ||||
|                 'line' => $e->getLine(), | ||||
|                 'stack' => $e->getTrace() | ||||
|             ]; | ||||
|         } | ||||
|  | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Поиск связанного аккаунта | ||||
|      * | ||||
|      * @param _document $session Инстанция сессии | ||||
|      * @param array &$errors Журнал ошибок | ||||
|      * | ||||
|      * @return ?_document Инстанция аккаунта, если удалось найти | ||||
|      */ | ||||
|     public static function account(_document $session, array &$errors = []): ?_document | ||||
|     { | ||||
|         try { | ||||
|             if ( | ||||
|                 collection::init(static::$db->session, self::COLLECTION) | ||||
|                 && collection::init(static::$db->session, account::COLLECTION) | ||||
|                 && collection::init(static::$db->session, self::COLLECTION . '_edge_' . account::COLLECTION, true) | ||||
|             ) { | ||||
|                 // Инициализированы коллекции | ||||
|  | ||||
|                 if ($account = collection::search(static::$db->session, sprintf( | ||||
|                     <<<AQL | ||||
|                         FOR document IN %s | ||||
|                         LET edge = ( | ||||
|                             FOR edge IN %s | ||||
|                             FILTER edge._from == '%s' | ||||
|                             SORT edge._key DESC | ||||
|                             LIMIT 1 | ||||
|                             RETURN edge | ||||
|                         ) | ||||
|                         FILTER document._id == edge[0]._to | ||||
|                         LIMIT 1 | ||||
|                         RETURN document | ||||
|                     AQL, | ||||
|                     account::COLLECTION, | ||||
|                     self::COLLECTION . '_edge_' . account::COLLECTION, | ||||
|                     $session->getId() | ||||
|                 ))) { | ||||
|                     // Найден аккаунт | ||||
|  | ||||
|                     return $account; | ||||
|                 } else throw new exception('Не удалось найти аккаунт'); | ||||
|             } else throw new exception('Не удалось инициализировать коллекцию'); | ||||
|         } catch (exception $e) { | ||||
|             // Запись в журнал ошибок | ||||
|             $errors[] = [ | ||||
|                 'text' => $e->getMessage(), | ||||
|                 'file' => $e->getFile(), | ||||
|                 'line' => $e->getLine(), | ||||
|                 'stack' => $e->getTrace() | ||||
|             ]; | ||||
|         } | ||||
|  | ||||
|         return null; | ||||
|     } | ||||
| } | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace mirzaev\site\account\models; | ||||
|  | ||||
| // Файлы проекта | ||||
| use mirzaev\site\account\models\account_model as account; | ||||
|  | ||||
| // Фреймворк ArangoDB | ||||
| use mirzaev\arangodb\collection, | ||||
|   mirzaev\arangodb\document; | ||||
|  | ||||
| // Библиотека для ArangoDB | ||||
| use ArangoDBClient\Document as _document; | ||||
|  | ||||
| // Встроенные библиотеки | ||||
| use exception; | ||||
|  | ||||
| /** | ||||
|  * Модель сессий | ||||
|  * | ||||
|  * @package mirzaev\site\account\models | ||||
|  * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy> | ||||
|  */ | ||||
| final class session_model extends core | ||||
| { | ||||
|   /** | ||||
|    * Коллекция | ||||
|    */ | ||||
|   public const COLLECTION = 'session'; | ||||
|  | ||||
|   /** | ||||
|    * Данные сессии из базы данных  | ||||
|    */ | ||||
|   public _document $document; | ||||
|  | ||||
|   /** | ||||
|    * Конструктор  | ||||
|    * | ||||
|    * Инициализация сессии и запись в свойство $this->document | ||||
|    * | ||||
|    * @param ?string $hash Хеш сессии в базе данных | ||||
|    * @param ?int $expires Дата окончания работы сессии (используется при создании новой сессии) | ||||
|    * @param array &$errors Журнал ошибок | ||||
|    * | ||||
|    * @return static Инстанция сессии | ||||
|    */ | ||||
|   public function __construct(?string $hash = null, ?int $expires = null, array &$errors = []) | ||||
|   { | ||||
|     try { | ||||
|       if (collection::init(static::$db->session, self::COLLECTION)) { | ||||
|         // Инициализирована коллекция | ||||
|  | ||||
|         if (isset($hash) && $session = collection::search(static::$db->session, sprintf( | ||||
|           <<<AQL | ||||
|             FOR d IN %s | ||||
|             FILTER d.hash == '$hash' && d.expires > %d | ||||
|             RETURN d | ||||
|           AQL, | ||||
|           self::COLLECTION, | ||||
|           time() | ||||
|         ))) { | ||||
|           // Найдена сессия по хешу | ||||
|  | ||||
|           // Запись в свойство | ||||
|           $this->document = $session; | ||||
|         } else if ($session = collection::search(static::$db->session, sprintf( | ||||
|           <<<AQL | ||||
|             FOR d IN %s | ||||
|             FILTER d.ip == '%s' && d.expires > %d | ||||
|             RETURN d | ||||
|           AQL, | ||||
|           self::COLLECTION, | ||||
|           $_SERVER['REMOTE_ADDR'], | ||||
|           time() | ||||
|         ))) { | ||||
|           // Найдена сессия по данным пользователя | ||||
|  | ||||
|           // Запись в свойство | ||||
|           $this->document = $session; | ||||
|         } else { | ||||
|           // Не найдена сессия | ||||
|  | ||||
|           // Запись сессии в базу данных | ||||
|           $_id = document::write(static::$db->session, self::COLLECTION, [ | ||||
|             'ip' => $_SERVER['REMOTE_ADDR'], | ||||
|             'expires' => $expires ?? time() + 604800 | ||||
|           ]); | ||||
|  | ||||
|           if ($session = collection::search(static::$db->session, sprintf( | ||||
|             <<<AQL | ||||
|               FOR d IN %s | ||||
|               FILTER d._id == '$_id' && d.expires > %d | ||||
|               RETURN d | ||||
|             AQL, | ||||
|             self::COLLECTION, | ||||
|             time() | ||||
|           ))) { | ||||
|             // Найдена созданная сессия | ||||
|  | ||||
|             // Запись хеша | ||||
|             $session->hash = sodium_bin2hex(sodium_crypto_generichash($_id)); | ||||
|  | ||||
|             if (document::update(static::$db->session, $session)) { | ||||
|               // Записано обновление | ||||
|  | ||||
|               // Запись в свойство | ||||
|               $this->document = $session; | ||||
|             } else throw new exception('Не удалось записать данные сессии'); | ||||
|           } else throw new exception('Не удалось создать или найти созданную сессию'); | ||||
|         } | ||||
|       } else throw new exception('Не удалось инициализировать коллекцию'); | ||||
|     } catch (exception $e) { | ||||
|       // Запись в журнал ошибок | ||||
|       $errors[] = [ | ||||
|         'text' => $e->getMessage(), | ||||
|         'file' => $e->getFile(), | ||||
|         'line' => $e->getLine(), | ||||
|         'stack' => $e->getTrace() | ||||
|       ]; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Связь сессии с аккаунтом | ||||
|    * | ||||
|    * @param _document $account Инстанция аккаунта | ||||
|    * @param array &$errors Журнал ошибок | ||||
|    * | ||||
|    * @return bool Статус выполнения | ||||
|    */ | ||||
|   public function connect(_document $account, array &$errors = []): bool | ||||
|   { | ||||
|     try { | ||||
|       if ( | ||||
|         collection::init(static::$db->session, self::COLLECTION) | ||||
|         && collection::init(static::$db->session, account::COLLECTION) | ||||
|         && collection::init(static::$db->session, self::COLLECTION . '_edge_' . account::COLLECTION, true) | ||||
|       ) { | ||||
|         // Инициализирована коллекция | ||||
|  | ||||
|         if (document::write(static::$db->session, self::COLLECTION . '_edge_' . account::COLLECTION, [ | ||||
|           '_from' => $this->document->getId(), | ||||
|           '_to' => $account->getId() | ||||
|         ])) { | ||||
|           // Создано ребро: session -> account | ||||
|  | ||||
|           return true; | ||||
|         } else throw new exception('Не удалось создать ребро: session -> account'); | ||||
|       } else throw new exception('Не удалось инициализировать коллекцию'); | ||||
|     } catch (exception $e) { | ||||
|       // Запись в журнал ошибок | ||||
|       $errors[] = [ | ||||
|         'text' => $e->getMessage(), | ||||
|         'file' => $e->getFile(), | ||||
|         'line' => $e->getLine(), | ||||
|         'stack' => $e->getTrace() | ||||
|       ]; | ||||
|     } | ||||
|  | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Поиск связанного аккаунта | ||||
|    * | ||||
|    * @param array &$errors Журнал ошибок | ||||
|    * | ||||
|    * @return ?_document Инстанция аккаунта, если удалось найти | ||||
|    */ | ||||
|   public function account(array &$errors = []): ?_document | ||||
|   { | ||||
|     try { | ||||
|       if ( | ||||
|         collection::init(static::$db->session, self::COLLECTION) | ||||
|         && collection::init(static::$db->session, account::COLLECTION) | ||||
|         && collection::init(static::$db->session, self::COLLECTION . '_edge_' . account::COLLECTION, true) | ||||
|       ) { | ||||
|         // Инициализированы коллекции | ||||
|  | ||||
|         if ($account = collection::search(static::$db->session, sprintf( | ||||
|           <<<AQL | ||||
|             FOR document IN %s | ||||
|             LET edge = ( | ||||
|               FOR edge IN %s | ||||
|               FILTER edge._from == '%s' | ||||
|               SORT edge._key DESC | ||||
|               LIMIT 1 | ||||
|               RETURN edge | ||||
|             ) | ||||
|             FILTER document._id == edge[0]._to | ||||
|             LIMIT 1 | ||||
|             RETURN document | ||||
|           AQL, | ||||
|           account::COLLECTION, | ||||
|           self::COLLECTION . '_edge_' . account::COLLECTION, | ||||
|           $this->document->getId() | ||||
|         ))) { | ||||
|           // Найден аккаунт | ||||
|  | ||||
|           return $account; | ||||
|         } else throw new exception('Не удалось найти аккаунт'); | ||||
|       } else throw new exception('Не удалось инициализировать коллекцию'); | ||||
|     } catch (exception $e) { | ||||
|       // Запись в журнал ошибок | ||||
|       $errors[] = [ | ||||
|         'text' => $e->getMessage(), | ||||
|         'file' => $e->getFile(), | ||||
|         'line' => $e->getLine(), | ||||
|         'stack' => $e->getTrace() | ||||
|       ]; | ||||
|     } | ||||
|  | ||||
|     return null; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Записать | ||||
|    * | ||||
|    * Ищет свойство, если не находит, то ищет его в инстанции документа сессии из базы данных, | ||||
|    * затем записывает в него переданные данные. Инициализация новых свойств происходит в инстанции | ||||
|    * документа сессии из базы данных  | ||||
|    * | ||||
|    * @param string $name Название | ||||
|    * @param mixed $value Содержимое | ||||
|    * | ||||
|    * @return void | ||||
|    */ | ||||
|   public function __set(string $name, mixed $value = null): void | ||||
|   { | ||||
|     if (isset($this->{$name})) $this->{$name} = $value; | ||||
|     else $this->document->{$name} = $value; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Прочитать | ||||
|    * | ||||
|    * Ищет свойство, если не находит, то ищет его в инстанции документа сессии из базы данных | ||||
|    * | ||||
|    * @param string $name Название | ||||
|    * | ||||
|    * @return mixed Данные свойства инстанции сессии или инстанции документа сессии из базы данных | ||||
|    */ | ||||
|   public function __get(string $name): mixed | ||||
|   { | ||||
|     return $this->{$name} ?? $this->document->{$name}; | ||||
|   } | ||||
| } | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
		Reference in New Issue
	
	Block a user