diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MongoDbSessionHandler.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MongoDbSessionHandler.php index 87aa7fb4f77a..b151c8e33d4e 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MongoDbSessionHandler.php +++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MongoDbSessionHandler.php @@ -62,9 +62,10 @@ public function __construct($mongo, array $options) $this->mongo = $mongo; $this->options = array_merge(array( - 'id_field' => '_id', - 'data_field' => 'data', - 'time_field' => 'time', + 'id_field' => '_id', + 'data_field' => 'data', + 'time_field' => 'time', + 'expiry_field' => false, ), $options); } @@ -109,6 +110,9 @@ public function gc($maxlifetime) * * See: http://docs.mongodb.org/manual/tutorial/expire-data/ */ + if (false !== $this->options['expiry_field']) { + return true; + } $time = new \MongoDate(time() - $maxlifetime); $this->getCollection()->remove(array( @@ -123,12 +127,27 @@ public function gc($maxlifetime) */ public function write($sessionId, $data) { + $fields = array( + $this->options['data_field'] => new \MongoBinData($data, \MongoBinData::BYTE_ARRAY), + $this->options['time_field'] => new \MongoDate(), + ); + + /* Note: As discussed in the gc method of this class. You can utilise + * TTL collections in MongoDB 2.2+ + * We are setting the "expiry_field" as part of the write operation here + * You will need to create the index on your collection that expires documents + * at that time + * e.g. + * db.MySessionCollection.ensureIndex( { "expireAt": 1 }, { expireAfterSeconds: 0 } ) + */ + if (false !== $this->options['expiry_field']) { + $expiry = new \MongoDate(time() + (int) ini_get('session.gc_maxlifetime')); + $fields[$this->options['expiry_field']] = $expiry; + } + $this->getCollection()->update( array($this->options['id_field'] => $sessionId), - array('$set' => array( - $this->options['data_field'] => new \MongoBinData($data, \MongoBinData::BYTE_ARRAY), - $this->options['time_field'] => new \MongoDate(), - )), + array('$set' => $fields), array('upsert' => true, 'multiple' => false) ); diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php index 9577d5247886..19170bbbf02e 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php @@ -41,7 +41,7 @@ protected function setUp() 'data_field' => 'data', 'time_field' => 'time', 'database' => 'sf2-test', - 'collection' => 'session-test' + 'collection' => 'session-test', ); $this->storage = new MongoDbSessionHandler($this->mongo, $this->options); @@ -100,6 +100,45 @@ public function testWrite() $that->assertInstanceOf('MongoDate', $data[$this->options['time_field']]); } + public function testWriteWhenUsingExpiresField() + { + $this->options = array( + 'id_field' => '_id', + 'data_field' => 'data', + 'time_field' => 'time', + 'database' => 'sf2-test', + 'collection' => 'session-test', + 'expiry_field' => 'expiresAt' + ); + + $this->storage = new MongoDbSessionHandler($this->mongo, $this->options); + + $collection = $this->createMongoCollectionMock(); + + $this->mongo->expects($this->once()) + ->method('selectCollection') + ->with($this->options['database'], $this->options['collection']) + ->will($this->returnValue($collection)); + + $that = $this; + $data = array(); + + $collection->expects($this->once()) + ->method('update') + ->will($this->returnCallback(function ($criteria, $updateData, $options) use ($that, &$data) { + $that->assertEquals(array($that->options['id_field'] => 'foo'), $criteria); + $that->assertEquals(array('upsert' => true, 'multiple' => false), $options); + + $data = $updateData['$set']; + })); + + $this->assertTrue($this->storage->write('foo', 'bar')); + + $this->assertEquals('bar', $data[$this->options['data_field']]->bin); + $that->assertInstanceOf('MongoDate', $data[$this->options['time_field']]); + $that->assertInstanceOf('MongoDate', $data[$this->options['expiry_field']]); + } + public function testReplaceSessionData() { $collection = $this->createMongoCollectionMock(); @@ -154,10 +193,36 @@ public function testGc() ->method('remove') ->will($this->returnCallback(function ($criteria) use ($that) { $that->assertInstanceOf('MongoDate', $criteria[$that->options['time_field']]['$lt']); - $that->assertGreaterThanOrEqual(time() - -1, $criteria[$that->options['time_field']]['$lt']->sec); + $that->assertGreaterThanOrEqual(time() - 1, $criteria[$that->options['time_field']]['$lt']->sec); })); - $this->assertTrue($this->storage->gc(-1)); + $this->assertTrue($this->storage->gc(1)); + } + + public function testGcWhenUsingExpiresField() + { + $this->options = array( + 'id_field' => '_id', + 'data_field' => 'data', + 'time_field' => 'time', + 'database' => 'sf2-test', + 'collection' => 'session-test', + 'expiry_field' => 'expiresAt' + ); + + $this->storage = new MongoDbSessionHandler($this->mongo, $this->options); + + $collection = $this->createMongoCollectionMock(); + + $this->mongo->expects($this->never()) + ->method('selectCollection'); + + $that = $this; + + $collection->expects($this->never()) + ->method('remove'); + + $this->assertTrue($this->storage->gc(1)); } private function createMongoCollectionMock()