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
|
* If you use such an index, you can drop `gc_probability` to 0 since
|
||||||
* no garbage-collection is required.
|
* no garbage-collection is required.
|
||||||
*
|
*
|
||||||
* @param \Mongo|\MongoClient $mongo A MongoClient or Mongo instance
|
* @param \Mongo|\MongoClient|\MongoDB\Client $mongo A MongoDB\Client, MongoClient or Mongo instance
|
||||||
* @param array $options An associative array of field options
|
* @param array $options An associative array of field options
|
||||||
*
|
*
|
||||||
* @throws \InvalidArgumentException When MongoClient or Mongo instance not provided
|
* @throws \InvalidArgumentException When MongoClient or Mongo instance not provided
|
||||||
* @throws \InvalidArgumentException When "database" or "collection" not provided
|
* @throws \InvalidArgumentException When "database" or "collection" not provided
|
||||||
*/
|
*/
|
||||||
public function __construct($mongo, array $options)
|
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');
|
throw new \InvalidArgumentException('MongoClient or Mongo instance required');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,7 +108,9 @@ class MongoDbSessionHandler implements \SessionHandlerInterface
|
|||||||
*/
|
*/
|
||||||
public function destroy($sessionId)
|
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,
|
$this->options['id_field'] => $sessionId,
|
||||||
));
|
));
|
||||||
|
|
||||||
@ -120,8 +122,10 @@ class MongoDbSessionHandler implements \SessionHandlerInterface
|
|||||||
*/
|
*/
|
||||||
public function gc($maxlifetime)
|
public function gc($maxlifetime)
|
||||||
{
|
{
|
||||||
$this->getCollection()->remove(array(
|
$methodName = ($this->mongo instanceof \MongoDB\Client) ? 'deleteOne' : 'remove';
|
||||||
$this->options['expiry_field'] => array('$lt' => new \MongoDate()),
|
|
||||||
|
$this->getCollection()->$methodName(array(
|
||||||
|
$this->options['expiry_field'] => array('$lt' => $this->createDateTime()),
|
||||||
));
|
));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -132,18 +136,28 @@ class MongoDbSessionHandler implements \SessionHandlerInterface
|
|||||||
*/
|
*/
|
||||||
public function write($sessionId, $data)
|
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(
|
$fields = array(
|
||||||
$this->options['data_field'] => new \MongoBinData($data, \MongoBinData::BYTE_ARRAY),
|
$this->options['time_field'] => $this->createDateTime(),
|
||||||
$this->options['time_field'] => new \MongoDate(),
|
|
||||||
$this->options['expiry_field'] => $expiry,
|
$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($this->options['id_field'] => $sessionId),
|
||||||
array('$set' => $fields),
|
array('$set' => $fields),
|
||||||
array('upsert' => true, 'multiple' => false)
|
$options
|
||||||
);
|
);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -156,10 +170,18 @@ class MongoDbSessionHandler implements \SessionHandlerInterface
|
|||||||
{
|
{
|
||||||
$dbData = $this->getCollection()->findOne(array(
|
$dbData = $this->getCollection()->findOne(array(
|
||||||
$this->options['id_field'] => $sessionId,
|
$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;
|
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>
|
* @author Markus Bachmann <markus.bachmann@bachi.biz>
|
||||||
* @requires extension mongo
|
|
||||||
* @group time-sensitive
|
* @group time-sensitive
|
||||||
*/
|
*/
|
||||||
class MongoDbSessionHandlerTest extends \PHPUnit_Framework_TestCase
|
class MongoDbSessionHandlerTest extends \PHPUnit_Framework_TestCase
|
||||||
@ -31,7 +30,15 @@ class MongoDbSessionHandlerTest extends \PHPUnit_Framework_TestCase
|
|||||||
{
|
{
|
||||||
parent::setUp();
|
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)
|
$this->mongo = $this->getMockBuilder($mongoClass)
|
||||||
->disableOriginalConstructor()
|
->disableOriginalConstructor()
|
||||||
@ -98,14 +105,28 @@ class MongoDbSessionHandlerTest extends \PHPUnit_Framework_TestCase
|
|||||||
|
|
||||||
$that->assertArrayHasKey($that->options['expiry_field'], $criteria);
|
$that->assertArrayHasKey($that->options['expiry_field'], $criteria);
|
||||||
$that->assertArrayHasKey('$gte', $criteria[$that->options['expiry_field']]);
|
$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['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'));
|
$this->assertEquals('bar', $this->storage->read('foo'));
|
||||||
@ -123,11 +144,18 @@ class MongoDbSessionHandlerTest extends \PHPUnit_Framework_TestCase
|
|||||||
$that = $this;
|
$that = $this;
|
||||||
$data = array();
|
$data = array();
|
||||||
|
|
||||||
|
$methodName = phpversion('mongodb') ? 'updateOne' : 'update';
|
||||||
|
|
||||||
$collection->expects($this->once())
|
$collection->expects($this->once())
|
||||||
->method('update')
|
->method($methodName)
|
||||||
->will($this->returnCallback(function ($criteria, $updateData, $options) use ($that, &$data) {
|
->will($this->returnCallback(function ($criteria, $updateData, $options) use ($that, &$data) {
|
||||||
$that->assertEquals(array($that->options['id_field'] => 'foo'), $criteria);
|
$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'];
|
$data = $updateData['$set'];
|
||||||
}));
|
}));
|
||||||
@ -135,10 +163,17 @@ class MongoDbSessionHandlerTest extends \PHPUnit_Framework_TestCase
|
|||||||
$expectedExpiry = time() + (int) ini_get('session.gc_maxlifetime');
|
$expectedExpiry = time() + (int) ini_get('session.gc_maxlifetime');
|
||||||
$this->assertTrue($this->storage->write('foo', 'bar'));
|
$this->assertTrue($this->storage->write('foo', 'bar'));
|
||||||
|
|
||||||
$this->assertEquals('bar', $data[$this->options['data_field']]->bin);
|
if (phpversion('mongodb')) {
|
||||||
$that->assertInstanceOf('MongoDate', $data[$this->options['time_field']]);
|
$that->assertEquals('bar', $data[$that->options['data_field']]->getData());
|
||||||
$this->assertInstanceOf('MongoDate', $data[$this->options['expiry_field']]);
|
$that->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$that->options['time_field']]);
|
||||||
$this->assertGreaterThanOrEqual($expectedExpiry, $data[$this->options['expiry_field']]->sec);
|
$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()
|
public function testWriteWhenUsingExpiresField()
|
||||||
@ -164,20 +199,33 @@ class MongoDbSessionHandlerTest extends \PHPUnit_Framework_TestCase
|
|||||||
$that = $this;
|
$that = $this;
|
||||||
$data = array();
|
$data = array();
|
||||||
|
|
||||||
|
$methodName = phpversion('mongodb') ? 'updateOne' : 'update';
|
||||||
|
|
||||||
$collection->expects($this->once())
|
$collection->expects($this->once())
|
||||||
->method('update')
|
->method($methodName)
|
||||||
->will($this->returnCallback(function ($criteria, $updateData, $options) use ($that, &$data) {
|
->will($this->returnCallback(function ($criteria, $updateData, $options) use ($that, &$data) {
|
||||||
$that->assertEquals(array($that->options['id_field'] => 'foo'), $criteria);
|
$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'];
|
$data = $updateData['$set'];
|
||||||
}));
|
}));
|
||||||
|
|
||||||
$this->assertTrue($this->storage->write('foo', 'bar'));
|
$this->assertTrue($this->storage->write('foo', 'bar'));
|
||||||
|
|
||||||
$this->assertEquals('bar', $data[$this->options['data_field']]->bin);
|
if (phpversion('mongodb')) {
|
||||||
$that->assertInstanceOf('MongoDate', $data[$this->options['time_field']]);
|
$that->assertEquals('bar', $data[$that->options['data_field']]->getData());
|
||||||
$that->assertInstanceOf('MongoDate', $data[$this->options['expiry_field']]);
|
$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()
|
public function testReplaceSessionData()
|
||||||
@ -191,8 +239,10 @@ class MongoDbSessionHandlerTest extends \PHPUnit_Framework_TestCase
|
|||||||
|
|
||||||
$data = array();
|
$data = array();
|
||||||
|
|
||||||
|
$methodName = phpversion('mongodb') ? 'updateOne' : 'update';
|
||||||
|
|
||||||
$collection->expects($this->exactly(2))
|
$collection->expects($this->exactly(2))
|
||||||
->method('update')
|
->method($methodName)
|
||||||
->will($this->returnCallback(function ($criteria, $updateData, $options) use (&$data) {
|
->will($this->returnCallback(function ($criteria, $updateData, $options) use (&$data) {
|
||||||
$data = $updateData;
|
$data = $updateData;
|
||||||
}));
|
}));
|
||||||
@ -200,7 +250,11 @@ class MongoDbSessionHandlerTest extends \PHPUnit_Framework_TestCase
|
|||||||
$this->storage->write('foo', 'bar');
|
$this->storage->write('foo', 'bar');
|
||||||
$this->storage->write('foo', 'foobar');
|
$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()
|
public function testDestroy()
|
||||||
@ -212,8 +266,10 @@ class MongoDbSessionHandlerTest extends \PHPUnit_Framework_TestCase
|
|||||||
->with($this->options['database'], $this->options['collection'])
|
->with($this->options['database'], $this->options['collection'])
|
||||||
->will($this->returnValue($collection));
|
->will($this->returnValue($collection));
|
||||||
|
|
||||||
|
$methodName = phpversion('mongodb') ? 'deleteOne' : 'remove';
|
||||||
|
|
||||||
$collection->expects($this->once())
|
$collection->expects($this->once())
|
||||||
->method('remove')
|
->method($methodName)
|
||||||
->with(array($this->options['id_field'] => 'foo'));
|
->with(array($this->options['id_field'] => 'foo'));
|
||||||
|
|
||||||
$this->assertTrue($this->storage->destroy('foo'));
|
$this->assertTrue($this->storage->destroy('foo'));
|
||||||
@ -230,19 +286,45 @@ class MongoDbSessionHandlerTest extends \PHPUnit_Framework_TestCase
|
|||||||
|
|
||||||
$that = $this;
|
$that = $this;
|
||||||
|
|
||||||
|
$methodName = phpversion('mongodb') ? 'deleteOne' : 'remove';
|
||||||
|
|
||||||
$collection->expects($this->once())
|
$collection->expects($this->once())
|
||||||
->method('remove')
|
->method($methodName)
|
||||||
->will($this->returnCallback(function ($criteria) use ($that) {
|
->will($this->returnCallback(function ($criteria) use ($that) {
|
||||||
$that->assertInstanceOf('MongoDate', $criteria[$that->options['expiry_field']]['$lt']);
|
if (phpversion('mongodb')) {
|
||||||
$that->assertGreaterThanOrEqual(time() - 1, $criteria[$that->options['expiry_field']]['$lt']->sec);
|
$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));
|
$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()
|
private function createMongoCollectionMock()
|
||||||
{
|
{
|
||||||
$collection = $this->getMockBuilder('MongoCollection')
|
$collectionClass = 'MongoCollection';
|
||||||
|
if (phpversion('mongodb')) {
|
||||||
|
$collectionClass = 'MongoDB\Collection';
|
||||||
|
}
|
||||||
|
|
||||||
|
$collection = $this->getMockBuilder($collectionClass)
|
||||||
->disableOriginalConstructor()
|
->disableOriginalConstructor()
|
||||||
->getMock();
|
->getMock();
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user