From 96365cb5c8c6c177c07a897499b52d17b85e2730 Mon Sep 17 00:00:00 2001 From: Masterklavi Date: Sat, 19 Mar 2016 16:45:08 +0500 Subject: [PATCH 1/3] Added Redis session storage --- .../Storage/Handler/RedisSessionHandler.php | 119 ++++++++++++++++ .../Handler/RedisSessionHandlerTest.php | 134 ++++++++++++++++++ 2 files changed, 253 insertions(+) create mode 100644 src/Symfony/Component/HttpFoundation/Session/Storage/Handler/RedisSessionHandler.php create mode 100644 src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/RedisSessionHandlerTest.php diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/RedisSessionHandler.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/RedisSessionHandler.php new file mode 100644 index 0000000000000..e07533f9b0b3e --- /dev/null +++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/RedisSessionHandler.php @@ -0,0 +1,119 @@ + + * + * 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; + +/** + * RedisSessionHandler. + * + * @author Master Klavi + */ +class RedisSessionHandler implements \SessionHandlerInterface +{ + /** + * @var \Redis Redis driver. + */ + private $redis; + + /** + * @var int Time to live in seconds + */ + private $ttl; + + /** + * @var string Key prefix for shared environments. + */ + private $prefix; + + /** + * Constructor. + * + * List of available options: + * * prefix: The prefix to use for the redis keys in order to avoid collision + * * expiretime: The time to live in seconds + * + * @param \Redis $redis A \Redis instance + * @param array $options An associative array options + * + * @throws \InvalidArgumentException When unsupported options are passed + */ + public function __construct(\Redis $redis, array $options = array()) + { + if ($diff = array_diff(array_keys($options), array('prefix', 'expiretime'))) { + throw new \InvalidArgumentException(sprintf( + 'The following options are not supported "%s"', implode(', ', $diff) + )); + } + + $this->redis = $redis; + $this->ttl = isset($options['expiretime']) ? (int) $options['expiretime'] : 86400; + $this->prefix = isset($options['prefix']) ? $options['prefix'] : 'sf2s'; + } + + /** + * {@inheritdoc} + */ + public function open($savePath, $sessionName) + { + return true; + } + + /** + * {@inheritdoc} + */ + public function close() + { + return $this->redis->close(); + } + + /** + * {@inheritdoc} + */ + public function read($sessionId) + { + return $this->redis->get($this->prefix.$sessionId) ?: ''; + } + + /** + * {@inheritdoc} + */ + public function write($sessionId, $data) + { + return $this->redis->setEx($this->prefix.$sessionId, $this->ttl, $data); + } + + /** + * {@inheritdoc} + */ + public function destroy($sessionId) + { + return $this->redis->delete($this->prefix.$sessionId); + } + + /** + * {@inheritdoc} + */ + public function gc($maxlifetime) + { + // not required here because redis will auto expire the records anyhow. + return true; + } + + /** + * Return a Redis instance. + * + * @return \Redis + */ + protected function getRedis() + { + return $this->redis; + } +} diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/RedisSessionHandlerTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/RedisSessionHandlerTest.php new file mode 100644 index 0000000000000..f00cb7a1e174f --- /dev/null +++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/RedisSessionHandlerTest.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 Symfony\Component\HttpFoundation\Session\Storage\Handler\RedisSessionHandler; + +/** + * @requires extension redis + * @group time-sensitive + */ +class RedisSessionHandlerTest extends \PHPUnit_Framework_TestCase +{ + const PREFIX = 'prefix_'; + const TTL = 1000; + /** + * @var RedisSessionHandler + */ + protected $storage; + + protected $redis; + + protected function setUp() + { + parent::setUp(); + $this->redis = $this->getMock('Redis'); + $this->storage = new RedisSessionHandler( + $this->redis, + array('prefix' => self::PREFIX, 'expiretime' => self::TTL) + ); + } + + protected function tearDown() + { + $this->redis = null; + $this->storage = null; + parent::tearDown(); + } + + public function testOpenSession() + { + $this->assertTrue($this->storage->open('', '')); + } + + public function testCloseSession() + { + $this->redis + ->expects($this->once()) + ->method('close') + ->will($this->returnValue(true)) + ; + + $this->assertTrue($this->storage->close()); + } + + public function testReadSession() + { + $this->redis + ->expects($this->once()) + ->method('get') + ->with(self::PREFIX.'id') + ; + + $this->assertEquals('', $this->storage->read('id')); + } + + public function testWriteSession() + { + $this->redis + ->expects($this->once()) + ->method('setEx') + ->with(self::PREFIX.'id', $this->equalTo(self::TTL, 2), 'data') + ->will($this->returnValue(true)) + ; + + $this->assertTrue($this->storage->write('id', 'data')); + } + + public function testDestroySession() + { + $this->redis + ->expects($this->once()) + ->method('delete') + ->with(self::PREFIX.'id') + ->will($this->returnValue(true)) + ; + + $this->assertTrue($this->storage->destroy('id')); + } + + public function testGcSession() + { + $this->assertTrue($this->storage->gc(123)); + } + + /** + * @dataProvider getOptionFixtures + */ + public function testSupportedOptions($options, $supported) + { + try { + new RedisSessionHandlerTest($this->redis, $options); + $this->assertTrue($supported); + } catch (\InvalidArgumentException $e) { + $this->assertFalse($supported); + } + } + + public function getOptionFixtures() + { + return array( + array(array('prefix' => 'session'), true), + array(array('expiretime' => 100), true), + array(array('prefix' => 'session', 'expiretime' => 200), true), + array(array('expiretime' => 100, 'foo' => 'bar'), false), + ); + } + + public function testGetConnection() + { + $method = new \ReflectionMethod($this->storage, 'getRedis'); + $method->setAccessible(true); + + $this->assertInstanceOf('\Redis', $method->invoke($this->storage)); + } +} From 8f48a15eb7da0c83f97e1dc5e08c110e4f15a1b2 Mon Sep 17 00:00:00 2001 From: Masterklavi Date: Sat, 19 Mar 2016 16:53:41 +0500 Subject: [PATCH 2/3] Fixed CS --- .../Session/Storage/Handler/RedisSessionHandler.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/RedisSessionHandler.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/RedisSessionHandler.php index e07533f9b0b3e..61de56dbba0b9 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/RedisSessionHandler.php +++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/RedisSessionHandler.php @@ -19,7 +19,7 @@ class RedisSessionHandler implements \SessionHandlerInterface { /** - * @var \Redis Redis driver. + * @var \Redis Redis driver. */ private $redis; @@ -40,8 +40,8 @@ class RedisSessionHandler implements \SessionHandlerInterface * * prefix: The prefix to use for the redis keys in order to avoid collision * * expiretime: The time to live in seconds * - * @param \Redis $redis A \Redis instance - * @param array $options An associative array options + * @param \Redis $redis A \Redis instance + * @param array $options An associative array options * * @throws \InvalidArgumentException When unsupported options are passed */ From 719fa48258911f11ea91b039713c781f5ce0e832 Mon Sep 17 00:00:00 2001 From: Masterklavi Date: Sat, 19 Mar 2016 16:54:14 +0500 Subject: [PATCH 3/3] Fixed a typo --- .../Tests/Session/Storage/Handler/RedisSessionHandlerTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/RedisSessionHandlerTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/RedisSessionHandlerTest.php index f00cb7a1e174f..00cb64228c8ea 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/RedisSessionHandlerTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/RedisSessionHandlerTest.php @@ -107,7 +107,7 @@ public function testGcSession() public function testSupportedOptions($options, $supported) { try { - new RedisSessionHandlerTest($this->redis, $options); + new RedisSessionHandler($this->redis, $options); $this->assertTrue($supported); } catch (\InvalidArgumentException $e) { $this->assertFalse($supported);