From 0ebc1f733bfe6700240c57994abcf6b397963443 Mon Sep 17 00:00:00 2001 From: Tobias Nyholm Date: Wed, 28 Jun 2017 14:12:41 +0200 Subject: [PATCH 1/2] [HttpFoundation] Adding PSR6 session handler --- .../Component/HttpFoundation/CHANGELOG.md | 1 + .../Storage/Handler/Psr6SessionHandler.php | 121 ++++++++++++ .../Handler/Psr6SessionHandlerTest.php | 175 ++++++++++++++++++ 3 files changed, 297 insertions(+) create mode 100644 src/Symfony/Component/HttpFoundation/Session/Storage/Handler/Psr6SessionHandler.php create mode 100644 src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/Psr6SessionHandlerTest.php diff --git a/src/Symfony/Component/HttpFoundation/CHANGELOG.md b/src/Symfony/Component/HttpFoundation/CHANGELOG.md index 659345c20cfe6..02ad8046b5dff 100644 --- a/src/Symfony/Component/HttpFoundation/CHANGELOG.md +++ b/src/Symfony/Component/HttpFoundation/CHANGELOG.md @@ -13,6 +13,7 @@ CHANGELOG * added `Request::preferSafeContent()` and `Response::setContentSafe()` to handle "safe" HTTP preference according to [RFC 8674](https://tools.ietf.org/html/rfc8674) * made the Mime component an optional dependency + * added `Psr6SessionHandler` 5.0.0 ----- diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/Psr6SessionHandler.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/Psr6SessionHandler.php new file mode 100644 index 0000000000000..992da46099af1 --- /dev/null +++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/Psr6SessionHandler.php @@ -0,0 +1,121 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Storage\Handler; + +use Psr\Cache\CacheItemPoolInterface; + +/** + * Session handler that supports a PSR6 cache implementation. + * + * @author Tobias Nyholm + */ +class Psr6SessionHandler implements \SessionHandlerInterface +{ + /** + * @var CacheItemPoolInterface + */ + private $cache; + + /** + * @var int Time to live in seconds + */ + private $ttl; + + /** + * @var string Key prefix for shared environments. + */ + private $prefix; + + /** + * List of available options: + * * prefix: The prefix to use for the cache keys in order to avoid collision + * * ttl: The time to live in seconds. + * + * @param CacheItemPoolInterface $cache A Cache instance + * @param array $options An associative array of cache options + */ + public function __construct(CacheItemPoolInterface $cache, array $options = array()) + { + $this->cache = $cache; + + $this->ttl = isset($options['ttl']) ? (int) $options['ttl'] : 86400; + $this->prefix = isset($options['prefix']) ? $options['prefix'] : 'sfPsr6sess_'; + } + + /** + * {@inheritdoc} + */ + public function open($savePath, $sessionName) + { + return true; + } + + /** + * {@inheritdoc} + */ + public function close() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function read($sessionId) + { + $item = $this->getCacheItem($sessionId); + if ($item->isHit()) { + return $item->get(); + } + + return ''; + } + + /** + * {@inheritdoc} + */ + public function write($sessionId, $data) + { + $item = $this->getCacheItem($sessionId); + $item->set($data) + ->expiresAfter($this->ttl); + + return $this->cache->save($item); + } + + /** + * {@inheritdoc} + */ + public function destroy($sessionId) + { + return $this->cache->deleteItem($this->prefix.$sessionId); + } + + /** + * {@inheritdoc} + */ + public function gc($lifetime) + { + // not required here because cache will auto expire the records anyhow. + return true; + } + + /** + * @param string $sessionId + * + * @return \Psr\Cache\CacheItemInterface + */ + private function getCacheItem($sessionId) + { + return $this->cache->getItem($this->prefix.$sessionId); + } +} diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/Psr6SessionHandlerTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/Psr6SessionHandlerTest.php new file mode 100644 index 0000000000000..918d82c465b6f --- /dev/null +++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/Psr6SessionHandlerTest.php @@ -0,0 +1,175 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Handler; + +use PHPUnit\Framework\TestCase; +use Psr\Cache\CacheItemInterface; +use Psr\Cache\CacheItemPoolInterface; +use Symfony\Component\HttpFoundation\Session\Storage\Handler\Psr6SessionHandler; + +/** + * @author Aaron Scherer + */ +class Psr6SessionHandlerTest extends TestCase +{ + const TTL = 100; + const PREFIX = 'pre'; + + /** + * @var Psr6SessionHandler + */ + private $handler; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|CacheItemPoolInterface + */ + private $psr6; + + protected function setUp() + { + parent::setUp(); + + $this->psr6 = $this->getMockBuilder(Cache::class) + ->setMethods(array('getItem', 'deleteItem', 'save')) + ->getMock(); + $this->handler = new Psr6SessionHandler($this->psr6, array('prefix' => self::PREFIX, 'ttl' => self::TTL)); + } + + public function testOpen() + { + $this->assertTrue($this->handler->open('foo', 'bar')); + } + + public function testClose() + { + $this->assertTrue($this->handler->close()); + } + + public function testGc() + { + $this->assertTrue($this->handler->gc(4711)); + } + + public function testReadMiss() + { + $item = $this->getItemMock(); + $item->expects($this->once()) + ->method('isHit') + ->willReturn(false); + $this->psr6->expects($this->once()) + ->method('getItem') + ->willReturn($item); + + $this->assertEquals('', $this->handler->read('foo')); + } + + public function testReadHit() + { + $item = $this->getItemMock(); + $item->expects($this->once()) + ->method('isHit') + ->willReturn(true); + $item->expects($this->once()) + ->method('get') + ->willReturn('bar'); + $this->psr6->expects($this->once()) + ->method('getItem') + ->willReturn($item); + + $this->assertEquals('bar', $this->handler->read('foo')); + } + + public function testWrite() + { + $item = $this->getItemMock(); + + $item->expects($this->once()) + ->method('set') + ->with('session value') + ->willReturnSelf(); + $item->expects($this->once()) + ->method('expiresAfter') + ->with(self::TTL) + ->willReturnSelf(); + + $this->psr6->expects($this->once()) + ->method('getItem') + ->with(self::PREFIX.'foo') + ->willReturn($item); + + $this->psr6->expects($this->once()) + ->method('save') + ->with($item) + ->willReturn(true); + + $this->assertTrue($this->handler->write('foo', 'session value')); + } + + public function testDestroy() + { + $this->psr6->expects($this->once()) + ->method('deleteItem') + ->with(self::PREFIX.'foo') + ->willReturn(true); + + $this->assertTrue($this->handler->destroy('foo')); + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function getItemMock() + { + return $this->getMockBuilder(CacheItemInterface::class) + ->setMethods(array('isHit', 'getKey', 'get', 'set', 'expiresAt', 'expiresAfter')) + ->getMock(); + } +} + +class Cache implements CacheItemPoolInterface +{ + public function getItem($key) + { + } + + public function getItems(array $keys = array()) + { + } + + public function hasItem($key) + { + } + + public function clear() + { + } + + public function deleteItem($key) + { + } + + public function deleteItems(array $keys) + { + } + + public function save(CacheItemInterface $item) + { + } + + public function saveDeferred(CacheItemInterface $item) + { + } + + public function commit() + { + } +} From 1b4db2f4c1d9c4e73490011a922ab01d9bcda5c1 Mon Sep 17 00:00:00 2001 From: Ahmed TAILOULOUTE Date: Fri, 7 Feb 2020 22:50:50 +0100 Subject: [PATCH 2/2] [HttpFoundation] Add Psr6SessionHandler --- .../Storage/Handler/Psr6SessionHandler.php | 61 +++++++++---------- .../Handler/Psr6SessionHandlerTest.php | 15 ++--- .../Component/HttpFoundation/composer.json | 1 + 3 files changed, 37 insertions(+), 40 deletions(-) diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/Psr6SessionHandler.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/Psr6SessionHandler.php index 992da46099af1..622aa8973012a 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/Psr6SessionHandler.php +++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/Psr6SessionHandler.php @@ -17,8 +17,9 @@ * Session handler that supports a PSR6 cache implementation. * * @author Tobias Nyholm + * @author Ahmed TAILOULOUTE */ -class Psr6SessionHandler implements \SessionHandlerInterface +class Psr6SessionHandler extends AbstractSessionHandler { /** * @var CacheItemPoolInterface @@ -31,7 +32,7 @@ class Psr6SessionHandler implements \SessionHandlerInterface private $ttl; /** - * @var string Key prefix for shared environments. + * @var string Key prefix for shared environments */ private $prefix; @@ -43,61 +44,54 @@ class Psr6SessionHandler implements \SessionHandlerInterface * @param CacheItemPoolInterface $cache A Cache instance * @param array $options An associative array of cache options */ - public function __construct(CacheItemPoolInterface $cache, array $options = array()) + public function __construct(CacheItemPoolInterface $cache, array $options = []) { $this->cache = $cache; - $this->ttl = isset($options['ttl']) ? (int) $options['ttl'] : 86400; - $this->prefix = isset($options['prefix']) ? $options['prefix'] : 'sfPsr6sess_'; - } + if ($diff = array_diff(array_keys($options), ['prefix', 'ttl'])) { + throw new \InvalidArgumentException(sprintf('The following options are not supported by %s: "%s".', static::class, implode('", "', $diff))); + } - /** - * {@inheritdoc} - */ - public function open($savePath, $sessionName) - { - return true; + $this->ttl = $options['ttl'] ?? null; + $this->prefix = $options['prefix'] ?? 'sf_s'; } /** * {@inheritdoc} */ - public function close() + protected function doRead(string $sessionId) { - return true; + $item = $this->cache->getItem($this->prefix.$sessionId); + + return $item->isHit() ? $item->get() : ''; } /** * {@inheritdoc} */ - public function read($sessionId) + protected function doWrite(string $sessionId, string $data) { - $item = $this->getCacheItem($sessionId); - if ($item->isHit()) { - return $item->get(); - } + $item = $this->cache->getItem($this->prefix.$sessionId); + $item->set($data) + ->expiresAfter($this->ttl ?? ini_get('session.gc_maxlifetime')); - return ''; + return $this->cache->save($item); } /** * {@inheritdoc} */ - public function write($sessionId, $data) + protected function doDestroy(string $sessionId) { - $item = $this->getCacheItem($sessionId); - $item->set($data) - ->expiresAfter($this->ttl); - - return $this->cache->save($item); + return $this->cache->deleteItem($this->prefix.$sessionId); } /** * {@inheritdoc} */ - public function destroy($sessionId) + public function close() { - return $this->cache->deleteItem($this->prefix.$sessionId); + return true; } /** @@ -110,12 +104,13 @@ public function gc($lifetime) } /** - * @param string $sessionId - * - * @return \Psr\Cache\CacheItemInterface + * {@inheritdoc} */ - private function getCacheItem($sessionId) + public function updateTimestamp($sessionId, $data) { - return $this->cache->getItem($this->prefix.$sessionId); + $cacheItem = $this->cache->getItem($this->prefix.$sessionId); + $cacheItem->expiresAfter((int) ($this->ttl ?? ini_get('session.gc_maxlifetime'))); + + return $this->cache->save($cacheItem); } } diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/Psr6SessionHandlerTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/Psr6SessionHandlerTest.php index 918d82c465b6f..747668e36bc01 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/Psr6SessionHandlerTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/Psr6SessionHandlerTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Handler; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Psr\Cache\CacheItemInterface; use Psr\Cache\CacheItemPoolInterface; @@ -30,18 +31,18 @@ class Psr6SessionHandlerTest extends TestCase private $handler; /** - * @var \PHPUnit_Framework_MockObject_MockObject|CacheItemPoolInterface + * @var MockObject|CacheItemPoolInterface */ private $psr6; - protected function setUp() + protected function setUp(): void { parent::setUp(); $this->psr6 = $this->getMockBuilder(Cache::class) - ->setMethods(array('getItem', 'deleteItem', 'save')) + ->setMethods(['getItem', 'deleteItem', 'save']) ->getMock(); - $this->handler = new Psr6SessionHandler($this->psr6, array('prefix' => self::PREFIX, 'ttl' => self::TTL)); + $this->handler = new Psr6SessionHandler($this->psr6, ['prefix' => self::PREFIX, 'ttl' => self::TTL]); } public function testOpen() @@ -125,12 +126,12 @@ public function testDestroy() } /** - * @return \PHPUnit_Framework_MockObject_MockObject + * @return MockObject */ private function getItemMock() { return $this->getMockBuilder(CacheItemInterface::class) - ->setMethods(array('isHit', 'getKey', 'get', 'set', 'expiresAt', 'expiresAfter')) + ->setMethods(['isHit', 'getKey', 'get', 'set', 'expiresAt', 'expiresAfter']) ->getMock(); } } @@ -141,7 +142,7 @@ public function getItem($key) { } - public function getItems(array $keys = array()) + public function getItems(array $keys = []) { } diff --git a/src/Symfony/Component/HttpFoundation/composer.json b/src/Symfony/Component/HttpFoundation/composer.json index 9848c97fbb51e..28fd0259fc4f3 100644 --- a/src/Symfony/Component/HttpFoundation/composer.json +++ b/src/Symfony/Component/HttpFoundation/composer.json @@ -22,6 +22,7 @@ }, "require-dev": { "predis/predis": "~1.0", + "psr/cache": "~1.0", "symfony/mime": "^4.4|^5.0", "symfony/expression-language": "^4.4|^5.0" }, pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy