Основная разработка завершена
This commit is contained in:
parent
c11cc93a19
commit
80c1673494
|
@ -28,12 +28,12 @@
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
"mirzaev\\yii2\\arangodb\\sessions\\": "mirzaev/yii2-arangodb-sessions"
|
"mirzaev\\yii2\\arangodb\\sessions\\": "mirzaev/yii2/arangodb/sessions"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"autoload-dev": {
|
"autoload-dev": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
"mirzaev\\yii2\\arangodb\\sessions\\tests\\": "mirzaev/yii2-arangodb-sessions/tests"
|
"mirzaev\\yii2\\arangodb\\sessions\\tests\\": "mirzaev/yii2/arangodb/sessions/tests"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,288 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace mirzaev\yii2\arangodb\sessions;
|
|
||||||
|
|
||||||
use yii;
|
|
||||||
use yii\web\DbSession;
|
|
||||||
use yii\db\PdoValue;
|
|
||||||
use yii\di\Instance;
|
|
||||||
use yii\base\InvalidConfigException;
|
|
||||||
|
|
||||||
use mirzaev\yii2\arangodb\Connection;
|
|
||||||
use mirzaev\yii2\arangodb\Query;
|
|
||||||
|
|
||||||
use Exception;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Реализация DbSession под ArangoDB
|
|
||||||
*
|
|
||||||
* @package Yii2\ArangoDB
|
|
||||||
* @author Arsen Mirzaev Tatyano-Muradovich <red@hood.su>
|
|
||||||
*/
|
|
||||||
final class ArangoDbSession extends DbSession {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Идентификатор компонента (Component ID)
|
|
||||||
*/
|
|
||||||
public Connection|array|string $db = 'arangodb';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Название документа для хранения данных сеанса (Document name for storing session data)
|
|
||||||
*/
|
|
||||||
public string $sessions = 'sessions';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Буфер данных для записи в документ сессий (Data buffer for write to session document)
|
|
||||||
*/
|
|
||||||
protected array $buffer = [];
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Инициализация компонента (Component initialization)
|
|
||||||
*
|
|
||||||
* @throws InvalidConfigException if $this->db is invalid.
|
|
||||||
*/
|
|
||||||
public function init(): self
|
|
||||||
{
|
|
||||||
parent::init();
|
|
||||||
|
|
||||||
return $this->db = Instance::ensure($this->db, Connection::class);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Открыть сессию (Open session)
|
|
||||||
*
|
|
||||||
* @param string $path session save path
|
|
||||||
* @param string $name session name
|
|
||||||
*
|
|
||||||
* @return bool Статус открытия сессии (Session opening status)
|
|
||||||
*
|
|
||||||
* @internal Do not call this method directly
|
|
||||||
*/
|
|
||||||
public function openSession($path, $name): bool
|
|
||||||
// public function openSession(string $save, string $session_name): bool
|
|
||||||
{
|
|
||||||
if ($this->getUseStrictMode()) {
|
|
||||||
$id = $this->getId();
|
|
||||||
if (!$this->getReadQuery($id)->exists()) {
|
|
||||||
// This session id does not exist, mark it for forced regeneration
|
|
||||||
$this->_forceRegenerateId = $id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return parent::openSession($path, $name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Метод не создаст эффект пока сессия неактивна (getIsActive()). Убедитесь, что открыли (open()) перед вызовом
|
|
||||||
* This method has no effect when session is not [[getIsActive()|active]]. Make sure to call [[open()]] before calling it.
|
|
||||||
*
|
|
||||||
* @param bool $delete Удалить прошлую сессию (Delete old session)
|
|
||||||
*
|
|
||||||
* @see $this->open()
|
|
||||||
* @see $this->getIsActive()
|
|
||||||
* @see https://secure.php.net/session_regenerate_id
|
|
||||||
*/
|
|
||||||
public function regenerateID($delete = false): void
|
|
||||||
// public function regenerateID(bool $delete = false): void
|
|
||||||
{
|
|
||||||
$oldID = session_id();
|
|
||||||
|
|
||||||
// if no session is started, there is nothing to regenerate
|
|
||||||
if (empty($oldID)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
parent::regenerateID(false);
|
|
||||||
|
|
||||||
$newID = session_id();
|
|
||||||
|
|
||||||
// if session id regeneration failed, no need to create/update it.
|
|
||||||
if (empty($newID)) {
|
|
||||||
Yii::warning('Failed to generate new session ID', __METHOD__);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$row = $this->db->useMaster(function() use ($oldID) {
|
|
||||||
return (new Query())->from($this->sessionTable)
|
|
||||||
->where(['id' => $oldID])
|
|
||||||
->createCommand($this->db)
|
|
||||||
->queryOne();
|
|
||||||
});
|
|
||||||
|
|
||||||
if ($row !== false) {
|
|
||||||
if ($delete) {
|
|
||||||
$this->db->createCommand()
|
|
||||||
->update($this->sessionTable, ['id' => $newID], ['id' => $oldID])
|
|
||||||
->execute();
|
|
||||||
} else {
|
|
||||||
$row['id'] = $newID;
|
|
||||||
$this->db->createCommand()
|
|
||||||
->insert($this->sessionTable, $row)
|
|
||||||
->execute();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// shouldn't reach here normally
|
|
||||||
$this->db->createCommand()
|
|
||||||
->insert($this->sessionTable, $this->composeFields($newID, ''))
|
|
||||||
->execute();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Закрыть сессию (Close session)
|
|
||||||
*
|
|
||||||
* Закрывает текущую сессию и сохраняет данные
|
|
||||||
* Ends the current session and store session data
|
|
||||||
*/
|
|
||||||
public function close(): void
|
|
||||||
{
|
|
||||||
if ($this->getIsActive()) {
|
|
||||||
// prepare writeCallback fields before session closes
|
|
||||||
$this->fields = $this->composeFields();
|
|
||||||
YII_DEBUG ? session_write_close() : @session_write_close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Прочитать сессию (Read session)
|
|
||||||
*
|
|
||||||
* @param string $id Идентификатор сессии (Session ID)
|
|
||||||
*
|
|
||||||
* @return string Данные сессии (The session data)
|
|
||||||
*
|
|
||||||
* @internal Do not call this method directly
|
|
||||||
*/
|
|
||||||
public function readSession($id): string
|
|
||||||
// public function readSession(string $id): string
|
|
||||||
{
|
|
||||||
$query = $this->getReadQuery($id);
|
|
||||||
|
|
||||||
if ($this->readCallback !== null) {
|
|
||||||
$fields = $query->one($this->db);
|
|
||||||
return $fields === false ? '' : $this->extractData($fields);
|
|
||||||
}
|
|
||||||
|
|
||||||
$data = $query->select(['data'])->scalar($this->db);
|
|
||||||
return $data === false ? '' : $data;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Записать сессию (Write session)
|
|
||||||
*
|
|
||||||
* @param string $id Идентификатор сессии (Session ID)
|
|
||||||
* @param string $data Данные сессии (Session data)
|
|
||||||
*
|
|
||||||
* @return bool Статус записи сессии (Session writing status)
|
|
||||||
*
|
|
||||||
* @internal Do not call this method directly
|
|
||||||
*/
|
|
||||||
public function writeSession($id, $data): bool
|
|
||||||
// public function writeSession(string $id, string $data): bool
|
|
||||||
{
|
|
||||||
if ($this->getUseStrictMode() && $id === $this->_forceRegenerateId) {
|
|
||||||
//Ignore write when forceRegenerate is active for this id
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// exception must be caught in session write handler
|
|
||||||
// https://secure.php.net/manual/en/function.session-set-save-handler.php#refsect1-function.session-set-save-handler-notes
|
|
||||||
try {
|
|
||||||
// ensure backwards compatability (fixed #9438)
|
|
||||||
if ($this->writeCallback && !$this->fields) {
|
|
||||||
$this->fields = $this->composeFields();
|
|
||||||
}
|
|
||||||
// ensure data consistency
|
|
||||||
if (!isset($this->fields['data'])) {
|
|
||||||
$this->fields['data'] = $data;
|
|
||||||
} else {
|
|
||||||
$_SESSION = $this->fields['data'];
|
|
||||||
}
|
|
||||||
// ensure 'id' and 'expire' are never affected by [[writeCallback]]
|
|
||||||
$this->fields = array_merge($this->fields, [
|
|
||||||
'id' => $id,
|
|
||||||
'expire' => time() + $this->getTimeout(),
|
|
||||||
]);
|
|
||||||
$this->fields = $this->typecastFields($this->fields);
|
|
||||||
$this->db->createCommand()->upsert($this->sessionTable, $this->fields)->execute();
|
|
||||||
$this->fields = [];
|
|
||||||
} catch (Exception $e) {
|
|
||||||
Yii::$app->errorHandler->handleException($e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Удалить сессию (Delete session)
|
|
||||||
*
|
|
||||||
* @param string $id Идентификатор сессии (Session ID)
|
|
||||||
*
|
|
||||||
* @return bool Статус удаления сессии (Session deleting status)
|
|
||||||
*
|
|
||||||
* @internal Do not call this method directly
|
|
||||||
*/
|
|
||||||
public function destroySession($id)
|
|
||||||
{
|
|
||||||
$this->db->createCommand()
|
|
||||||
->delete($this->sessionTable, ['id' => $id])
|
|
||||||
->execute();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Удалить неиспользуемые данные (Delete garbage)
|
|
||||||
*.
|
|
||||||
* @param int $max Периодичность очистки в секундах (Cleaning frequency in seconds)
|
|
||||||
*
|
|
||||||
* @return bool Статус очистки неиспользуемых данных сессии (Session garbage deleting status)
|
|
||||||
*
|
|
||||||
* @internal Do not call this method directly
|
|
||||||
*/
|
|
||||||
public function gcSession($max): bool
|
|
||||||
// public function gcSession(int $max): bool
|
|
||||||
{
|
|
||||||
$this->db->createCommand()
|
|
||||||
->delete($this->sessionTable, '[[expire]]<:expire', [':expire' => time()])
|
|
||||||
->execute();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Генерация запроса для чтения сеанса (Generating a query to read a session)
|
|
||||||
*
|
|
||||||
* @param string $id Идентификатор сессии (Session ID)
|
|
||||||
*/
|
|
||||||
protected function getReadQuery($id): Query
|
|
||||||
// protected function getReadQuery(string $id): Query
|
|
||||||
{
|
|
||||||
return (new Query())
|
|
||||||
->from($this->sessionTable)
|
|
||||||
->where('[[expire]]>:expire AND [[id]]=:id', [':expire' => time(), ':id' => $id]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Конвертация для отправки в PDO (Convertation for sending to PDO)
|
|
||||||
*
|
|
||||||
* Default implementation casts field `data` to `\PDO::PARAM_LOB`.
|
|
||||||
* You can override this method in case you need special type casting.
|
|
||||||
*
|
|
||||||
* @param array $data Данные для конвертации (Data for convertation) [name => value]
|
|
||||||
*/
|
|
||||||
protected function typecastFields($data): array
|
|
||||||
// protected function typecastFields(array $data): array
|
|
||||||
{
|
|
||||||
// if (isset($data['data']) && !is_array($data['data']) && !is_object($data['data'])) {
|
|
||||||
// $data['data'] = new PdoValue($data['data'], \PDO::PARAM_LOB);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return $data;
|
|
||||||
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,381 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace mirzaev\yii2\arangodb\sessions;
|
||||||
|
|
||||||
|
use yii;
|
||||||
|
use yii\di\Instance;
|
||||||
|
use yii\base\InvalidConfigException;
|
||||||
|
|
||||||
|
use mirzaev\yii2\arangodb\Connection;
|
||||||
|
use mirzaev\yii2\arangodb\Query;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Реализация DbSession под ArangoDB
|
||||||
|
*
|
||||||
|
* @property Connection|array|string $database Идентификатор компонента (Component ID)
|
||||||
|
* @property string $document Название документа для хранения данных сеанса (Document name for storing session data)
|
||||||
|
* @property array $buffer Буфер данных для записи в документ сессии (Data buffer for write to session document)
|
||||||
|
*
|
||||||
|
* @method public init() Инициализация компонента (Component initialization)
|
||||||
|
* @method public openSession() Открыть сессию (Open session)
|
||||||
|
* @method public close() Закрыть сессию (Close session)
|
||||||
|
* @method public regenerateID() Обновление сессии (Update session)
|
||||||
|
* @method public readSession() Прочитать сессию (Read session)
|
||||||
|
* @method public searchSession() Найти сессию (Search session)
|
||||||
|
* @method public initSession() Инициализировать сессию (Initialize session)
|
||||||
|
* @method public writeSession() Записать сессию (Write session)
|
||||||
|
* @method public destroySession() Удалить сессию (Delete session)
|
||||||
|
* @method public gcSession() Удалить неиспользуемые данные (Delete garbage)
|
||||||
|
* @method protected getReadQuery() Генерация запроса для чтения сеанса (Generating a query to read a session)
|
||||||
|
* @method protected typecastFields() Конвертация для отправки в PDO (Convertation for sending to PDO)
|
||||||
|
*
|
||||||
|
* @example ./Migrations/create_sessions_collection.php
|
||||||
|
*
|
||||||
|
* @uses $this->gcSession() Do not call this method directly
|
||||||
|
* @uses $this->openSession() Do not call this method directly
|
||||||
|
* @uses $this->readSession() Do not call this method directly
|
||||||
|
* @uses $this->writeSession() Do not call this method directly
|
||||||
|
* @uses $this->destroySession() Do not call this method directly
|
||||||
|
*
|
||||||
|
* @see yii\web\DbSession Наследует, копирует (Inherit, copy)
|
||||||
|
*
|
||||||
|
* @package yii2\ArangoDB
|
||||||
|
* @author Arsen Mirzaev Tatyano-Muradovich <red@hood.su>
|
||||||
|
*/
|
||||||
|
final class ArangoDbSession extends MultiFieldSession
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Идентификатор компонента (Component ID)
|
||||||
|
*/
|
||||||
|
public Connection|array|string $database = 'arangodb';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Название документа для хранения данных сеанса (Document name for storing session data)
|
||||||
|
*/
|
||||||
|
public string $document = 'sessions';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Буфер данных для записи в документ сессии (Data buffer for write to session document)
|
||||||
|
*/
|
||||||
|
protected array $buffer = [];
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Инициализация компонента (Component initialization)
|
||||||
|
*
|
||||||
|
* @throws InvalidConfigException if $this->database is invalid.
|
||||||
|
*/
|
||||||
|
public function init(): Connection
|
||||||
|
{
|
||||||
|
return $this->database = Instance::ensure($this->database, Connection::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Открыть сессию (Open session)
|
||||||
|
*
|
||||||
|
* @param string $path Путь для сохранения сессии (Path to save session)
|
||||||
|
* @param string $name Имя сессии (Session name)
|
||||||
|
*
|
||||||
|
* @return bool Статус открытия сессии (Session opening status)
|
||||||
|
*/
|
||||||
|
public function openSession($path, $name): bool
|
||||||
|
// public function openSession(string $path, string $name): bool
|
||||||
|
{
|
||||||
|
if ($this->getUseStrictMode()) {
|
||||||
|
|
||||||
|
$id = $this->getId();
|
||||||
|
|
||||||
|
if (!$this->getReadQuery($id)->exists()) {
|
||||||
|
// This session id does not exist, mark it for forced regeneration
|
||||||
|
$this->_forceRegenerateId = $id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent::openSession($path, $name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Закрыть сессию (Close session)
|
||||||
|
*
|
||||||
|
* Закрывает текущую сессию и сохраняет данные
|
||||||
|
* Ends the current session and store session data
|
||||||
|
*/
|
||||||
|
public function close(): void
|
||||||
|
{
|
||||||
|
if ($this->getIsActive()) {
|
||||||
|
// prepare writeCallback fields before session closes
|
||||||
|
|
||||||
|
$this->buffer = $this->composeFields();
|
||||||
|
|
||||||
|
YII_DEBUG ? session_write_close() : @session_write_close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Обновление сессии (Update session)
|
||||||
|
*
|
||||||
|
* Метод не создаст эффект пока сессия неактивна (getIsActive()). Убедитесь, что открыли (open()) перед вызовом
|
||||||
|
* This method has no effect when session is not [[getIsActive()|active]]. Make sure to call [[open()]] before calling it.
|
||||||
|
*
|
||||||
|
* @param bool $rewrite Перезаписать прошлую сессию (Rewrite old session)
|
||||||
|
*
|
||||||
|
* @see $this->open()
|
||||||
|
* @see $this->getIsActive()
|
||||||
|
*
|
||||||
|
* @tutorial https://secure.php.net/session_regenerate_id
|
||||||
|
*/
|
||||||
|
public function regenerateID($rewrite = false): void
|
||||||
|
// public function regenerateID(bool $rewrite = false): void
|
||||||
|
{
|
||||||
|
// Инициализация
|
||||||
|
$oldID = session_id();
|
||||||
|
|
||||||
|
if (empty($oldID)) {
|
||||||
|
// if no session is started, there is nothing to regenerate
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// parent::regenerateID(false);
|
||||||
|
|
||||||
|
$newID = session_id();
|
||||||
|
|
||||||
|
// if session id regeneration failed, no need to create/update it.
|
||||||
|
if (empty($newID)) {
|
||||||
|
yii::warning('Failed to generate new session ID', __METHOD__);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$row = (new Query())
|
||||||
|
->in($this->document)
|
||||||
|
->where(['hash' => $oldID])
|
||||||
|
->one($this->database);
|
||||||
|
|
||||||
|
if ($row) {
|
||||||
|
if ($rewrite) {
|
||||||
|
// Обновить
|
||||||
|
|
||||||
|
(new Query())
|
||||||
|
->in($this->document)
|
||||||
|
->where(['hash' => $newID])
|
||||||
|
->update(['hash' => $oldID], db: $this->database);
|
||||||
|
} else {
|
||||||
|
// Создать
|
||||||
|
|
||||||
|
(new Query())
|
||||||
|
->in($this->document)
|
||||||
|
->insert(['hash' => $newID], db: $this->database);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// shouldn't reach here normally
|
||||||
|
(new Query())
|
||||||
|
->in($this->document)
|
||||||
|
->insert($this->composeFields($newID, ''), db: $this->database);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Прочитать сессию (Read session)
|
||||||
|
*
|
||||||
|
* @param string $id Идентификатор сессии (Session ID)
|
||||||
|
*
|
||||||
|
* @return string Данные сессии (The session data)
|
||||||
|
*/
|
||||||
|
public function readSession($id): string
|
||||||
|
// public function readSession(string $id): string
|
||||||
|
{
|
||||||
|
$query = $this->getReadQuery($id);
|
||||||
|
|
||||||
|
|
||||||
|
if ($this->readCallback !== null) {
|
||||||
|
$data = $query->one($this->database);
|
||||||
|
|
||||||
|
return $data ? $this->extractData($data) : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = $query->select(['data' => 'data'])->one($this->database);
|
||||||
|
|
||||||
|
return $data['data'] ?? '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Найти сессию (Search session)
|
||||||
|
*
|
||||||
|
* @param string $id Идентификатор сессии (Session ID)
|
||||||
|
*
|
||||||
|
* @return array|null Сессия, если найдена (Session, if founded)
|
||||||
|
*/
|
||||||
|
public function searchSession(string $id): ?array
|
||||||
|
{
|
||||||
|
$session = (new Query())
|
||||||
|
->in($this->document)
|
||||||
|
->where(['hash' => $id])
|
||||||
|
->one($this->database);
|
||||||
|
|
||||||
|
return $session ? $session : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Инициализировать сессию (Initialize session)
|
||||||
|
*
|
||||||
|
* @param string $id Идентификатор сессии (Session ID)
|
||||||
|
*
|
||||||
|
* @return array Сессия (Session)
|
||||||
|
*/
|
||||||
|
public function initSession(string $id): array
|
||||||
|
{
|
||||||
|
return $this->searchSession($id) ?? (new Query())
|
||||||
|
->in($this->document)
|
||||||
|
->insert($this->buffer, db: $this->database);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Записать сессию (Write session)
|
||||||
|
*
|
||||||
|
* @param string $id Идентификатор сессии (Session ID)
|
||||||
|
* @param string $data Данные сессии (Session data)
|
||||||
|
*
|
||||||
|
* @return bool Статус записи сессии (Session writing status)
|
||||||
|
*/
|
||||||
|
public function writeSession($id, $data): bool
|
||||||
|
// public function writeSession(string $id, string $data): bool
|
||||||
|
{
|
||||||
|
if ($this->getUseStrictMode() && $id === $this->_forceRegenerateId) {
|
||||||
|
//Ignore write when forceRegenerate is active for this id
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// exception must be caught in session write handler
|
||||||
|
// https://secure.php.net/manual/en/function.session-set-save-handler.php#refsect1-function.session-set-save-handler-notes
|
||||||
|
try {
|
||||||
|
// ensure backwards compatability (fixed #9438)
|
||||||
|
if ($this->writeCallback && !$this->buffer) {
|
||||||
|
$this->buffer = $this->composeFields();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure data consistency
|
||||||
|
if (isset($this->buffer['data'])) {
|
||||||
|
$_SESSION = $this->buffer['data'];
|
||||||
|
} else {
|
||||||
|
$this->buffer['data'] = $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->buffer = array_merge($this->buffer, [
|
||||||
|
'hash' => $id,
|
||||||
|
'from' => time(),
|
||||||
|
'to' => time() + $this->getTimeout(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Конвертация или сериализация, не пойму
|
||||||
|
$this->buffer = $this->typecastFields($this->buffer);
|
||||||
|
|
||||||
|
// Инициализация сессии
|
||||||
|
$this->initSession($id);
|
||||||
|
|
||||||
|
// Деинициализация буфера данных сессии
|
||||||
|
$this->buffer = [];
|
||||||
|
} catch (Exception $e) {
|
||||||
|
yii::$app->errorHandler->handleException($e);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Удалить сессию (Delete session)
|
||||||
|
*
|
||||||
|
* @param string $id Идентификатор сессии (Session ID)
|
||||||
|
*
|
||||||
|
* @return bool Статус удаления сессии (Session deleting status)
|
||||||
|
*/
|
||||||
|
public function destroySession($id): bool
|
||||||
|
// public function destroySession(string $id): bool
|
||||||
|
{
|
||||||
|
return (new Query())
|
||||||
|
->in($this->document)
|
||||||
|
->where([
|
||||||
|
'hash' => $id
|
||||||
|
])
|
||||||
|
->remove(db: $this->database);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Удалить неиспользуемые данные (Delete garbage)
|
||||||
|
*
|
||||||
|
* Очищает от сессий чей срок хранения истёк
|
||||||
|
* Clears sessions whose retention period has expired
|
||||||
|
*.
|
||||||
|
* @param int $max Периодичность очистки в секундах (Cleaning frequency in seconds)
|
||||||
|
*
|
||||||
|
* @return bool Статус удаления устаревших сессий (Deletion status of obsolete sessions)
|
||||||
|
*/
|
||||||
|
public function gcSession($max): bool
|
||||||
|
// public function gcSession(int $max): bool
|
||||||
|
{
|
||||||
|
return (new Query())
|
||||||
|
->in($this->document)
|
||||||
|
->where([
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'to' => time()
|
||||||
|
],
|
||||||
|
'operator' => '>='
|
||||||
|
]
|
||||||
|
])
|
||||||
|
->remove(db: $this->database);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Генерация запроса для чтения сеанса (Generating a query to read a session)
|
||||||
|
*
|
||||||
|
* @param string $id Идентификатор сессии (Session ID)
|
||||||
|
*
|
||||||
|
* @return Query Инстанция запроса на чтение (Read query instance)
|
||||||
|
*/
|
||||||
|
protected function getReadQuery(string $id): Query
|
||||||
|
{
|
||||||
|
return (new Query())
|
||||||
|
->in($this->document)
|
||||||
|
->where([
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'to' => time()
|
||||||
|
],
|
||||||
|
'operator' => '<='
|
||||||
|
]
|
||||||
|
])
|
||||||
|
->where([
|
||||||
|
[
|
||||||
|
'to' => null
|
||||||
|
]
|
||||||
|
], 'OR')
|
||||||
|
->where([
|
||||||
|
[
|
||||||
|
'hash' => $id,
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Конвертация для отправки в PDO (Convertation for sending to PDO)
|
||||||
|
*
|
||||||
|
* Default implementation casts field `data` to `\PDO::PARAM_LOB`.
|
||||||
|
* You can override this method in case you need special type casting.
|
||||||
|
*
|
||||||
|
* @param array $data Данные для конвертации (Data for convertation) [name => value]
|
||||||
|
*
|
||||||
|
* @return array Конвертированные данные (Converted data)
|
||||||
|
*/
|
||||||
|
protected function typecastFields(array $data): array
|
||||||
|
{
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use mirzaev\yii2\arangodb\Migration;
|
||||||
|
|
||||||
|
class create_sessions_collection extends Migration
|
||||||
|
{
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param string Название коллекции
|
||||||
|
* @param array Тип коллекции (2 - документ, 3 - ребро)
|
||||||
|
*/
|
||||||
|
$this->createCollection('sessions', ['type' => 2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
$this->dropCollection('sessions');
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,134 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace mirzaev\yii2\arangodb\sessions;
|
||||||
|
|
||||||
|
use yii;
|
||||||
|
|
||||||
|
use yii\web\Session;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Базовый класс для реализации хранилища сессий
|
||||||
|
*
|
||||||
|
* MultiFieldSession is the base class for session storage implementations with multi-field data storage support
|
||||||
|
*
|
||||||
|
* @property-read bool $useCustomStorage Разрешение использовать своё хранилища (Whether to use custom storage)
|
||||||
|
*
|
||||||
|
* @author Arsen Mirzaev Tatyano-Muradovich <red@hood.su>
|
||||||
|
* @author Paul Klimov <klimov.paul@gmail.com>
|
||||||
|
*/
|
||||||
|
abstract class MultiFieldSession extends Session
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var callable a callback that will be called during session data reading.
|
||||||
|
* The signature of the callback should be as follows:
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* function ($fields)
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* where `$fields` is the storage field set for read session and `$session` is this session instance.
|
||||||
|
* If callback returns an array, it will be merged into the session data.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
*
|
||||||
|
* ```php
|
||||||
|
* function ($fields) {
|
||||||
|
* return [
|
||||||
|
* 'expireDate' => Yii::$app->formatter->asDate($fields['expire']),
|
||||||
|
* ];
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
public $readCallback;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var callable a callback that will be called during session data writing.
|
||||||
|
* The signature of the callback should be as follows:
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* function ($session)
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* where `$session` is this session instance, this variable can be used to retrieve session data.
|
||||||
|
* Callback should return the actual fields set, which should be saved into the session storage.
|
||||||
|
*
|
||||||
|
* For example:
|
||||||
|
*
|
||||||
|
* ```php
|
||||||
|
* function ($session) {
|
||||||
|
* return [
|
||||||
|
* 'user_id' => Yii::$app->user->id,
|
||||||
|
* 'ip' => $_SERVER['REMOTE_ADDR'],
|
||||||
|
* 'is_trusted' => $session->get('is_trusted', false),
|
||||||
|
* ];
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
public $writeCallback;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a value indicating whether to use custom session storage.
|
||||||
|
* This method overrides the parent implementation and always returns true.
|
||||||
|
*
|
||||||
|
* @return bool whether to use custom storage.
|
||||||
|
*/
|
||||||
|
public function getUseCustomStorage(): bool
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Composes storage field set for session writing.
|
||||||
|
*
|
||||||
|
* @param string $id Optional session id
|
||||||
|
* @param string $data Optional session data
|
||||||
|
*
|
||||||
|
* @return array storage fields
|
||||||
|
*/
|
||||||
|
protected function composeFields($id = null, $data = null): array
|
||||||
|
{
|
||||||
|
$buffer = $this->writeCallback ? call_user_func($this->writeCallback, $this) : [];
|
||||||
|
|
||||||
|
if ($id !== null) {
|
||||||
|
$buffer['hash'] = $id;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($data !== null) {
|
||||||
|
$buffer['data'] = $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts session data from storage field set.
|
||||||
|
*
|
||||||
|
* @param array $buffer storage buffer.
|
||||||
|
*
|
||||||
|
* @return string session data.
|
||||||
|
*/
|
||||||
|
protected function extractData(array $buffer): string
|
||||||
|
{
|
||||||
|
if ($this->readCallback !== null) {
|
||||||
|
if (!isset($buffer['data'])) {
|
||||||
|
$buffer['data'] = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$extraData = call_user_func($this->readCallback, $buffer);
|
||||||
|
|
||||||
|
if (!empty($extraData)) {
|
||||||
|
session_decode($buffer['data']);
|
||||||
|
|
||||||
|
$_SESSION = array_merge((array) $_SESSION, (array) $extraData);
|
||||||
|
|
||||||
|
return session_encode();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $buffer['data'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return isset($buffer['data']) ? $buffer['data'] : '';
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue