*/ 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); } }