diff --git a/docs/book/v1/manual.md b/docs/book/v1/manual.md index 43bfdbb..7e1030a 100644 --- a/docs/book/v1/manual.md +++ b/docs/book/v1/manual.md @@ -36,7 +36,10 @@ The following details the constructor of the `Mezzio\Session\Cache\CacheSessionP * @param string $cookieSameSite The same-site rule to apply to the persisted * cookie. Options include "Lax", "Strict", and "None". * Available since 1.4.0 - * + * @param bool $autoRegenerate Whether or not the session ID should be + * regenerated on session data changes + * Available since 1.13.0 + * * @todo reorder these arguments so they make more sense and are in an * order of importance */ @@ -51,7 +54,8 @@ public function __construct( string $cookieDomain = null, bool $cookieSecure = false, bool $cookieHttpOnly = false, - string $cookieSameSite = 'Lax' + string $cookieSameSite = 'Lax', + bool $autoRegenerate = true, ) { ``` diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 3c353c0..4d68d07 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -22,13 +22,10 @@ $cacheService get($cacheService)]]> $cookieDomain - $cookieHttpOnly $cookieName $cookiePath $cookieSameSite - $cookieSecure $lastModified - $persistent @@ -43,6 +40,7 @@ + $cacheExpire @@ -58,6 +56,7 @@ $cookieSecure $lastModified $persistent + $autoRegenerate diff --git a/src/CacheSessionPersistence.php b/src/CacheSessionPersistence.php index 66f81ad..13f3c27 100644 --- a/src/CacheSessionPersistence.php +++ b/src/CacheSessionPersistence.php @@ -34,6 +34,7 @@ class CacheSessionPersistence implements SessionPersistenceInterface private CacheItemPoolInterface $cache; private bool $persistent; + private bool $autoRegenerate; /** * Prepare session cache and default HTTP caching headers. @@ -67,6 +68,8 @@ class CacheSessionPersistence implements SessionPersistenceInterface * be accessed by client-side apis. * @param string $cookieSameSite The same-site rule to apply to the persisted * cookie. Options include "Lax", "Strict", and "None". + * @param bool $autoRegenerate Whether or not the session ID should be + * regenerated on session data changes * @todo reorder the constructor arguments */ public function __construct( @@ -80,7 +83,8 @@ public function __construct( ?string $cookieDomain = null, bool $cookieSecure = false, bool $cookieHttpOnly = false, - string $cookieSameSite = 'Lax' + string $cookieSameSite = 'Lax', + bool $autoRegenerate = true ) { $this->cache = $cache; @@ -112,6 +116,8 @@ public function __construct( : $this->getLastModified(); $this->persistent = $persistent; + + $this->autoRegenerate = $autoRegenerate; } public function initializeSessionFromRequest(ServerRequestInterface $request): SessionInterface @@ -139,8 +145,8 @@ public function persistSession(SessionInterface $session, ResponseInterface $res // Regenerate the session if: // - we have no session identifier // - the session is marked as regenerated - // - the session has changed (data is different) - if ('' === $id || $session->isRegenerated() || $session->hasChanged()) { + // - the session has changed (data is different) and autoRegenerate is turned on (default) in the configuration + if ('' === $id || $session->isRegenerated() || ($this->autoRegenerate && $session->hasChanged())) { $id = $this->regenerateSession($id); } diff --git a/src/CacheSessionPersistenceFactory.php b/src/CacheSessionPersistenceFactory.php index 65a9e4c..b2e4654 100644 --- a/src/CacheSessionPersistenceFactory.php +++ b/src/CacheSessionPersistenceFactory.php @@ -34,6 +34,7 @@ public function __invoke(ContainerInterface $container) $cacheExpire = $config['cache_expire'] ?? 10800; $lastModified = $config['last_modified'] ?? null; $persistent = $config['persistent'] ?? false; + $autoRegenerate = $config['auto_regenerate'] ?? true; return new CacheSessionPersistence( $container->get($cacheService), @@ -42,11 +43,12 @@ public function __invoke(ContainerInterface $container) $cacheLimiter, $cacheExpire, $lastModified, - $persistent, + (bool) $persistent, $cookieDomain, - $cookieSecure, - $cookieHttpOnly, - $cookieSameSite + (bool) $cookieSecure, + (bool) $cookieHttpOnly, + $cookieSameSite, + (bool) $autoRegenerate ); } } diff --git a/test/CacheSessionPersistenceFactoryTest.php b/test/CacheSessionPersistenceFactoryTest.php index b288b65..96c675c 100644 --- a/test/CacheSessionPersistenceFactoryTest.php +++ b/test/CacheSessionPersistenceFactoryTest.php @@ -74,6 +74,7 @@ public function testFactoryUsesSaneDefaultsForConstructorArguments(): void $this->assertAttributeSame(10800, 'cacheExpire', $persistence); $this->assertAttributeNotEmpty('lastModified', $persistence); $this->assertAttributeSame(false, 'persistent', $persistence); + $this->assertAttributeSame(true, 'autoRegenerate', $persistence); } public function testFactoryAllowsConfiguringAllConstructorArguments(): void @@ -95,6 +96,7 @@ public function testFactoryAllowsConfiguringAllConstructorArguments(): void 'cache_expire' => 300, 'last_modified' => $lastModified, 'persistent' => true, + 'auto_regenerate' => false, ], ]); @@ -115,6 +117,7 @@ public function testFactoryAllowsConfiguringAllConstructorArguments(): void $persistence ); $this->assertAttributeSame(true, 'persistent', $persistence); + $this->assertAttributeSame(false, 'autoRegenerate', $persistence); } public function testFactoryAllowsConfiguringCacheAdapterServiceName(): void diff --git a/test/CacheSessionPersistenceIntegrationTest.php b/test/CacheSessionPersistenceIntegrationTest.php index da85037..e56b491 100644 --- a/test/CacheSessionPersistenceIntegrationTest.php +++ b/test/CacheSessionPersistenceIntegrationTest.php @@ -111,4 +111,40 @@ public function testThatAChangedSessionWillCauseRegenerationAndASetCookieHeader( $value = $item->get(); self::assertNull($value, 'The previous session data should have been deleted'); } + + public function testThatAChangedSessionWillNotCauseRegenerationWhenAutoRegenerateIsFalse(): void + { + $this->storage = new CacheSessionPersistence( + $this->cache, + cookieName: 'Session', + autoRegenerate: false, + ); + + $item = $this->cache->getItem('foo'); + $item->set(['foo' => 'bar']); + $this->cache->save($item); + + $request = (new ServerRequest())->withHeader('Cookie', 'Session=foo;'); + $middleware = new SessionMiddleware($this->storage); + $handler = new TestHandler(); + $handler->setSessionVariable('something', 'groovy'); + $response = $middleware->process($request, $handler); + + $setCookie = SetCookies::fromResponse($response); + $cookie = $setCookie->get('Session'); + self::assertNotNull($cookie); + + $id = $cookie->getValue(); + self::assertNotNull($id); + + self::assertSame('foo', $id); + self::assertNotSame($handler->response, $response); + + $item = $this->cache->getItem('foo'); + $value = $item->get(); + self::assertSame([ + 'foo' => 'bar', + 'something' => 'groovy', + ], $value, 'The session data should have been updated'); + } }