From 732e80d4f6edd68c9837bc1c0cb26ba1b6eeab5d Mon Sep 17 00:00:00 2001 From: michalsn Date: Tue, 10 Feb 2026 18:31:57 +0100 Subject: [PATCH 1/2] fix: correct inverted savePath check in MemcachedHandler constructor --- system/Session/Handlers/MemcachedHandler.php | 2 +- .../Session/Handlers/MemcachedHandlerTest.php | 131 ++++++++++++++++++ user_guide_src/source/changelogs/v4.7.1.rst | 1 + 3 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 tests/system/Session/Handlers/MemcachedHandlerTest.php diff --git a/system/Session/Handlers/MemcachedHandler.php b/system/Session/Handlers/MemcachedHandler.php index 478aa88c7a11..7b7775916019 100644 --- a/system/Session/Handlers/MemcachedHandler.php +++ b/system/Session/Handlers/MemcachedHandler.php @@ -63,7 +63,7 @@ public function __construct(SessionConfig $config, string $ipAddress) $this->sessionExpiration = $config->expiration; - if ($this->savePath !== '') { + if ($this->savePath === '') { throw SessionException::forEmptySavepath(); } diff --git a/tests/system/Session/Handlers/MemcachedHandlerTest.php b/tests/system/Session/Handlers/MemcachedHandlerTest.php new file mode 100644 index 000000000000..8a24a7377722 --- /dev/null +++ b/tests/system/Session/Handlers/MemcachedHandlerTest.php @@ -0,0 +1,131 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Session\Handlers; + +use CodeIgniter\Session\Exceptions\SessionException; +use CodeIgniter\Test\CIUnitTestCase; +use CodeIgniter\Test\TestLogger; +use Config\Logger as LoggerConfig; +use Config\Session as SessionConfig; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\RequiresPhpExtension; + +/** + * @internal + */ +#[Group('DatabaseLive')] +#[RequiresPhpExtension('memcached')] +final class MemcachedHandlerTest extends CIUnitTestCase +{ + private string $sessionDriver = MemcachedHandler::class; + private string $sessionName = 'ci_session'; + private string $sessionSavePath = '127.0.0.1:11211'; + private string $userIpAddress = '127.0.0.1'; + + /** + * @param array $options Replace values for `Config\Session`. + */ + protected function getInstance($options = []): MemcachedHandler + { + $defaults = [ + 'driver' => $this->sessionDriver, + 'cookieName' => $this->sessionName, + 'expiration' => 7200, + 'savePath' => $this->sessionSavePath, + 'matchIP' => false, + 'timeToUpdate' => 300, + 'regenerateDestroy' => false, + ]; + $sessionConfig = new SessionConfig(); + $config = array_merge($defaults, $options); + + foreach ($config as $key => $value) { + $sessionConfig->{$key} = $value; + } + + $handler = new MemcachedHandler($sessionConfig, $this->userIpAddress); + $handler->setLogger(new TestLogger(new LoggerConfig())); + + return $handler; + } + + protected function tearDown(): void + { + parent::tearDown(); + + MemcachedHandler::resetPersistentConnections(); + } + + public function testConstructorThrowsWithEmptySavePath(): void + { + $this->expectException(SessionException::class); + + $this->getInstance(['savePath' => '']); + } + + public function testConstructorDoesNotThrowWithValidSavePath(): void + { + $handler = $this->getInstance(['savePath' => '127.0.0.1:11211']); + + $this->assertInstanceOf(MemcachedHandler::class, $handler); + } + + public function testOpen(): void + { + $handler = $this->getInstance(); + $this->assertTrue($handler->open($this->sessionSavePath, $this->sessionName)); + } + + public function testWriteAndReadBack(): void + { + $handler = $this->getInstance(); + $handler->open($this->sessionSavePath, $this->sessionName); + + $sessionId = '555556b43phsnnf8if6bo33b635e4447'; + + // Initial read to acquire lock and set session ID + $this->assertSame('', $handler->read($sessionId)); + + $data = <<<'DATA' + __ci_last_regenerate|i:1664607454;_ci_previous_url|s:32:"http://localhost:8080/index.php/";key|s:5:"value"; + DATA; + $this->assertTrue($handler->write($sessionId, $data)); + + $handler->close(); + + // Read back in a new handler to verify persistence + $handler2 = $this->getInstance(); + $handler2->open($this->sessionSavePath, $this->sessionName); + + $this->assertSame($data, $handler2->read($sessionId)); + + $handler2->close(); + } + + public function testReadEmptySession(): void + { + $handler = $this->getInstance(); + $handler->open($this->sessionSavePath, $this->sessionName); + + $this->assertSame('', $handler->read('123456b43phsnnf8if6bo33b635e4321')); + + $handler->close(); + } + + public function testGC(): void + { + $handler = $this->getInstance(); + $this->assertSame(1, $handler->gc(3600)); + } +} diff --git a/user_guide_src/source/changelogs/v4.7.1.rst b/user_guide_src/source/changelogs/v4.7.1.rst index d1f4ba8647a6..cef1ca7c48dd 100644 --- a/user_guide_src/source/changelogs/v4.7.1.rst +++ b/user_guide_src/source/changelogs/v4.7.1.rst @@ -33,6 +33,7 @@ Bugs Fixed ********** - **ContentSecurityPolicy:** Fixed a bug where ``generateNonces()`` produces corrupted JSON responses by replacing CSP nonce placeholders with unescaped double quotes. The method now automatically JSON-escapes nonce attributes when the response Content-Type is JSON. +- **Session:** Fixed a bug in ``MemcachedHandler`` where the constructor threw an exception when ``savePath`` was not empty instead of when it was empty. See the repo's `CHANGELOG.md `_ From 6c72cae6fd47c7822442cf62ff2c3196ab45966c Mon Sep 17 00:00:00 2001 From: michalsn Date: Tue, 10 Feb 2026 19:05:40 +0100 Subject: [PATCH 2/2] update changelog --- user_guide_src/source/changelogs/v4.7.1.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/changelogs/v4.7.1.rst b/user_guide_src/source/changelogs/v4.7.1.rst index cef1ca7c48dd..8a94a731a413 100644 --- a/user_guide_src/source/changelogs/v4.7.1.rst +++ b/user_guide_src/source/changelogs/v4.7.1.rst @@ -33,7 +33,7 @@ Bugs Fixed ********** - **ContentSecurityPolicy:** Fixed a bug where ``generateNonces()`` produces corrupted JSON responses by replacing CSP nonce placeholders with unescaped double quotes. The method now automatically JSON-escapes nonce attributes when the response Content-Type is JSON. -- **Session:** Fixed a bug in ``MemcachedHandler`` where the constructor threw an exception when ``savePath`` was not empty instead of when it was empty. +- **Session:** Fixed a bug in ``MemcachedHandler`` where the constructor incorrectly threw an exception when ``savePath`` was not empty. See the repo's `CHANGELOG.md `_