Fix for #19183 to add support for new PHP MongoDB extension in sessions.
This commit is contained in:
parent
6cdb090dd0
commit
ebbc7068f9
@ -61,15 +61,15 @@ class MongoDbSessionHandler implements \SessionHandlerInterface
|
||||
* If you use such an index, you can drop `gc_probability` to 0 since
|
||||
* no garbage-collection is required.
|
||||
*
|
||||
* @param \Mongo|\MongoClient $mongo A MongoClient or Mongo instance
|
||||
* @param array $options An associative array of field options
|
||||
* @param \Mongo|\MongoClient|\MongoDB\Client $mongo A MongoDB\Client, MongoClient or Mongo instance
|
||||
* @param array $options An associative array of field options
|
||||
*
|
||||
* @throws \InvalidArgumentException When MongoClient or Mongo instance not provided
|
||||
* @throws \InvalidArgumentException When "database" or "collection" not provided
|
||||
*/
|
||||
public function __construct($mongo, array $options)
|
||||
{
|
||||
if (!($mongo instanceof \MongoClient || $mongo instanceof \Mongo)) {
|
||||
if (!($mongo instanceof \MongoDB\Client || $mongo instanceof \MongoClient || $mongo instanceof \Mongo)) {
|
||||
throw new \InvalidArgumentException('MongoClient or Mongo instance required');
|
||||
}
|
||||
|
||||
@ -108,7 +108,9 @@ class MongoDbSessionHandler implements \SessionHandlerInterface
|
||||
*/
|
||||
public function destroy($sessionId)
|
||||
{
|
||||
$this->getCollection()->remove(array(
|
||||
$methodName = ($this->mongo instanceof \MongoDB\Client) ? 'deleteOne' : 'remove';
|
||||
|
||||
$this->getCollection()->$methodName(array(
|
||||
$this->options['id_field'] => $sessionId,
|
||||
));
|
||||
|
||||
@ -120,8 +122,10 @@ class MongoDbSessionHandler implements \SessionHandlerInterface
|
||||
*/
|
||||
public function gc($maxlifetime)
|
||||
{
|
||||
$this->getCollection()->remove(array(
|
||||
$this->options['expiry_field'] => array('$lt' => new \MongoDate()),
|
||||
$methodName = ($this->mongo instanceof \MongoDB\Client) ? 'deleteOne' : 'remove';
|
||||
|
||||
$this->getCollection()->$methodName(array(
|
||||
$this->options['expiry_field'] => array('$lt' => $this->createDateTime()),
|
||||
));
|
||||
|
||||
return true;
|
||||
@ -132,18 +136,28 @@ class MongoDbSessionHandler implements \SessionHandlerInterface
|
||||
*/
|
||||
public function write($sessionId, $data)
|
||||
{
|
||||
$expiry = new \MongoDate(time() + (int) ini_get('session.gc_maxlifetime'));
|
||||
$expiry = $this->createDateTime(time() + (int) ini_get('session.gc_maxlifetime'));
|
||||
|
||||
$fields = array(
|
||||
$this->options['data_field'] => new \MongoBinData($data, \MongoBinData::BYTE_ARRAY),
|
||||
$this->options['time_field'] => new \MongoDate(),
|
||||
$this->options['time_field'] => $this->createDateTime(),
|
||||
$this->options['expiry_field'] => $expiry,
|
||||
);
|
||||
|
||||
$this->getCollection()->update(
|
||||
$options = array('upsert' => true);
|
||||
|
||||
if ($this->mongo instanceof \MongoDB\Client) {
|
||||
$fields[$this->options['data_field']] = new \MongoDB\BSON\Binary($data, \MongoDB\BSON\Binary::TYPE_OLD_BINARY);
|
||||
} else {
|
||||
$fields[$this->options['data_field']] = new \MongoBinData($data, \MongoBinData::BYTE_ARRAY);
|
||||
$options['multiple'] = false;
|
||||
}
|
||||
|
||||
$methodName = ($this->mongo instanceof \MongoDB\Client) ? 'updateOne' : 'update';
|
||||
|
||||
$this->getCollection()->$methodName(
|
||||
array($this->options['id_field'] => $sessionId),
|
||||
array('$set' => $fields),
|
||||
array('upsert' => true, 'multiple' => false)
|
||||
$options
|
||||
);
|
||||
|
||||
return true;
|
||||
@ -156,10 +170,18 @@ class MongoDbSessionHandler implements \SessionHandlerInterface
|
||||
{
|
||||
$dbData = $this->getCollection()->findOne(array(
|
||||
$this->options['id_field'] => $sessionId,
|
||||
$this->options['expiry_field'] => array('$gte' => new \MongoDate()),
|
||||
$this->options['expiry_field'] => array('$gte' => $this->createDateTime()),
|
||||
));
|
||||
|
||||
return null === $dbData ? '' : $dbData[$this->options['data_field']]->bin;
|
||||
if (null === $dbData) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if ($dbData[$this->options['data_field']] instanceof \MongoDB\BSON\Binary) {
|
||||
return $dbData[$this->options['data_field']]->getData();
|
||||
}
|
||||
|
||||
return $dbData[$this->options['data_field']]->bin;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -185,4 +207,24 @@ class MongoDbSessionHandler implements \SessionHandlerInterface
|
||||
{
|
||||
return $this->mongo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a date object using the class appropriate for the current mongo connection.
|
||||
*
|
||||
* Return an instance of a MongoDate or \MongoDB\BSON\UTCDateTime
|
||||
*
|
||||
* @param int $seconds An integer representing UTC seconds since Jan 1 1970. Defaults to now.
|
||||
*/
|
||||
private function createDateTime($seconds = null)
|
||||
{
|
||||
if (is_null($seconds)) {
|
||||
$seconds = time();
|
||||
}
|
||||
|
||||
if ($this->mongo instanceof \MongoDB\Client) {
|
||||
return new \MongoDB\BSON\UTCDateTime($seconds * 1000);
|
||||
}
|
||||
|
||||
return new \MongoDate($seconds);
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,6 @@ use Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandl
|
||||
|
||||
/**
|
||||
* @author Markus Bachmann <markus.bachmann@bachi.biz>
|
||||
* @requires extension mongo
|
||||
* @group time-sensitive
|
||||
*/
|
||||
class MongoDbSessionHandlerTest extends \PHPUnit_Framework_TestCase
|
||||
@ -31,7 +30,15 @@ class MongoDbSessionHandlerTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$mongoClass = version_compare(phpversion('mongo'), '1.3.0', '<') ? 'Mongo' : 'MongoClient';
|
||||
if (!extension_loaded('mongo') && !extension_loaded('mongodb')) {
|
||||
$this->markTestSkipped('The Mongo or MongoDB extension is required.');
|
||||
}
|
||||
|
||||
if (phpversion('mongodb')) {
|
||||
$mongoClass = 'MongoDB\Client';
|
||||
} else {
|
||||
$mongoClass = version_compare(phpversion('mongo'), '1.3.0', '<') ? 'Mongo' : 'MongoClient';
|
||||
}
|
||||
|
||||
$this->mongo = $this->getMockBuilder($mongoClass)
|
||||
->disableOriginalConstructor()
|
||||
@ -98,14 +105,28 @@ class MongoDbSessionHandlerTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
$that->assertArrayHasKey($that->options['expiry_field'], $criteria);
|
||||
$that->assertArrayHasKey('$gte', $criteria[$that->options['expiry_field']]);
|
||||
$that->assertInstanceOf('MongoDate', $criteria[$that->options['expiry_field']]['$gte']);
|
||||
$that->assertGreaterThanOrEqual($criteria[$that->options['expiry_field']]['$gte']->sec, $testTimeout);
|
||||
|
||||
return array(
|
||||
if (phpversion('mongodb')) {
|
||||
$that->assertInstanceOf('MongoDB\BSON\UTCDateTime', $criteria[$that->options['expiry_field']]['$gte']);
|
||||
$that->assertGreaterThanOrEqual(round(intval((string) $criteria[$that->options['expiry_field']]['$gte']) / 1000), $testTimeout);
|
||||
} else {
|
||||
$that->assertInstanceOf('MongoDate', $criteria[$that->options['expiry_field']]['$gte']);
|
||||
$that->assertGreaterThanOrEqual($criteria[$that->options['expiry_field']]['$gte']->sec, $testTimeout);
|
||||
}
|
||||
|
||||
$fields = array(
|
||||
$that->options['id_field'] => 'foo',
|
||||
$that->options['data_field'] => new \MongoBinData('bar', \MongoBinData::BYTE_ARRAY),
|
||||
$that->options['id_field'] => new \MongoDate(),
|
||||
);
|
||||
|
||||
if (phpversion('mongodb')) {
|
||||
$fields[$that->options['data_field']] = new \MongoDB\BSON\Binary('bar', \MongoDB\BSON\Binary::TYPE_OLD_BINARY);
|
||||
$fields[$that->options['id_field']] = new \MongoDB\BSON\UTCDateTime(time() * 1000);
|
||||
} else {
|
||||
$fields[$that->options['data_field']] = new \MongoBinData('bar', \MongoBinData::BYTE_ARRAY);
|
||||
$fields[$that->options['id_field']] = new \MongoDate();
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}));
|
||||
|
||||
$this->assertEquals('bar', $this->storage->read('foo'));
|
||||
@ -123,11 +144,18 @@ class MongoDbSessionHandlerTest extends \PHPUnit_Framework_TestCase
|
||||
$that = $this;
|
||||
$data = array();
|
||||
|
||||
$methodName = phpversion('mongodb') ? 'updateOne' : 'update';
|
||||
|
||||
$collection->expects($this->once())
|
||||
->method('update')
|
||||
->method($methodName)
|
||||
->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);
|
||||
|
||||
if (phpversion('mongodb')) {
|
||||
$that->assertEquals(array('upsert' => true), $options);
|
||||
} else {
|
||||
$that->assertEquals(array('upsert' => true, 'multiple' => false), $options);
|
||||
}
|
||||
|
||||
$data = $updateData['$set'];
|
||||
}));
|
||||
@ -135,10 +163,17 @@ class MongoDbSessionHandlerTest extends \PHPUnit_Framework_TestCase
|
||||
$expectedExpiry = time() + (int) ini_get('session.gc_maxlifetime');
|
||||
$this->assertTrue($this->storage->write('foo', 'bar'));
|
||||
|
||||
$this->assertEquals('bar', $data[$this->options['data_field']]->bin);
|
||||
$that->assertInstanceOf('MongoDate', $data[$this->options['time_field']]);
|
||||
$this->assertInstanceOf('MongoDate', $data[$this->options['expiry_field']]);
|
||||
$this->assertGreaterThanOrEqual($expectedExpiry, $data[$this->options['expiry_field']]->sec);
|
||||
if (phpversion('mongodb')) {
|
||||
$that->assertEquals('bar', $data[$that->options['data_field']]->getData());
|
||||
$that->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$that->options['time_field']]);
|
||||
$that->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$that->options['expiry_field']]);
|
||||
$that->assertGreaterThanOrEqual($expectedExpiry, round(intval((string) $data[$that->options['expiry_field']]) / 1000));
|
||||
} else {
|
||||
$that->assertEquals('bar', $data[$that->options['data_field']]->bin);
|
||||
$that->assertInstanceOf('MongoDate', $data[$that->options['time_field']]);
|
||||
$that->assertInstanceOf('MongoDate', $data[$that->options['expiry_field']]);
|
||||
$that->assertGreaterThanOrEqual($expectedExpiry, $data[$that->options['expiry_field']]->sec);
|
||||
}
|
||||
}
|
||||
|
||||
public function testWriteWhenUsingExpiresField()
|
||||
@ -164,20 +199,33 @@ class MongoDbSessionHandlerTest extends \PHPUnit_Framework_TestCase
|
||||
$that = $this;
|
||||
$data = array();
|
||||
|
||||
$methodName = phpversion('mongodb') ? 'updateOne' : 'update';
|
||||
|
||||
$collection->expects($this->once())
|
||||
->method('update')
|
||||
->method($methodName)
|
||||
->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);
|
||||
|
||||
if (phpversion('mongodb')) {
|
||||
$that->assertEquals(array('upsert' => true), $options);
|
||||
} else {
|
||||
$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']]);
|
||||
if (phpversion('mongodb')) {
|
||||
$that->assertEquals('bar', $data[$that->options['data_field']]->getData());
|
||||
$that->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$that->options['time_field']]);
|
||||
$that->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$that->options['expiry_field']]);
|
||||
} else {
|
||||
$that->assertEquals('bar', $data[$that->options['data_field']]->bin);
|
||||
$that->assertInstanceOf('MongoDate', $data[$that->options['time_field']]);
|
||||
$that->assertInstanceOf('MongoDate', $data[$that->options['expiry_field']]);
|
||||
}
|
||||
}
|
||||
|
||||
public function testReplaceSessionData()
|
||||
@ -191,8 +239,10 @@ class MongoDbSessionHandlerTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
$data = array();
|
||||
|
||||
$methodName = phpversion('mongodb') ? 'updateOne' : 'update';
|
||||
|
||||
$collection->expects($this->exactly(2))
|
||||
->method('update')
|
||||
->method($methodName)
|
||||
->will($this->returnCallback(function ($criteria, $updateData, $options) use (&$data) {
|
||||
$data = $updateData;
|
||||
}));
|
||||
@ -200,7 +250,11 @@ class MongoDbSessionHandlerTest extends \PHPUnit_Framework_TestCase
|
||||
$this->storage->write('foo', 'bar');
|
||||
$this->storage->write('foo', 'foobar');
|
||||
|
||||
$this->assertEquals('foobar', $data['$set'][$this->options['data_field']]->bin);
|
||||
if (phpversion('mongodb')) {
|
||||
$this->assertEquals('foobar', $data['$set'][$this->options['data_field']]->getData());
|
||||
} else {
|
||||
$this->assertEquals('foobar', $data['$set'][$this->options['data_field']]->bin);
|
||||
}
|
||||
}
|
||||
|
||||
public function testDestroy()
|
||||
@ -212,8 +266,10 @@ class MongoDbSessionHandlerTest extends \PHPUnit_Framework_TestCase
|
||||
->with($this->options['database'], $this->options['collection'])
|
||||
->will($this->returnValue($collection));
|
||||
|
||||
$methodName = phpversion('mongodb') ? 'deleteOne' : 'remove';
|
||||
|
||||
$collection->expects($this->once())
|
||||
->method('remove')
|
||||
->method($methodName)
|
||||
->with(array($this->options['id_field'] => 'foo'));
|
||||
|
||||
$this->assertTrue($this->storage->destroy('foo'));
|
||||
@ -230,19 +286,45 @@ class MongoDbSessionHandlerTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
$that = $this;
|
||||
|
||||
$methodName = phpversion('mongodb') ? 'deleteOne' : 'remove';
|
||||
|
||||
$collection->expects($this->once())
|
||||
->method('remove')
|
||||
->method($methodName)
|
||||
->will($this->returnCallback(function ($criteria) use ($that) {
|
||||
$that->assertInstanceOf('MongoDate', $criteria[$that->options['expiry_field']]['$lt']);
|
||||
$that->assertGreaterThanOrEqual(time() - 1, $criteria[$that->options['expiry_field']]['$lt']->sec);
|
||||
if (phpversion('mongodb')) {
|
||||
$that->assertInstanceOf('MongoDB\BSON\UTCDateTime', $criteria[$that->options['expiry_field']]['$lt']);
|
||||
$that->assertGreaterThanOrEqual(time() - 1, round(intval((string) $criteria[$that->options['expiry_field']]['$lt']) / 1000));
|
||||
} else {
|
||||
$that->assertInstanceOf('MongoDate', $criteria[$that->options['expiry_field']]['$lt']);
|
||||
$that->assertGreaterThanOrEqual(time() - 1, $criteria[$that->options['expiry_field']]['$lt']->sec);
|
||||
}
|
||||
}));
|
||||
|
||||
$this->assertTrue($this->storage->gc(1));
|
||||
}
|
||||
|
||||
public function testGetConnection()
|
||||
{
|
||||
$method = new \ReflectionMethod($this->storage, 'getMongo');
|
||||
$method->setAccessible(true);
|
||||
|
||||
if (phpversion('mongodb')) {
|
||||
$mongoClass = 'MongoDB\Client';
|
||||
} else {
|
||||
$mongoClass = version_compare(phpversion('mongo'), '1.3.0', '<') ? 'Mongo' : 'MongoClient';
|
||||
}
|
||||
|
||||
$this->assertInstanceOf($mongoClass, $method->invoke($this->storage));
|
||||
}
|
||||
|
||||
private function createMongoCollectionMock()
|
||||
{
|
||||
$collection = $this->getMockBuilder('MongoCollection')
|
||||
$collectionClass = 'MongoCollection';
|
||||
if (phpversion('mongodb')) {
|
||||
$collectionClass = 'MongoDB\Collection';
|
||||
}
|
||||
|
||||
$collection = $this->getMockBuilder($collectionClass)
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
|
Reference in New Issue
Block a user