From 8c3e4c63014002fc65478f82c4777e9505c17018 Mon Sep 17 00:00:00 2001 From: Ben Oman Date: Sat, 25 Jun 2016 17:51:43 -0600 Subject: [PATCH 1/5] Fix for #19183 to add support for new PHP MongoDB extension in sessions. --- .../Storage/Handler/MongoDbSessionHandler.php | 88 ++++++++++++++----- 1 file changed, 67 insertions(+), 21 deletions(-) diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MongoDbSessionHandler.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MongoDbSessionHandler.php index f1df25d0a629b..e228da5244c8c 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MongoDbSessionHandler.php +++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MongoDbSessionHandler.php @@ -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 @@ public function close() */ 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 @@ public function destroy($sessionId) */ 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,19 +136,33 @@ public function gc($maxlifetime) */ public function write($sessionId, $data) { - $expiry = new \MongoDate(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['expiry_field'] => $expiry, - ); - - $this->getCollection()->update( - array($this->options['id_field'] => $sessionId), - array('$set' => $fields), - array('upsert' => true, 'multiple' => false) - ); + $expiry = $this->createDateTime(time() + (int) ini_get('session.gc_maxlifetime')); + + if ($this->mongo instanceof \MongoDB\Client) { + $fields = array( + $this->options['data_field'] => new \MongoDB\BSON\Binary($data, \MongoDB\BSON\Binary::TYPE_OLD_BINARY), + $this->options['time_field'] => $this->createDateTime(), + $this->options['expiry_field'] => $expiry, + ); + + $this->getCollection()->updateOne( + array($this->options['id_field'] => $sessionId), + array('$set' => $fields), + array('upsert' => true) + ); + } else { + $fields = array( + $this->options['data_field'] => new \MongoBinData($data, \MongoBinData::BYTE_ARRAY), + $this->options['time_field'] => $this->createDateTime(), + $this->options['expiry_field'] => $expiry, + ); + + $this->getCollection()->update( + array($this->options['id_field'] => $sessionId), + array('$set' => $fields), + array('upsert' => true, 'multiple' => false) + ); + } return true; } @@ -156,10 +174,18 @@ public function read($sessionId) { $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 +211,24 @@ protected function getMongo() { 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); + } } From 2cf192893ce32784189c737b713c0446e984bc1e Mon Sep 17 00:00:00 2001 From: Ben Oman Date: Sun, 26 Jun 2016 15:22:15 -0600 Subject: [PATCH 2/5] Improved fields array declaration to use less code. Modified unit tests to utilize new mongodb extension's class names if mongodb extension exists. --- .../Storage/Handler/MongoDbSessionHandler.php | 17 +-- .../Handler/MongoDbSessionHandlerTest.php | 123 ++++++++++++++---- 2 files changed, 103 insertions(+), 37 deletions(-) diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MongoDbSessionHandler.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MongoDbSessionHandler.php index e228da5244c8c..5992fa837e16c 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MongoDbSessionHandler.php +++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MongoDbSessionHandler.php @@ -138,12 +138,13 @@ public function write($sessionId, $data) { $expiry = $this->createDateTime(time() + (int) ini_get('session.gc_maxlifetime')); + $fields = array( + $this->options['time_field'] => $this->createDateTime(), + $this->options['expiry_field'] => $expiry, + ); + if ($this->mongo instanceof \MongoDB\Client) { - $fields = array( - $this->options['data_field'] => new \MongoDB\BSON\Binary($data, \MongoDB\BSON\Binary::TYPE_OLD_BINARY), - $this->options['time_field'] => $this->createDateTime(), - $this->options['expiry_field'] => $expiry, - ); + $fields[$this->options['data_field']] = new \MongoDB\BSON\Binary($data, \MongoDB\BSON\Binary::TYPE_OLD_BINARY); $this->getCollection()->updateOne( array($this->options['id_field'] => $sessionId), @@ -151,11 +152,7 @@ public function write($sessionId, $data) array('upsert' => true) ); } else { - $fields = array( - $this->options['data_field'] => new \MongoBinData($data, \MongoBinData::BYTE_ARRAY), - $this->options['time_field'] => $this->createDateTime(), - $this->options['expiry_field'] => $expiry, - ); + $fields[$this->options['data_field']] = new \MongoBinData($data, \MongoBinData::BYTE_ARRAY); $this->getCollection()->update( array($this->options['id_field'] => $sessionId), 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 3fef78b8ee85d..4d9e238374bdc 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php @@ -15,7 +15,7 @@ /** * @author Markus Bachmann - * @requires extension mongo + * @requires extension mongo or mongodb * @group time-sensitive */ class MongoDbSessionHandlerTest extends \PHPUnit_Framework_TestCase @@ -31,7 +31,11 @@ protected function setUp() { parent::setUp(); - $mongoClass = version_compare(phpversion('mongo'), '1.3.0', '<') ? 'Mongo' : 'MongoClient'; + if (phpversion('mongodb')) { + $mongoClass = 'MongoDB\Client'; + } else { + $mongoClass = version_compare(phpversion('mongo'), '1.3.0', '<') ? 'Mongo' : 'MongoClient'; + } $this->mongo = $this->getMockBuilder($mongoClass) ->disableOriginalConstructor() @@ -96,14 +100,28 @@ public function testRead() $this->assertArrayHasKey($this->options['expiry_field'], $criteria); $this->assertArrayHasKey('$gte', $criteria[$this->options['expiry_field']]); - $this->assertInstanceOf('MongoDate', $criteria[$this->options['expiry_field']]['$gte']); - $this->assertGreaterThanOrEqual($criteria[$this->options['expiry_field']]['$gte']->sec, $testTimeout); - return array( - $this->options['id_field'] => 'foo', - $this->options['data_field'] => new \MongoBinData('bar', \MongoBinData::BYTE_ARRAY), - $this->options['id_field'] => new \MongoDate(), + if (phpversion('mongodb')) { + $this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $criteria[$this->options['expiry_field']]['$gte']); + $this->assertGreaterThanOrEqual(round(intval((string)$criteria[$this->options['expiry_field']]['$gte']) / 1000), $testTimeout); + } else { + $this->assertInstanceOf('MongoDate', $criteria[$this->options['expiry_field']]['$gte']); + $this->assertGreaterThanOrEqual($criteria[$this->options['expiry_field']]['$gte']->sec, $testTimeout); + } + + $fields = array( + $this->options['id_field'] => 'foo' ); + + if (phpversion('mongodb')) { + $fields[$this->options['data_field']] = new \MongoDB\BSON\Binary('bar', \MongoDB\BSON\Binary::TYPE_OLD_BINARY); + $fields[$this->options['id_field']] = new \MongoDB\BSON\UTCDateTime(time() * 1000); + } else { + $fields[$this->options['data_field']] = new \MongoBinData('bar', \MongoBinData::BYTE_ARRAY); + $fields[$this->options['id_field']] = new \MongoDate(); + } + + return $fields; })); $this->assertEquals('bar', $this->storage->read('foo')); @@ -120,11 +138,18 @@ public function testWrite() $data = array(); + $methodName = phpversion('mongodb') ? 'updateOne' : 'update'; + $collection->expects($this->once()) - ->method('update') + ->method($methodName) ->will($this->returnCallback(function ($criteria, $updateData, $options) use (&$data) { $this->assertEquals(array($this->options['id_field'] => 'foo'), $criteria); - $this->assertEquals(array('upsert' => true, 'multiple' => false), $options); + + if (phpversion('mongodb')) { + $this->assertEquals(array('upsert' => true), $options); + } else { + $this->assertEquals(array('upsert' => true, 'multiple' => false), $options); + } $data = $updateData['$set']; })); @@ -132,10 +157,17 @@ public function testWrite() $expectedExpiry = time() + (int) ini_get('session.gc_maxlifetime'); $this->assertTrue($this->storage->write('foo', 'bar')); - $this->assertEquals('bar', $data[$this->options['data_field']]->bin); - $this->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')) { + $this->assertEquals('bar', $data[$this->options['data_field']]->getData()); + $this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$this->options['time_field']]); + $this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$this->options['expiry_field']]); + $this->assertGreaterThanOrEqual($expectedExpiry, round(intval((string)$data[$this->options['expiry_field']]) / 1000)); + } else { + $this->assertEquals('bar', $data[$this->options['data_field']]->bin); + $this->assertInstanceOf('MongoDate', $data[$this->options['time_field']]); + $this->assertInstanceOf('MongoDate', $data[$this->options['expiry_field']]); + $this->assertGreaterThanOrEqual($expectedExpiry, $data[$this->options['expiry_field']]->sec); + } } public function testWriteWhenUsingExpiresField() @@ -160,20 +192,33 @@ public function testWriteWhenUsingExpiresField() $data = array(); + $methodName = phpversion('mongodb') ? 'updateOne' : 'update'; + $collection->expects($this->once()) - ->method('update') + ->method($methodName) ->will($this->returnCallback(function ($criteria, $updateData, $options) use (&$data) { $this->assertEquals(array($this->options['id_field'] => 'foo'), $criteria); - $this->assertEquals(array('upsert' => true, 'multiple' => false), $options); + + if (phpversion('mongodb')) { + $this->assertEquals(array('upsert' => true), $options); + } else { + $this->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); - $this->assertInstanceOf('MongoDate', $data[$this->options['time_field']]); - $this->assertInstanceOf('MongoDate', $data[$this->options['expiry_field']]); + if (phpversion('mongodb')) { + $this->assertEquals('bar', $data[$this->options['data_field']]->getData()); + $this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$this->options['time_field']]); + $this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$this->options['expiry_field']]); + } else { + $this->assertEquals('bar', $data[$this->options['data_field']]->bin); + $this->assertInstanceOf('MongoDate', $data[$this->options['time_field']]); + $this->assertInstanceOf('MongoDate', $data[$this->options['expiry_field']]); + } } public function testReplaceSessionData() @@ -187,8 +232,10 @@ public function testReplaceSessionData() $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; })); @@ -196,7 +243,11 @@ public function testReplaceSessionData() $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() @@ -208,8 +259,10 @@ public function testDestroy() ->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')); @@ -224,11 +277,18 @@ public function testGc() ->with($this->options['database'], $this->options['collection']) ->will($this->returnValue($collection)); + $methodName = phpversion('mongodb') ? 'deleteOne' : 'remove'; + $collection->expects($this->once()) - ->method('remove') + ->method($methodName) ->will($this->returnCallback(function ($criteria) { - $this->assertInstanceOf('MongoDate', $criteria[$this->options['expiry_field']]['$lt']); - $this->assertGreaterThanOrEqual(time() - 1, $criteria[$this->options['expiry_field']]['$lt']->sec); + if (phpversion('mongodb')) { + $this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $criteria[$this->options['expiry_field']]['$lt']); + $this->assertGreaterThanOrEqual(time() - 1, round(intval((string)$criteria[$this->options['expiry_field']]['$lt']) / 1000)); + } else { + $this->assertInstanceOf('MongoDate', $criteria[$this->options['expiry_field']]['$lt']); + $this->assertGreaterThanOrEqual(time() - 1, $criteria[$this->options['expiry_field']]['$lt']->sec); + } })); $this->assertTrue($this->storage->gc(1)); @@ -239,14 +299,23 @@ public function testGetConnection() $method = new \ReflectionMethod($this->storage, 'getMongo'); $method->setAccessible(true); - $mongoClass = (version_compare(phpversion('mongo'), '1.3.0', '<')) ? '\Mongo' : '\MongoClient'; + 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(); From ea9ddd2b4fed587a97926ab48b7af0db52720a18 Mon Sep 17 00:00:00 2001 From: Ben Oman Date: Sun, 26 Jun 2016 15:26:08 -0600 Subject: [PATCH 3/5] style improvements --- .../Storage/Handler/MongoDbSessionHandlerTest.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) 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 4d9e238374bdc..b51a57fa601e4 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php @@ -103,14 +103,14 @@ public function testRead() if (phpversion('mongodb')) { $this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $criteria[$this->options['expiry_field']]['$gte']); - $this->assertGreaterThanOrEqual(round(intval((string)$criteria[$this->options['expiry_field']]['$gte']) / 1000), $testTimeout); + $this->assertGreaterThanOrEqual(round(intval((string) $criteria[$this->options['expiry_field']]['$gte']) / 1000), $testTimeout); } else { $this->assertInstanceOf('MongoDate', $criteria[$this->options['expiry_field']]['$gte']); $this->assertGreaterThanOrEqual($criteria[$this->options['expiry_field']]['$gte']->sec, $testTimeout); } $fields = array( - $this->options['id_field'] => 'foo' + $this->options['id_field'] => 'foo', ); if (phpversion('mongodb')) { @@ -161,7 +161,7 @@ public function testWrite() $this->assertEquals('bar', $data[$this->options['data_field']]->getData()); $this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$this->options['time_field']]); $this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$this->options['expiry_field']]); - $this->assertGreaterThanOrEqual($expectedExpiry, round(intval((string)$data[$this->options['expiry_field']]) / 1000)); + $this->assertGreaterThanOrEqual($expectedExpiry, round(intval((string) $data[$this->options['expiry_field']]) / 1000)); } else { $this->assertEquals('bar', $data[$this->options['data_field']]->bin); $this->assertInstanceOf('MongoDate', $data[$this->options['time_field']]); @@ -198,7 +198,7 @@ public function testWriteWhenUsingExpiresField() ->method($methodName) ->will($this->returnCallback(function ($criteria, $updateData, $options) use (&$data) { $this->assertEquals(array($this->options['id_field'] => 'foo'), $criteria); - + if (phpversion('mongodb')) { $this->assertEquals(array('upsert' => true), $options); } else { @@ -284,7 +284,7 @@ public function testGc() ->will($this->returnCallback(function ($criteria) { if (phpversion('mongodb')) { $this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $criteria[$this->options['expiry_field']]['$lt']); - $this->assertGreaterThanOrEqual(time() - 1, round(intval((string)$criteria[$this->options['expiry_field']]['$lt']) / 1000)); + $this->assertGreaterThanOrEqual(time() - 1, round(intval((string) $criteria[$this->options['expiry_field']]['$lt']) / 1000)); } else { $this->assertInstanceOf('MongoDate', $criteria[$this->options['expiry_field']]['$lt']); $this->assertGreaterThanOrEqual(time() - 1, $criteria[$this->options['expiry_field']]['$lt']->sec); From 76865e8530e7e3daabc40b97f80fb03374528983 Mon Sep 17 00:00:00 2001 From: Ben Oman Date: Sun, 26 Jun 2016 21:05:44 -0600 Subject: [PATCH 4/5] Implemented correct way to test whether either Mongo or MongoDB extension is loaded. --- .../Session/Storage/Handler/MongoDbSessionHandlerTest.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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 b51a57fa601e4..eabc51e79fe91 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php @@ -15,7 +15,6 @@ /** * @author Markus Bachmann - * @requires extension mongo or mongodb * @group time-sensitive */ class MongoDbSessionHandlerTest extends \PHPUnit_Framework_TestCase @@ -31,6 +30,10 @@ protected function setUp() { parent::setUp(); + if (!extension_loaded('mongo') && !extension_loaded('mongodb')) { + $this->markTestSkipped('The Mongo or MongoDB extension is required.'); + } + if (phpversion('mongodb')) { $mongoClass = 'MongoDB\Client'; } else { From a6bd3c74214eac4b98d5c22f66a571139fe3a422 Mon Sep 17 00:00:00 2001 From: Ben Oman Date: Tue, 28 Jun 2016 08:27:21 -0600 Subject: [PATCH 5/5] Refactored write method to consolidate what ends up inside conditional blocks. --- .../Storage/Handler/MongoDbSessionHandler.php | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MongoDbSessionHandler.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MongoDbSessionHandler.php index 5992fa837e16c..23bca61e6068b 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MongoDbSessionHandler.php +++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MongoDbSessionHandler.php @@ -143,24 +143,23 @@ public function write($sessionId, $data) $this->options['expiry_field'] => $expiry, ); + $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); - - $this->getCollection()->updateOne( - array($this->options['id_field'] => $sessionId), - array('$set' => $fields), - array('upsert' => true) - ); } else { $fields[$this->options['data_field']] = new \MongoBinData($data, \MongoBinData::BYTE_ARRAY); - - $this->getCollection()->update( - array($this->options['id_field'] => $sessionId), - array('$set' => $fields), - array('upsert' => true, 'multiple' => false) - ); + $options['multiple'] = false; } + $methodName = ($this->mongo instanceof \MongoDB\Client) ? 'updateOne' : 'update'; + + $this->getCollection()->$methodName( + array($this->options['id_field'] => $sessionId), + array('$set' => $fields), + $options + ); + return true; }