From 6821d68e3cc9a78e020417e16c88ad49d8b3f882 Mon Sep 17 00:00:00 2001 From: Arkanov Andrey Date: Wed, 18 Sep 2024 14:43:40 +0500 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D1=80=D0=B0=D0=B1=D0=BE=D1=82?= =?UTF-8?q?=D0=BA=D0=B0=20=D0=B4=D0=BB=D1=8F=20=D0=BA=D0=BE=D1=80=D1=80?= =?UTF-8?q?=D0=B5=D0=BA=D1=82=D0=BD=D0=BE=D0=B9=20=D1=80=D0=B0=D0=B1=D0=BE?= =?UTF-8?q?=D1=82=D1=8B=20=D1=81=20=D0=9F=D0=93.=20=D0=91=D0=BE=D0=BB?= =?UTF-8?q?=D1=8C=D1=88=D0=B0=D1=8F=20=D1=87=D0=B0=D1=81=D1=82=D1=8C=20?= =?UTF-8?q?=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D0=BE=D0=BD=D0=B0=D0=BB?= =?UTF-8?q?=D0=B0=20=D0=BF=D1=80=D0=B5=D0=BA=D1=80=D0=B0=D1=81=D0=BD=D0=BE?= =?UTF-8?q?=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=D0=B5=D1=82=20=D0=B8?= =?UTF-8?q?=D0=B7=20=D1=80=D0=BE=D0=B4=D0=B8=D1=82=D0=B5=D0=BB=D1=8C=D1=81?= =?UTF-8?q?=D0=BA=D0=BE=D0=B3=D0=BE=20=D0=BA=D0=BB=D0=B0=D1=81=D1=81=D0=B0?= =?UTF-8?q?,=20=D0=BD=D1=83=D0=B6=D0=BD=D0=B0=20=D0=B1=D1=8B=D0=BB=D0=B0?= =?UTF-8?q?=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=20=D1=81=20=D0=B1=D0=BB?= =?UTF-8?q?=D0=BE=D0=BA=D0=B8=D1=80=D0=BE=D0=B2=D0=BA=D0=BE=D0=B9=20=D0=BD?= =?UTF-8?q?=D0=B0=20=D1=83=D1=80=D0=BE=D0=B2=D0=BD=D0=B5=20=D1=81=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=BA.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DbHttpSession.php | 99 +++++++++++++++++++++++++---------------------- 1 file changed, 53 insertions(+), 46 deletions(-) diff --git a/DbHttpSession.php b/DbHttpSession.php index 572ee30..e856ce3 100644 --- a/DbHttpSession.php +++ b/DbHttpSession.php @@ -7,6 +7,12 @@ use PDO; use Intersvyaz\Pdo\Oci8; +/** + * {@inheritDoc} + * Расширение для корректной работы с ораклом, плюс добавление других фич, а именно блокировка строк на уровне базы + * для исключения гонки при обновлениях. Используется в дальнейшем в другом расширении - + * Intersvyaz\Oauth2client\OauthEventHandler + */ class DbHttpSession extends CDbHttpSession { /** @@ -49,10 +55,15 @@ public function setExclusive($status) */ public function regenerateID($deleteOldSession = false) { - $oldID = session_id(); + if (!$this->isOci8Driver()) { + parent::regenerateID($deleteOldSession); + return; + } + + $oldId = session_id(); // if no session is started, there is nothing to regenerate - if (empty($oldID)) { + if (empty($oldId)) { return; } @@ -60,17 +71,17 @@ public function regenerateID($deleteOldSession = false) session_regenerate_id($deleteOldSession); } - $newID = session_id(); + $newId = session_id(); $db = $this->getDbConnection(); - $row = $db->createCommand("SELECT ID,EXPIRE,DATA FROM {$this->sessionTableName} WHERE id=:id") - ->bindValue(':id', $oldID)->queryRow(); + $row = $db->createCommand("SELECT id, expire, data FROM {$this->sessionTableName} WHERE id = :id") + ->bindValue(':id', $oldId)->queryRow(); if ($row !== false) { if ($deleteOldSession) { - $db->createCommand("UPDATE {$this->sessionTableName} SET id=:newID WHERE id=:oldID") - ->bindValue(':newID', $newID) - ->bindValue(':oldID', $oldID) + $db->createCommand("UPDATE {$this->sessionTableName} SET id = :newId WHERE id = :oldId") + ->bindValue(':newId', $newId) + ->bindValue(':oldId', $oldId) ->execute(); } else { if ($this->isOci8Driver()) { @@ -82,9 +93,11 @@ public function regenerateID($deleteOldSession = false) $transaction = $db->beginTransaction(); } - $db->createCommand("INSERT INTO {$this->sessionTableName} (id, expire,data) - VALUES (:id, :expire, empty_blob()) returning data into :data") - ->bindValue(':id', $newID) + $db->createCommand( + "INSERT INTO {$this->sessionTableName} (id, expire,data) + VALUES (:id, :expire, empty_blob()) returning data into :data" + ) + ->bindValue(':id', $newId) ->bindValue(':expire', $row['EXPIRE']) ->bindParam(':data', $fp, PDO::PARAM_LOB) ->execute(); @@ -96,10 +109,10 @@ public function regenerateID($deleteOldSession = false) } } else { // shouldn't reach here normally - $db->createCommand("INSERT INTO {$this->sessionTableName} (id, expire) values (:ID, :EXPIRE)") + $db->createCommand("INSERT INTO {$this->sessionTableName} (id, expire) values (:id, :expire)") ->execute(array( - 'ID' => $newID, - 'EXPIRE' => time() + $this->getTimeout(), + 'id' => $newId, + 'expire' => time() + $this->getTimeout(), )); } } @@ -132,44 +145,36 @@ public function readSession($id) */ public function writeSession($id, $data) { + if (!$this->isOci8Driver()) { + // Для всех не оракловых баз берем функционал из родительского метода + return parent::writeSession($id, $data); + } + // exception must be caught in session write handler // http://us.php.net/manual/en/function.session-set-save-handler.php try { $expire = time() + $this->getTimeout(); $db = $this->getDbConnection(); - $sql = "SELECT id FROM {$this->sessionTableName} WHERE id=:id"; + + // Весь код, который тут был, нормально работает для Оракла, но криво работает для ПГ. + // Поэтому делаем его отдельно, а для остальных баз берем из родительского метода. + $sql = "SELECT id FROM {$this->sessionTableName} WHERE id = :id"; if ($db->createCommand($sql)->bindValue(':id', $id)->queryScalar() === false) { - $sql = "INSERT INTO {$this->sessionTableName} (id, expire,data) + $sql = "INSERT INTO {$this->sessionTableName} (id, expire, data) VALUES (:id, :expire, empty_blob()) returning data into :data"; } else { $data = $this->getDataForSave($id, $data); - $sql = "UPDATE {$this->sessionTableName} SET data=empty_blob(), expire=:expire - WHERE id=:id returning data into :data"; - } - - if ($this->isOci8Driver()) { - $fp = $data; - } else { - $fp = fopen('php://memory', "rwb"); - fwrite($fp, $data); - fseek($fp, 0); - - if (!$this->transaction) { - $this->transaction = $this->getDbConnection()->beginTransaction(); - } + $sql = "UPDATE {$this->sessionTableName} SET data = empty_blob(), expire = :expire + WHERE id = :id RETURNING data into :data"; } $db->createCommand($sql) ->bindValue(':id', $id) ->bindValue(':expire', $expire) - ->bindParam(':data', $fp, PDO::PARAM_LOB) + ->bindParam(':data', $data, PDO::PARAM_LOB) ->execute(); - if (!$this->isOci8Driver()) { - fclose($fp); - } - if ($this->transaction) { $this->transaction->commit(); $this->transaction = null; @@ -204,18 +209,20 @@ protected function getSessionData($id, $forUpdate = false) } $data = $this->getDbConnection() - ->createCommand("SELECT data FROM {$this->sessionTableName} WHERE expire>:expire AND id=:id" . + ->createCommand( + "SELECT data FROM {$this->sessionTableName} + WHERE expire > :expire AND id = :id" . ($forUpdate ? ' FOR UPDATE' : '') ) ->bindValue(':id', $id) ->bindValue(':expire', time()) - ->queryRow(); + ->queryScalar(); if ($this->isOci8Driver()) { - return (string)$data['DATA']; + return (string)$data; } - return $data === false ? '' : stream_get_contents($data['DATA']); + return $data === false ? '' : $data; } /** @@ -255,16 +262,16 @@ protected function getDataForSave($id, $data) */ protected function createSessionTable($db, $tableName) { - $sql = 'CREATE - TABLE ' . $this->sessionTableName . ' + // Реально таблица никогда тут не будет создаваться, это исключительно для понимания её структуры, можно сказать + // скрипт для создания таблицы. Тут структура для оракловой таблицы. Хотя в родительском классе тоже описана. + $sql = 'CREATE TABLE ' . $this->sessionTableName . ' ( - "ID" VARCHAR2(32 BYTE), - "EXPIRE" NUMBER(10,0) NOT NULL, - "DATA" BLOB, + id VARCHAR2(32 BYTE), + expire NUMBER(10,0) NOT NULL, + data BLOB, CONSTRAINT YiiSession_PK PRIMARY KEY(ID) );'; - $db->createCommand($sql) - ->execute(); + $db->createCommand($sql)->execute(); } /**