Skip to content

Commit

Permalink
feature #11668 [HttpFoundation] MongoDbSessionHandler supports auto e…
Browse files Browse the repository at this point in the history
…xpiry via configurable expiry_field (catchamonkey)

This PR was merged into the 2.6-dev branch.

Discussion
----------

[HttpFoundation] MongoDbSessionHandler supports auto expiry via configurable expiry_field

| Q             | A
| ------------- | ---
| Bug fix?      | no
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #11508
| License       | MIT
| Doc PR        | no

This PR applies #11510 to master instead of 2.3

Should be merged on master:
- [x] after #11667 is merged to 2.3
- [x] and after 2.3 is merged back to master

Commits
-------

beca900 [HttpFoundation] MongoDbSessionHandler supports auto expiry via configurable expiry_field
  • Loading branch information
nicolas-grekas committed Aug 18, 2014
2 parents 7f38207 + beca900 commit 73c7eae
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 10 deletions.
Expand Up @@ -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);
}

Expand Down Expand Up @@ -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(
Expand All @@ -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)
);

Expand Down
Expand Up @@ -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);
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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));
}

public function testGetConnection()
Expand Down

0 comments on commit 73c7eae

Please sign in to comment.