generated from mirzaev/pot
323 lines
13 KiB
PHP
323 lines
13 KiB
PHP
<?php
|
||
|
||
declare(strict_types=1);
|
||
|
||
namespace mirzaev\site\account\models;
|
||
|
||
// Фреймворк 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 extends core
|
||
{
|
||
/**
|
||
* Коллекция
|
||
*/
|
||
public const COLLECTION = 'account';
|
||
|
||
/**
|
||
* Инстанция документа аккаунта в базе данных
|
||
*/
|
||
public ?_document $document;
|
||
|
||
/**
|
||
* Конструктор
|
||
*
|
||
* 1. Проверяет связь сессии с аккаунтом
|
||
* 1.1. Если найдена связь, то возвращает связанный аккаунт (выход)
|
||
* 2. [authenticate === true] Проверяет наличие данных в буфере сессии
|
||
* 2.1 Если найден входной псевдоним и пароли совпадают, то аутентифицирует (выход)
|
||
* 2.2 [register === true] Если найдены данные для регистрации, то регистрирует (выход)
|
||
*
|
||
* @param ?session $session Инстанция сессии
|
||
* @param bool $authenticate Аутентифицировать аккаунт?
|
||
* @param bool $register Регистрировать аккаунт?
|
||
* @param array &$errors Реестр ошибок
|
||
*
|
||
* @return static Инстанция аккаунта
|
||
*/
|
||
public function __construct(?session $session = null, bool $authenticate = false, bool $register = false, array &$errors = [])
|
||
{
|
||
try {
|
||
if (isset($session)) {
|
||
// Получена инстанция сессии
|
||
|
||
if ($account = $session->account()) {
|
||
// Найден связанный с сессией аккаунт
|
||
|
||
// Инициализация инстанции документа аккаунта в базе данных
|
||
$this->document = $account->document;
|
||
|
||
// Связь сессии с аккаунтом
|
||
$session->connect($this, $errors);
|
||
|
||
return $this;
|
||
} else {
|
||
// Не найден связанный с сессией аккаунт
|
||
|
||
if ($authenticate) {
|
||
// Запрошена аутентификация
|
||
|
||
if (!empty($session->buffer['entry'])) {
|
||
// Найдены данные для идентификации в буфере сессии
|
||
|
||
if (!empty($session->buffer['entry']['login'])) {
|
||
// Найдены входной псевдоним в буфере сессии
|
||
|
||
if (($account = self::login($session->buffer['entry']['login'])) instanceof self) {
|
||
// Найден аккаунт (игнорируются ошибки)
|
||
|
||
if (isset($account->password) && $account->password === '') {
|
||
// Не имеет пароля аккаунт
|
||
|
||
// Проверка отсутствия переданного пароля
|
||
if (isset($session->buffer['entry']['password']) && $session->buffer['entry']['password'] !== '') throw new exception('Неправильный пароль');
|
||
|
||
// Инициализация инстанции документа аккаунта в базе данных
|
||
$this->document = $account->document;
|
||
|
||
// Связь сессии с аккаунтом
|
||
$session->connect($this, $errors);
|
||
|
||
// Удаление использованных данных из буфера сессии
|
||
$session->write(['entry' => ['password' => null]]);
|
||
|
||
return $this;
|
||
} else if (!empty($session->buffer['entry']['password'])) {
|
||
// Найден пароль в буфере сессии
|
||
|
||
if (sodium_crypto_pwhash_str_verify($account->password, $session->buffer['entry']['password'])) {
|
||
// Аутентифицирован аккаунт (прошёл проверку пароль, либо аккаунт не имеет пароля)
|
||
|
||
// Инициализация инстанции документа аккаунта в базе данных
|
||
$this->document = $account->document;
|
||
|
||
// Связь сессии с аккаунтом
|
||
$session->connect($this, $errors);
|
||
|
||
// Удаление использованных данных из буфера сессии
|
||
$session->write(['entry' => ['password' => null]]);
|
||
|
||
return $this;
|
||
} else throw new exception('Неправильный пароль');
|
||
} throw new exception('Неправильный пароль');
|
||
} else {
|
||
// Не найден аккаунт
|
||
|
||
if ($register) {
|
||
// Запрошена регистрация
|
||
|
||
if (!empty($session->buffer['entry']['invite'])) {
|
||
// Найден ключ приглашения в буфере сессии
|
||
|
||
// Проверка наличия переданного пароля
|
||
if (!isset($session->buffer['entry']['password'])) throw new exception('Не найден пароль в буфере сессии');
|
||
|
||
if (self::create(
|
||
[
|
||
'login' => $session->buffer['entry']['login'],
|
||
'password' => $session->buffer['entry']['password'] === ''
|
||
? ''
|
||
: sodium_crypto_pwhash_str(
|
||
$session->buffer['entry']['password'],
|
||
SODIUM_CRYPTO_PWHASH_OPSLIMIT_SENSITIVE,
|
||
SODIUM_CRYPTO_PWHASH_MEMLIMIT_SENSITIVE
|
||
)
|
||
],
|
||
$errors
|
||
)) {
|
||
// Зарегистрирован аккаунт
|
||
|
||
if (($account = self::login($session->buffer['entry']['login'], $errors)) instanceof self) {
|
||
// Найден аккаунт
|
||
|
||
// Инициализация инстанции документа аккаунта в базе данных
|
||
$this->document = $account->document;
|
||
|
||
// Связь сессии с аккаунтом
|
||
$session->connect($this, $errors);
|
||
|
||
// Удаление использованных данных из буфера сессии
|
||
$session->write(['entry' => ['password' => null, 'invite' => null]]);
|
||
|
||
return $this;
|
||
} else throw new exception('Не удалось аутентифицировать аккаунт после его регистрации');
|
||
} else throw new exception('Не удалось зарегистрировать аккаунт');
|
||
} 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 string $login Входной псевдоним
|
||
* @param array &$errors Реестр ошибок
|
||
*
|
||
* @return ?self Инстанция аккаунта, если аутентифицирован
|
||
*/
|
||
public static function login(string $login, array &$errors = []): ?self
|
||
{
|
||
try {
|
||
if (collection::init(static::$db->session, self::COLLECTION)) {
|
||
// Инициализирована коллекция
|
||
|
||
// Инициализация инстанции аккаунта
|
||
$instance = new self;
|
||
|
||
// Поиск инстанции аккаунта в базе данных
|
||
$instance->document = collection::search(
|
||
static::$db->session,
|
||
sprintf(
|
||
<<<'AQL'
|
||
FOR d IN %s
|
||
FILTER d.login == '%s'
|
||
RETURN d
|
||
AQL,
|
||
self::COLLECTION,
|
||
$login
|
||
)
|
||
);
|
||
|
||
if ($instance->document instanceof _document) return $instance;
|
||
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 array $data Данные аккаунта
|
||
* @param array &$errors Реестр ошибок
|
||
*
|
||
* @return bool Создан аккаунт?
|
||
*/
|
||
public static function create(array $data = [], array &$errors = []): bool
|
||
{
|
||
try {
|
||
if (collection::init(static::$db->session, self::COLLECTION))
|
||
if (document::write(static::$db->session, self::COLLECTION, $data)) return true;
|
||
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 false;
|
||
}
|
||
|
||
/**
|
||
* Записать
|
||
*
|
||
* Записывает свойство в инстанцию документа аккаунта из базы данных
|
||
*
|
||
* @param string $name Название
|
||
* @param mixed $value Содержимое
|
||
*
|
||
* @return void
|
||
*/
|
||
public function __set(string $name, mixed $value = null): void
|
||
{
|
||
$this->document->{$name} = $value;
|
||
}
|
||
|
||
/**
|
||
* Прочитать
|
||
*
|
||
* Читает свойство из инстанции документа аккаунта из базы данных
|
||
*
|
||
* @param string $name Название
|
||
*
|
||
* @return mixed Данные свойства инстанции аккаунта или инстанции документа аккаунта из базы данных
|
||
*/
|
||
public function __get(string $name): mixed
|
||
{
|
||
return $this->document->{$name};
|
||
}
|
||
|
||
/**
|
||
* Проверить инициализированность
|
||
*
|
||
* Проверяет инициализированность свойства в инстанции документа аккаунта из базы данных
|
||
*
|
||
* @param string $name Название
|
||
*
|
||
* @return bool Свойство инициализировано?
|
||
*/
|
||
public function __isset(string $name): bool
|
||
{
|
||
return isset($this->document->{$name});
|
||
}
|
||
|
||
/**
|
||
* Удалить
|
||
*
|
||
* Деинициализировать свойство в инстанции документа аккаунта из базы данных
|
||
*
|
||
* @param string $name Название
|
||
*
|
||
* @return void
|
||
*/
|
||
public function __unset(string $name): void
|
||
{
|
||
unset($this->document->{$name});
|
||
}
|
||
|
||
/**
|
||
* Выполнить метод
|
||
*
|
||
* Выполнить метод в инстанции документа аккаунта из базы данных
|
||
*
|
||
* @param string $name Название
|
||
* @param array $arguments Аргументы
|
||
*/
|
||
public function __call(string $name, array $arguments = [])
|
||
{
|
||
if (method_exists($this->document, $name)) return $this->document->{$name}($arguments);
|
||
}
|
||
}
|