From ba865f25ff19dea38bc0a55f1f8b89d355119a3f Mon Sep 17 00:00:00 2001 From: Tobias Nyholm Date: Wed, 28 Jun 2017 14:12:41 +0200 Subject: [PATCH 1/7] Adding PSR6 session handler --- .../Storage/Handler/Psr6SessionHandler.php | 121 ++++++++++++++++ .../Handler/Psr6SessionHandlerTest.php | 134 ++++++++++++++++++ 2 files changed, 255 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/Session/Storage/Handler/Psr6SessionHandler.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/Psr6SessionHandler.php new file mode 100644 index 0000000000000..2ebd18566b61f --- /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 +{ + /** + * @type CacheItemPoolInterface + */ + private $cache; + + /** + * @type int Time to live in seconds + */ + private $ttl; + + /** + * @type 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 = []) + { + $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(string $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..a7eca057bfe01 --- /dev/null +++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/Psr6SessionHandlerTest.php @@ -0,0 +1,134 @@ + + * + * 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(CacheItemPoolInterface::class)->getMock(); + $this->handler = new Psr6SessionHandler($this->psr6, ['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(): \PHPUnit_Framework_MockObject_MockObject + { + return $this->getMockBuilder(CacheItemInterface::class) + ->setMethods(['isHit', 'getKey', 'get', 'set', 'expiresAt', 'expiresAfter']) + ->getMock(); + } +} From 704411aa8dda466a719dea89438ed2539992434b Mon Sep 17 00:00:00 2001 From: Tobias Nyholm Date: Wed, 28 Jun 2017 17:53:20 +0200 Subject: [PATCH 2/7] Added changelog --- src/Symfony/Component/HttpFoundation/CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Symfony/Component/HttpFoundation/CHANGELOG.md b/src/Symfony/Component/HttpFoundation/CHANGELOG.md index e1fdf77b9b8ae..73308f3e98491 100644 --- a/src/Symfony/Component/HttpFoundation/CHANGELOG.md +++ b/src/Symfony/Component/HttpFoundation/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +3.4.0 +----- + + * Added `Psr6SessionHandler`. + 3.3.0 ----- From 97e1364bac2b44dca691952bf39f9b404d9064e0 Mon Sep 17 00:00:00 2001 From: Tobias Nyholm Date: Wed, 28 Jun 2017 17:54:51 +0200 Subject: [PATCH 3/7] Style fixes from fabbot.io --- .../Session/Storage/Handler/Psr6SessionHandler.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/Psr6SessionHandler.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/Psr6SessionHandler.php index 2ebd18566b61f..50167c678f93a 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/Psr6SessionHandler.php +++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/Psr6SessionHandler.php @@ -21,33 +21,33 @@ class Psr6SessionHandler implements \SessionHandlerInterface { /** - * @type CacheItemPoolInterface + * @var CacheItemPoolInterface */ private $cache; /** - * @type int Time to live in seconds + * @var int Time to live in seconds */ private $ttl; /** - * @type string Key prefix for shared environments. + * @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 + * * 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 = []) + public function __construct(CacheItemPoolInterface $cache, array $options = array()) { $this->cache = $cache; - $this->ttl = isset($options['ttl']) ? (int) $options['ttl'] : 86400; + $this->ttl = isset($options['ttl']) ? (int) $options['ttl'] : 86400; $this->prefix = isset($options['prefix']) ? $options['prefix'] : 'sfPsr6sess_'; } From 59accf63e5aa5899ea0016bfc6c89ea5e97110e3 Mon Sep 17 00:00:00 2001 From: Tobias Nyholm Date: Wed, 28 Jun 2017 17:57:23 +0200 Subject: [PATCH 4/7] Specify methods better --- .../Tests/Session/Storage/Handler/Psr6SessionHandlerTest.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 a7eca057bfe01..16b0b13fd76e5 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/Psr6SessionHandlerTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/Psr6SessionHandlerTest.php @@ -38,7 +38,9 @@ protected function setUp() { parent::setUp(); - $this->psr6 = $this->getMockBuilder(CacheItemPoolInterface::class)->getMock(); + $this->psr6 = $this->getMockBuilder(CacheItemPoolInterface::class) + ->setMethods(['getItem', 'deleteItem']) + ->getMock(); $this->handler = new Psr6SessionHandler($this->psr6, ['prefix' => self::PREFIX, 'ttl' => self::TTL]); } From daa49358af5b046ed3ba9574df95e8fbd98e0ba5 Mon Sep 17 00:00:00 2001 From: Tobias Nyholm Date: Wed, 28 Jun 2017 18:13:03 +0200 Subject: [PATCH 5/7] Fixed the tests --- .../Storage/Handler/Psr6SessionHandlerTest.php | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) 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 16b0b13fd76e5..7648da0ee0651 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/Psr6SessionHandlerTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/Psr6SessionHandlerTest.php @@ -14,6 +14,7 @@ use PHPUnit\Framework\TestCase; use Psr\Cache\CacheItemInterface; use Psr\Cache\CacheItemPoolInterface; +use Psr\Cache\InvalidArgumentException; use Symfony\Component\HttpFoundation\Session\Storage\Handler\Psr6SessionHandler; /** @@ -38,8 +39,8 @@ protected function setUp() { parent::setUp(); - $this->psr6 = $this->getMockBuilder(CacheItemPoolInterface::class) - ->setMethods(['getItem', 'deleteItem']) + $this->psr6 = $this->getMockBuilder(Cache::class) + ->setMethods(['getItem', 'deleteItem', 'save']) ->getMock(); $this->handler = new Psr6SessionHandler($this->psr6, ['prefix' => self::PREFIX, 'ttl' => self::TTL]); } @@ -134,3 +135,16 @@ private function getItemMock(): \PHPUnit_Framework_MockObject_MockObject ->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 5eb4af9e2681427b7c5822c5e2c24678dced1e08 Mon Sep 17 00:00:00 2001 From: Tobias Nyholm Date: Wed, 28 Jun 2017 18:24:39 +0200 Subject: [PATCH 6/7] Updated requirements and removed php7 code --- .../Tests/Session/Storage/Handler/Psr6SessionHandlerTest.php | 2 +- src/Symfony/Component/HttpFoundation/composer.json | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) 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 7648da0ee0651..8a805e61e4cb1 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/Psr6SessionHandlerTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/Psr6SessionHandlerTest.php @@ -128,7 +128,7 @@ public function testDestroy() /** * @return \PHPUnit_Framework_MockObject_MockObject */ - private function getItemMock(): \PHPUnit_Framework_MockObject_MockObject + private function getItemMock() { return $this->getMockBuilder(CacheItemInterface::class) ->setMethods(['isHit', 'getKey', 'get', 'set', 'expiresAt', 'expiresAfter']) diff --git a/src/Symfony/Component/HttpFoundation/composer.json b/src/Symfony/Component/HttpFoundation/composer.json index 44192dfe17d3c..4b2e51ed26138 100644 --- a/src/Symfony/Component/HttpFoundation/composer.json +++ b/src/Symfony/Component/HttpFoundation/composer.json @@ -20,7 +20,8 @@ "symfony/polyfill-mbstring": "~1.1" }, "require-dev": { - "symfony/expression-language": "~2.8|~3.0|~4.0" + "symfony/expression-language": "~2.8|~3.0|~4.0", + "psr/cache": "~1.0" }, "autoload": { "psr-4": { "Symfony\\Component\\HttpFoundation\\": "" }, From 074c46041ca3bca3789a53de2bbcf3de800cc2b5 Mon Sep 17 00:00:00 2001 From: Tobias Nyholm Date: Wed, 28 Jun 2017 19:30:02 +0200 Subject: [PATCH 7/7] Removed PHP7 code --- .../Storage/Handler/Psr6SessionHandler.php | 2 +- .../Handler/Psr6SessionHandlerTest.php | 51 ++++++++++++++----- 2 files changed, 39 insertions(+), 14 deletions(-) diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/Psr6SessionHandler.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/Psr6SessionHandler.php index 50167c678f93a..992da46099af1 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/Psr6SessionHandler.php +++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/Psr6SessionHandler.php @@ -114,7 +114,7 @@ public function gc($lifetime) * * @return \Psr\Cache\CacheItemInterface */ - private function getCacheItem(string $sessionId) + 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 index 8a805e61e4cb1..918d82c465b6f 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/Psr6SessionHandlerTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/Psr6SessionHandlerTest.php @@ -14,7 +14,6 @@ use PHPUnit\Framework\TestCase; use Psr\Cache\CacheItemInterface; use Psr\Cache\CacheItemPoolInterface; -use Psr\Cache\InvalidArgumentException; use Symfony\Component\HttpFoundation\Session\Storage\Handler\Psr6SessionHandler; /** @@ -40,9 +39,9 @@ protected function setUp() parent::setUp(); $this->psr6 = $this->getMockBuilder(Cache::class) - ->setMethods(['getItem', 'deleteItem', 'save']) + ->setMethods(array('getItem', 'deleteItem', 'save')) ->getMock(); - $this->handler = new Psr6SessionHandler($this->psr6, ['prefix' => self::PREFIX, 'ttl' => self::TTL]); + $this->handler = new Psr6SessionHandler($this->psr6, array('prefix' => self::PREFIX, 'ttl' => self::TTL)); } public function testOpen() @@ -131,20 +130,46 @@ public function testDestroy() private function getItemMock() { return $this->getMockBuilder(CacheItemInterface::class) - ->setMethods(['isHit', 'getKey', 'get', 'set', 'expiresAt', 'expiresAfter']) + ->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() {} + 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() + { + } }