From bbc9fbee527aa42c4238bbac20e98ad7ca1e13f2 Mon Sep 17 00:00:00 2001 From: Viktor Khokhryakov Date: Thu, 5 Dec 2024 13:50:13 +0400 Subject: [PATCH 1/2] Fix #20140: PHP Deprecated: Calling session_set_save_handler() in PHP 8.4 --- framework/CHANGELOG.md | 1 + framework/UPGRADE.md | 7 ++ framework/web/CacheSession.php | 2 +- framework/web/DbSession.php | 8 +- framework/web/Session.php | 37 +++------ framework/web/SessionHandler.php | 79 +++++++++++++++++++ .../web/session/AbstractDbSessionTest.php | 3 +- 7 files changed, 106 insertions(+), 31 deletions(-) create mode 100644 framework/web/SessionHandler.php diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index 8263b086376..b15f0b982a4 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -17,6 +17,7 @@ Yii Framework 2 Change Log - New #20279: Add to the `\yii\web\Request` CSRF validation by custom HTTP header (olegbaturin) - Enh #20279: Add to the `\yii\web\Request` `csrfHeader` property to configure a custom HTTP header for CSRF validation (olegbaturin) - Enh #20279: Add to the `\yii\web\Request` `csrfTokenSafeMethods` property to configure a custom safe HTTP methods list (olegbaturin) +- Bug #20140: Fix compatibility with PHP 8.4: calling session_set_save_handler() (Izumi-kun) 2.0.51 July 18, 2024 -------------------- diff --git a/framework/UPGRADE.md b/framework/UPGRADE.md index f75f025301c..72aa39821d6 100644 --- a/framework/UPGRADE.md +++ b/framework/UPGRADE.md @@ -51,6 +51,13 @@ if you want to upgrade from version A to version C and there is version B between A and C, you need to follow the instructions for both A and B. +Upgrade from Yii 2.0.51 +----------------------- + +* The function signature for `yii\web\Session::readSession()` and `yii\web\Session::gcSession()` have been changed. + They now have the same return types as `\SessionHandlerInterface::read()` and `\SessionHandlerInterface::gc()` respectively. + In case those methods have overwritten you will need to update your child classes accordingly. + Upgrade from Yii 2.0.50 ----------------------- diff --git a/framework/web/CacheSession.php b/framework/web/CacheSession.php index 5763a854096..f3d0d87a1af 100644 --- a/framework/web/CacheSession.php +++ b/framework/web/CacheSession.php @@ -92,7 +92,7 @@ public function openSession($savePath, $sessionName) * Session read handler. * @internal Do not call this method directly. * @param string $id session ID - * @return string the session data + * @return string|false the session data, or false on failure */ public function readSession($id) { diff --git a/framework/web/DbSession.php b/framework/web/DbSession.php index 7c7743b509d..1ef79cb7c6a 100644 --- a/framework/web/DbSession.php +++ b/framework/web/DbSession.php @@ -171,7 +171,7 @@ public function close() * Session read handler. * @internal Do not call this method directly. * @param string $id session ID - * @return string the session data + * @return string|false the session data, or false on failure */ public function readSession($id) { @@ -247,15 +247,13 @@ public function destroySession($id) * Session GC (garbage collection) handler. * @internal Do not call this method directly. * @param int $maxLifetime the number of seconds after which data will be seen as 'garbage' and cleaned up. - * @return bool whether session is GCed successfully + * @return int|false the number of deleted sessions on success, or false on failure */ public function gcSession($maxLifetime) { - $this->db->createCommand() + return $this->db->createCommand() ->delete($this->sessionTable, '[[expire]]<:expire', [':expire' => time()]) ->execute(); - - return true; } /** diff --git a/framework/web/Session.php b/framework/web/Session.php index e9c3ebd5c48..98e80495500 100644 --- a/framework/web/Session.php +++ b/framework/web/Session.php @@ -175,34 +175,23 @@ protected function registerSessionHandler() static::$_originalSessionModule = $sessionModuleName; } + if ($this->handler === null && $this->getUseCustomStorage()) { + $this->handler = Yii::createObject( + [ + '__class' => SessionHandler::class, + '__construct()' => [$this], + ] + ); + } + if ($this->handler !== null) { - if (!is_object($this->handler)) { + if (is_array($this->handler)) { $this->handler = Yii::createObject($this->handler); } if (!$this->handler instanceof \SessionHandlerInterface) { throw new InvalidConfigException('"' . get_class($this) . '::handler" must implement the SessionHandlerInterface.'); } YII_DEBUG ? session_set_save_handler($this->handler, false) : @session_set_save_handler($this->handler, false); - } elseif ($this->getUseCustomStorage()) { - if (YII_DEBUG) { - session_set_save_handler( - [$this, 'openSession'], - [$this, 'closeSession'], - [$this, 'readSession'], - [$this, 'writeSession'], - [$this, 'destroySession'], - [$this, 'gcSession'] - ); - } else { - @session_set_save_handler( - [$this, 'openSession'], - [$this, 'closeSession'], - [$this, 'readSession'], - [$this, 'writeSession'], - [$this, 'destroySession'], - [$this, 'gcSession'] - ); - } } elseif ( $sessionModuleName !== static::$_originalSessionModule && static::$_originalSessionModule !== null @@ -610,7 +599,7 @@ public function closeSession() * This method should be overridden if [[useCustomStorage]] returns true. * @internal Do not call this method directly. * @param string $id session ID - * @return string the session data + * @return string|false the session data, or false on failure */ public function readSession($id) { @@ -647,11 +636,11 @@ public function destroySession($id) * This method should be overridden if [[useCustomStorage]] returns true. * @internal Do not call this method directly. * @param int $maxLifetime the number of seconds after which data will be seen as 'garbage' and cleaned up. - * @return bool whether session is GCed successfully + * @return int|false the number of deleted sessions on success, or false on failure */ public function gcSession($maxLifetime) { - return true; + return 0; } /** diff --git a/framework/web/SessionHandler.php b/framework/web/SessionHandler.php new file mode 100644 index 00000000000..7382f4545d6 --- /dev/null +++ b/framework/web/SessionHandler.php @@ -0,0 +1,79 @@ + + * @since 2.0.52 + */ +class SessionHandler implements SessionHandlerInterface +{ + /** + * @var Session + */ + private $_session; + + public function __construct(Session $session) + { + $this->_session = $session; + } + + /** + * @inheritDoc + */ + public function close(): bool + { + return $this->_session->closeSession(); + } + + /** + * @inheritDoc + */ + public function destroy($id): bool + { + return $this->_session->destroySession($id); + } + + /** + * @inheritDoc + */ + #[\ReturnTypeWillChange] + public function gc($max_lifetime) + { + return $this->_session->gcSession($max_lifetime); + } + + /** + * @inheritDoc + */ + public function open($path, $name): bool + { + return $this->_session->openSession($path, $name); + } + + /** + * @inheritDoc + */ + #[\ReturnTypeWillChange] + public function read($id) + { + return $this->_session->readSession($id); + } + + /** + * @inheritDoc + */ + public function write($id, $data): bool + { + return $this->_session->writeSession($id, $data); + } +} diff --git a/tests/framework/web/session/AbstractDbSessionTest.php b/tests/framework/web/session/AbstractDbSessionTest.php index f8dbb32f550..2fe0fa2cdff 100644 --- a/tests/framework/web/session/AbstractDbSessionTest.php +++ b/tests/framework/web/session/AbstractDbSessionTest.php @@ -127,8 +127,9 @@ public function testGarbageCollection() $session->db->createCommand() ->update('session', ['expire' => time() - 100], 'id = :id', ['id' => 'expire']) ->execute(); - $session->gcSession(1); + $deleted = $session->gcSession(1); + $this->assertEquals(1, $deleted); $this->assertEquals('', $session->readSession('expire')); $this->assertEquals('new data', $session->readSession('new')); } From 6349d57d50b8999afdf58fbbd04ff1791ae872e0 Mon Sep 17 00:00:00 2001 From: Viktor Khokhryakov Date: Fri, 6 Dec 2024 13:37:33 +0400 Subject: [PATCH 2/2] Update CHANGELOG.md Co-authored-by: Alexander Makarov --- framework/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index b15f0b982a4..f0a12aeb608 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -17,7 +17,7 @@ Yii Framework 2 Change Log - New #20279: Add to the `\yii\web\Request` CSRF validation by custom HTTP header (olegbaturin) - Enh #20279: Add to the `\yii\web\Request` `csrfHeader` property to configure a custom HTTP header for CSRF validation (olegbaturin) - Enh #20279: Add to the `\yii\web\Request` `csrfTokenSafeMethods` property to configure a custom safe HTTP methods list (olegbaturin) -- Bug #20140: Fix compatibility with PHP 8.4: calling session_set_save_handler() (Izumi-kun) +- Bug #20140: Fix compatibility with PHP 8.4: calling `session_set_save_handler()` (Izumi-kun) 2.0.51 July 18, 2024 --------------------