*/ final class session extends core implements document_interface, collection_interface { use status, document_trait, buffer, cart { buffer::write as write; cart::initialize as cart; } /** * Collection name * * @var string COLLECTION Name of the collection in ArangoDB */ final public const string COLLECTION = 'session'; /** * Session verification type * * @var verification VERIFICATION Type of session verification */ final public const verification VERIFICATION = verification::hash_else_address; /** * Constructor * * Initialize session and write into 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 void */ public function __construct(?string $$hash = null, ?int $$expires = null, array &$$errors = []) { try { if (collection::initialize(static::COLLECTION, static::TYPE, errors: $$errors)) { // Initialized the collection if (isset($$hash) && $$document = $$this->hash($$hash, errors: $$errors)) { // Found the 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: $$errors)) { // Found the 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 the instance of the ArangoDB document of session // Initializing a new session and write they into ArangoDB $$_id = document::write( static::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::execute( <<<'AQL' FOR d IN @@collection FILTER d._id == @_id && d.expires > @time && d.active == true RETURN d AQL, [ '@collection' => static::COLLECTION, '_id' => $$_id, 'time' => time() ], errors: $$errors )) { // Found the instance of just created new session // Generating a hash and write into the instance of the ArangoDB document of session property $$session->hash = sodium_bin2hex(sodium_crypto_generichash($$_id)); if (document::update($$session, errors: $$errors)) { // Writed to ArangoDB // Writing instance of the session document from ArangoDB to the property of the implementing object $$this->__document($$session); } else throw new exception('Failed to write the session data'); } else throw new exception('Failed to create or find just created session'); } } else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION); } catch (exception $$e) { // Writing to the registry of errors $$errors[] = [ 'text' => $$e->getMessage(), 'file' => $$e->getFile(), 'line' => $$e->getLine(), 'stack' => $$e->getTrace() ]; } } /** * 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::initialize(static::COLLECTION, static::TYPE, errors: $$errors)) { // Collection initialized // Search the session data in ArangoDB return collection::execute( <<<'AQL' FOR d IN @@collection FILTER d.hash == @hash && d.expires > @time && d.active == true RETURN d AQL, [ '@collection' => static::COLLECTION, 'hash' => $$hash, 'time' => time() ], errors: $$errors ); } else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION); } catch (exception $$e) { // Writing 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::initialize(static::COLLECTION, static::TYPE, errors: $$errors)) { // Collection initialized // Search the session data in ArangoDB return collection::execute( <<<'AQL' FOR d IN @@collection FILTER d.address == @address && d.expires > @time && d.active == true SORT d.updated DESC LIMIT 1 RETURN d AQL, [ '@collection' => static::COLLECTION, 'address' => $$address, 'time' => time() ], errors: $$errors ); } else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION); } catch (exception $$e) { // Writing to the registry of errors $$errors[] = [ 'text' => $$e->getMessage(), 'file' => $$e->getFile(), 'line' => $$e->getLine(), 'stack' => $$e->getTrace() ]; } // Exit (fail) return null; }}