Skip to content

Commit 65e3369

Browse files
authored
Fix #20140: Fix compatibility with PHP 8.4: calling session_set_save_handler()
1 parent 5df412d commit 65e3369

File tree

7 files changed

+106
-31
lines changed

7 files changed

+106
-31
lines changed

framework/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ Yii Framework 2 Change Log
1818
- New #20279: Add to the `\yii\web\Request` CSRF validation by custom HTTP header (olegbaturin)
1919
- Enh #20279: Add to the `\yii\web\Request` `csrfHeader` property to configure a custom HTTP header for CSRF validation (olegbaturin)
2020
- Enh #20279: Add to the `\yii\web\Request` `csrfTokenSafeMethods` property to configure a custom safe HTTP methods list (olegbaturin)
21+
- Bug #20140: Fix compatibility with PHP 8.4: calling `session_set_save_handler()` (Izumi-kun)
2122

2223
2.0.51 July 18, 2024
2324
--------------------

framework/UPGRADE.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,13 @@ if you want to upgrade from version A to version C and there is
5151
version B between A and C, you need to follow the instructions
5252
for both A and B.
5353

54+
Upgrade from Yii 2.0.51
55+
-----------------------
56+
57+
* The function signature for `yii\web\Session::readSession()` and `yii\web\Session::gcSession()` have been changed.
58+
They now have the same return types as `\SessionHandlerInterface::read()` and `\SessionHandlerInterface::gc()` respectively.
59+
In case those methods have overwritten you will need to update your child classes accordingly.
60+
5461
Upgrade from Yii 2.0.50
5562
-----------------------
5663

