huesos/mirzaev/arming_bot/system/models/session.php

339 lines
10 KiB
PHP
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
declare(strict_types=1);
namespace mirzaev\arming_bot\models;
// Files of the project
use mirzaev\arming_bot\models\account,
mirzaev\arming_bot\models\enumerations\session as verification,
mirzaev\arming_bot\models\traits\status,
mirzaev\arming_bot\models\traits\document as arangodb_document_trait,
mirzaev\arming_bot\models\interfaces\document as arangodb_document_interface;
// Framework for ArangoDB
use mirzaev\arangodb\collection,
mirzaev\arangodb\document;
// Library для ArangoDB
use ArangoDBClient\Document as _document;
// Built-in libraries
use exception;
/**
* Model of a session
*
* @package mirzaev\arming_bot\models
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
*/
final class session extends core implements arangodb_document_interface
{
use status, arangodb_document_trait;
/**
* Name of the collection in ArangoDB
*/
final public const string COLLECTION = 'session';
/**
* Type of session verification(
*/
public const verification VERIFICATION = verification::hash_else_address;
/**
* Constructor of an instance
*
* Initialize of a session and write them to the $this->document property
*
* @param ?string $hash Hash of the session in ArangoDB
* @param ?int $expires Date of expiring of the session (used for creating a new session)
* @param array &$errors Registry of errors
*
* @return static instance of the ArangoDB document of session
*/
public function __construct(?string $hash = null, ?int $expires = null, array &$errors = [])
{
try {
if (collection::init(static::$arangodb->session, self::COLLECTION)) {
// Initialized the collection
if (isset($hash) && $document = $this->hash($hash, $errors)) {
// Found an instance of the ArangoDB document of session and received a session hash
// Writing document instance of the session from ArangoDB to the property of the implementing object
$this->document = $document;
} else if (static::VERIFICATION === verification::hash_else_address && $document = $this->address($_SERVER['REMOTE_ADDR'], $errors)) {
// Found an instance of the ArangoDB document of session and received a session hash
// Writing document instance of the session from ArangoDB to the property of the implementing object
$this->document = $document;
} else {
// Not found an instance of the ArangoDB document of session
// Initializing a new session and write they into ArangoDB
$_id = document::write($this::$arangodb->session, self::COLLECTION, [
'active' => true,
'expires' => $expires ?? time() + 604800,
'address' => $_SERVER['REMOTE_ADDR'],
'x-forwarded-for' => $_SERVER['HTTP_X_FORWARDED_FOR'] ?? null,
'referer' => $_SERVER['HTTP_REFERER'] ?? null,
'useragent' => $_SERVER['HTTP_USER_AGENT'] ?? null
]);
if ($session = collection::search($this::$arangodb->session, sprintf(
<<<AQL
FOR d IN %s
FILTER d._id == '%s' && d.expires > %d && d.active == true
RETURN d
AQL,
self::COLLECTION,
$_id,
time()
))) {
// Found an instance of just created new session
// Generate a hash and write into an instance of the ArangoDB document of session property
$session->hash = sodium_bin2hex(sodium_crypto_generichash($_id));
if (document::update($this::$arangodb->session, $session)) {
// Is writed update
// Writing document instance of the session from ArangoDB to the property of the implementing object
$this->document = $session;
} else throw new exception('Could not write the session data');
} else throw new exception('Could not create or find just created session');
}
} else throw new exception('Could not initialize the collection');
} catch (exception $e) {
// Write to the registry of errors
$errors[] = [
'text' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'stack' => $e->getTrace()
];
}
}
/**
* Search for a connected account
*
* @param array &$errors Registry of errors
*
* @return account|null An object implementing the account instance from the database, if found
*/
public function account(array &$errors = []): ?account
{
try {
if (collection::init(core::$arangodb->session, static::COLLECTION)) {
if (collection::init(core::$arangodb->session, 'connect', true)) {
if (collection::init(core::$arangodb->session, account::COLLECTION)) {
// Инициализирована коллекция
if ($document = collection::search(
core::$arangodb->session,
sprintf(
<<<AQL
FOR v IN INBOUND "%s" GRAPH sessions
SORT v.created DESC
LIMIT 1
RETURN v
AQL,
$this->getId(),
)
)) {
// Найден аккаунт
// Инициализация объекта аккаунта
$account = new account;
// Запись инстанции документа в объект
$account->__document($document);
// Exit (success)
return $account;
} else return null;
} else throw new exception('Failed to initialize document collection: ' . account::COLLECTION);
} else throw new exception('Failed to initialize edge collection: connect');
} else throw new exception('Failed to initialize document collection: ' . static::COLLECTION);
} catch (exception $e) {
// Write to the registry of errors
$errors[] = [
'text' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'stack' => $e->getTrace()
];
}
// Exit (fail)
return null;
}
/**
* Connect account to session
*
* @param account $account Account
* @param array &$errors Registry of errors
*
* @return string|null The identifier of the created edge of the "connect" collection, if created
*/
public function connect(account $account, array &$errors = []): ?string
{
try {
if (collection::init(core::$arangodb->session, static::COLLECTION)) {
if (collection::init(core::$arangodb->session, 'connect', true)) {
if (collection::init(core::$arangodb->session, account::COLLECTION)) {
// Collections initialized
// Writing document and exit (success)
return document::write(
core::$arangodb->session,
'connect',
[
'_from' => $account->getId(),
'_to' => $this->document->getId()
]
);
} else throw new exception('Failed to initialize document collection: ' . account::COLLECTION);
} else throw new exception('Failed to initialize edge collection: connect');
} else throw new exception('Failed to initialize document collection: ' . static::COLLECTION);
} catch (exception $e) {
// Write to the registry of errors
$errors[] = [
'text' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'stack' => $e->getTrace()
];
}
// Exit (fail)
return null;
}
/**
* Search by hash
*
* Search for the session in ArangoDB by hash
*
* @param string $hash Hash of the session in ArangoDB
* @param array &$errors Registry of errors
*
* @return _document|null instance of document of the session in ArangoDB
*/
public static function hash(string $hash, array &$errors = []): ?_document
{
try {
if (collection::init(core::$arangodb->session, static::COLLECTION)) {
// Collection initialized
// Search the session data in ArangoDB
return collection::search(static::$arangodb->session, sprintf(
<<<AQL
FOR d IN %s
FILTER d.hash == '%s' && d.expires > %d && d.active == true
RETURN d
AQL,
static::COLLECTION,
$hash,
time()
));
} else throw new exception('Failed to initialize document collection: ' . static::COLLECTION);
} catch (exception $e) {
// Write to the registry of errors
$errors[] = [
'text' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'stack' => $e->getTrace()
];
}
// Exit (fail)
return null;
}
/**
* Search by IP-address
*
* Search for the session in ArangoDB by IP-address
*
* @param string $address IP-address writed to the session in ArangoDB
* @param array &$errors Registry of errors
*
* @return _document|null instance of document of the session in ArangoDB
*/
public static function address(string $address, array &$errors = []): ?_document
{
try {
if (collection::init(core::$arangodb->session, static::COLLECTION)) {
// Collection initialized
// Search the session data in ArangoDB
return collection::search(static::$arangodb->session, sprintf(
<<<AQL
FOR d IN %s
FILTER d.address == '%s' && d.expires > %d && d.active == true
RETURN d
AQL,
static::COLLECTION,
$address,
time()
));
} else throw new exception('Failed to initialize document collection: ' . static::COLLECTION);
} catch (exception $e) {
// Write to the registry of errors
$errors[] = [
'text' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'stack' => $e->getTrace()
];
}
// Exit (fail)
return null;
}
/**
* Write to buffer of the session
*
* @param array $data Data for merging
* @param array &$errors Registry of errors
*
* @return bool Is data has written into the session buffer?
*/
public function write(array $data, array &$errors = []): bool
{
try {
if (collection::init($this::$arangodb->session, self::COLLECTION)) {
// Initialized the collection
// An instance of the ArangoDB document of session is initialized?
if (!isset($this->document)) throw new exception('An instance of the ArangoDB document of session is not initialized');
// Write data into buffwer of an instance of the ArangoDB document of session
$this->document->buffer = array_replace_recursive(
$this->document->buffer ?? [],
[$_SERVER['INTERFACE'] => array_replace_recursive($this->document->buffer[$_SERVER['INTERFACE']] ?? [], $data)]
);
// Write to ArangoDB and exit (success)
return document::update($this::$arangodb->session, $this->document) ? true : throw new exception('Не удалось записать данные в буфер сессии');
} else throw new exception('Could not initialize the collection');
} catch (exception $e) {
// Write to the registry of errors
$errors[] = [
'text' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'stack' => $e->getTrace()
];
}
return false;
}
}