framework/web/CacheSession.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ public function openSession($savePath, $sessionName)
9292
* Session read handler.
9393
* @internal Do not call this method directly.
9494
* @param string $id session ID
95-
* @return string the session data
95+
* @return string|false the session data, or false on failure
9696
*/
9797
public function readSession($id)
9898
{

framework/web/DbSession.php

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ public function close()
171171
* Session read handler.
172172
* @internal Do not call this method directly.
173173
* @param string $id session ID
174-
* @return string the session data
174+
* @return string|false the session data, or false on failure
175175
*/
176176
public function readSession($id)
177177
{
@@ -247,15 +247,13 @@ public function destroySession($id)
247247
* Session GC (garbage collection) handler.
248248
* @internal Do not call this method directly.
249249
* @param int $maxLifetime the number of seconds after which data will be seen as 'garbage' and cleaned up.
250-
* @return bool whether session is GCed successfully
250+
* @return int|false the number of deleted sessions on success, or false on failure
251251
*/
252252
public function gcSession($maxLifetime)
253253
{
254-
$this->db->createCommand()
254+
return $this->db->createCommand()
255255
->delete($this->sessionTable, '[[expire]]<:expire', [':expire' => time()])
256256
->execute();
257-
258-
return true;
259257
}
260258

261259
/**

framework/web/Session.php

Lines changed: 13 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -175,34 +175,23 @@ protected function registerSessionHandler()
175175
static::$_originalSessionModule = $sessionModuleName;
176176
}
177177

178+
if ($this->handler === null && $this->getUseCustomStorage()) {
179+
$this->handler = Yii::createObject(
180+
[
181+
'__class' => SessionHandler::class,
182+
'__construct()' => [$this],
183+
]
184+
);
185+
}
186+
178187
if ($this->handler !== null) {
179-
if (!is_object($this->handler)) {
188+
if (is_array($this->handler)) {
180189
$this->handler = Yii::createObject($this->handler);
181190
}
182191
if (!$this->handler instanceof \SessionHandlerInterface) {
183192
throw new InvalidConfigException('"' . get_class($this) . '::handler" must implement the SessionHandlerInterface.');
184193
}
185194
YII_DEBUG ? session_set_save_handler($this->handler, false) : @session_set_save_handler($this->handler, false);
186-
} elseif ($this->getUseCustomStorage()) {
187-
if (YII_DEBUG) {
188-
session_set_save_handler(
189-
[$this, 'openSession'],
190-
[$this, 'closeSession'],
191-
[$this, 'readSession'],
192-
[$this, 'writeSession'],
193-
[$this, 'destroySession'],
194-
[$this, 'gcSession']
195-
);
196-
} else {
197-
@session_set_save_handler(
198-
[$this, 'openSession'],
199-
[$this, 'closeSession'],
200-
[$this, 'readSession'],
201-
[$this, 'writeSession'],
202-
[$this, 'destroySession'],
203-
[$this, 'gcSession']
204-
);
205-
}
206195
} elseif (
207196
$sessionModuleName !== static::$_originalSessionModule
208197
&& static::$_originalSessionModule !== null
@@ -610,7 +599,7 @@ public function closeSession()
610599
* This method should be overridden if [[useCustomStorage]] returns true.
611600
* @internal Do not call this method directly.
612601
* @param string $id session ID
613-
* @return string the session data
602+
* @return string|false the session data, or false on failure
614603
*/
615604
public function readSession($id)
616605
{
@@ -647,11 +636,11 @@ public function destroySession($id)
647636
* This method should be overridden if [[useCustomStorage]] returns true.
648637
* @internal Do not call this method directly.
649638
* @param int $maxLifetime the number of seconds after which data will be seen as 'garbage' and cleaned up.
650-
* @return bool whether session is GCed successfully
639+
* @return int|false the number of deleted sessions on success, or false on failure
651640
*/
652641
public function gcSession($maxLifetime)
653642
{
654-
return true;
643+
return 0;
655644
}
656645

657646
/**

framework/web/SessionHandler.php

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<?php
2+
/**
3+
* @link https://www.yiiframework.com/
4+
* @copyright Copyright (c) 2008 Yii Software LLC
5+
* @license https://www.yiiframework.com/license/
6+
*/
7+
8+
namespace yii\web;
9+
10+
use SessionHandlerInterface;
11+
12+
/**
13+
* SessionHandler implements an [[\SessionHandlerInterface]] for handling [[Session]] with custom session storage.
14+
*
15+
* @author Viktor Khokhryakov <[email protected]>
16+
* @since 2.0.52
17+
*/
18+
class SessionHandler implements SessionHandlerInterface
19+
{
20+
/**
21+
* @var Session
22+
*/
23+
private $_session;
24+
25+
public function __construct(Session $session)
26+
{
27+
$this->_session = $session;
28+
}
29+
30+
/**
31+
* @inheritDoc
32+
*/
33+
public function close(): bool
34+
{
35+
return $this->_session->closeSession();
36+
}
37+
38+
/**
39+
* @inheritDoc
40+
*/
41+
public function destroy($id): bool
42+
{
43+
return $this->_session->destroySession($id);
44+
}
45+
46+
/**
47+
* @inheritDoc
48+
*/
49+
#[\ReturnTypeWillChange]
50+
public function gc($max_lifetime)
51+
{
52+
return $this->_session->gcSession($max_lifetime);
53+
}
54+
55+
/**
56+
* @inheritDoc
57+
*/
58+
public function open($path, $name): bool
59+
{
60+
return $this->_session->openSession($path, $name);
61+
}
62+
63+
/**
64+
* @inheritDoc
65+
*/
66+
#[\ReturnTypeWillChange]
67+
public function read($id)
68+
{
69+
return $this->_session->readSession($id);
70+
}
71+
72+
/**
73+
* @inheritDoc
74+
*/
75+
public function write($id, $data): bool
76+
{
77+
return $this->_session->writeSession($id, $data);
78+
}
79+
}

tests/framework/web/session/AbstractDbSessionTest.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,9 @@ public function testGarbageCollection()
127127
$session->db->createCommand()
128128
->update('session', ['expire' => time() - 100], 'id = :id', ['id' => 'expire'])
129129
->execute();
130-
$session->gcSession(1);
130+
$deleted = $session->gcSession(1);
131131

132+
$this->assertEquals(1, $deleted);
132133
$this->assertEquals('', $session->readSession('expire'));
133134
$this->assertEquals('new data', $session->readSession('new'));
134135
}

0 commit comments

Comments
 (0)