-
Notifications
You must be signed in to change notification settings - Fork 62
use mongodb extension instead of mongo extension #34
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,17 +10,24 @@ | |
|
||
namespace Zend\Session\SaveHandler; | ||
|
||
use Mongo; | ||
use MongoClient; | ||
use MongoCollection; | ||
use MongoDate; | ||
use MongoDB\BSON\Binary; | ||
use MongoDB\BSON\UTCDatetime; | ||
use MongoDB\Client as MongoClient; | ||
use MongoDB\Collection as MongoCollection; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's only available in mongo php library. We don't need this. We can do everything with the raw extension here, imho. |
||
use Zend\Session\Exception\InvalidArgumentException; | ||
|
||
/** | ||
* MongoDB session save handler | ||
*/ | ||
class MongoDB implements SaveHandlerInterface | ||
{ | ||
/** | ||
* MongoClient instance | ||
* | ||
* @var MongoClient | ||
*/ | ||
protected $mongoClient; | ||
|
||
/** | ||
* MongoCollection instance | ||
* | ||
|
@@ -51,21 +58,12 @@ class MongoDB implements SaveHandlerInterface | |
/** | ||
* Constructor | ||
* | ||
* @param Mongo|MongoClient $mongo | ||
* @param MongoClient $mongoClient | ||
* @param MongoDBOptions $options | ||
* @throws InvalidArgumentException | ||
*/ | ||
public function __construct($mongo, MongoDBOptions $options) | ||
public function __construct($mongoClient, MongoDBOptions $options) | ||
{ | ||
if (!($mongo instanceof MongoClient || $mongo instanceof Mongo)) { | ||
throw new InvalidArgumentException( | ||
sprintf( | ||
'Parameter of type %s is invalid; must be MongoClient or Mongo', | ||
(is_object($mongo) ? get_class($mongo) : gettype($mongo)) | ||
) | ||
); | ||
} | ||
|
||
if (null === ($database = $options->getDatabase())) { | ||
throw new InvalidArgumentException('The database option cannot be empty'); | ||
} | ||
|
@@ -74,7 +72,7 @@ public function __construct($mongo, MongoDBOptions $options) | |
throw new InvalidArgumentException('The collection option cannot be empty'); | ||
} | ||
|
||
$this->mongoCollection = $mongo->selectCollection($database, $collection); | ||
$this->mongoClient = $mongoClient; | ||
$this->options = $options; | ||
} | ||
|
||
|
@@ -91,6 +89,16 @@ public function open($savePath, $name) | |
$this->sessionName = $name; | ||
$this->lifetime = ini_get('session.gc_maxlifetime'); | ||
|
||
$this->mongoCollection = $this->mongoClient->selectCollection( | ||
$this->options->getDatabase(), | ||
$this->options->getCollection() | ||
); | ||
|
||
$this->mongoCollection->createIndex( | ||
[$this->options->getModifiedField() => 1], | ||
$this->options->useExpireAfterSecondsIndex() ? ['expireAfterSeconds' => $this->lifetime] : [] | ||
); | ||
|
||
return true; | ||
} | ||
|
||
|
@@ -118,12 +126,18 @@ public function read($id) | |
]); | ||
|
||
if (null !== $session) { | ||
if ($session[$this->options->getModifiedField()] instanceof MongoDate && | ||
$session[$this->options->getModifiedField()]->sec + | ||
$session[$this->options->getLifetimeField()] > time()) { | ||
return $session[$this->options->getDataField()]; | ||
// check if session has expired if index is not used | ||
if (!$this->options->useExpireAfterSecondsIndex()) { | ||
$timestamp = $session[$this->options->getLifetimeField()]; | ||
$timestamp += floor(((string)$session[$this->options->getModifiedField()]) / 1000); | ||
|
||
// session expired | ||
if ($timestamp <= time()) { | ||
$this->destroy($id); | ||
return ''; | ||
} | ||
} | ||
$this->destroy($id); | ||
return $session[$this->options->getDataField()]->getData(); | ||
} | ||
|
||
return ''; | ||
|
@@ -148,21 +162,23 @@ public function write($id, $data) | |
$this->options->getNameField() => $this->sessionName, | ||
]; | ||
|
||
$newObj = ['$set' => [ | ||
$this->options->getDataField() => (string) $data, | ||
$this->options->getLifetimeField() => $this->lifetime, | ||
$this->options->getModifiedField() => new MongoDate(), | ||
]]; | ||
$newObj = [ | ||
'$set' => [ | ||
$this->options->getDataField() => new Binary((string)$data, Binary::TYPE_GENERIC), | ||
$this->options->getLifetimeField() => $this->lifetime, | ||
$this->options->getModifiedField() => new UTCDatetime(floor(microtime(true) * 1000)), | ||
], | ||
]; | ||
|
||
/* Note: a MongoCursorException will be thrown if a record with this ID | ||
* already exists with a different session name, since the upsert query | ||
* cannot insert a new document with the same ID and new session name. | ||
* This should only happen if ID's are not unique or if the session name | ||
* is altered mid-process. | ||
*/ | ||
$result = $this->mongoCollection->update($criteria, $newObj, $saveOptions); | ||
$result = $this->mongoCollection->updateOne($criteria, $newObj, $saveOptions); | ||
|
||
return (bool) (isset($result['ok']) ? $result['ok'] : $result); | ||
return $result->isAcknowledged(); | ||
} | ||
|
||
/** | ||
|
@@ -173,12 +189,15 @@ public function write($id, $data) | |
*/ | ||
public function destroy($id) | ||
{ | ||
$result = $this->mongoCollection->remove([ | ||
'_id' => $id, | ||
$this->options->getNameField() => $this->sessionName, | ||
], $this->options->getSaveOptions()); | ||
$result = $this->mongoCollection->deleteOne( | ||
[ | ||
'_id' => $id, | ||
$this->options->getNameField() => $this->sessionName, | ||
], | ||
$this->options->getSaveOptions() | ||
); | ||
|
||
return (bool) (isset($result['ok']) ? $result['ok'] : $result); | ||
return $result->isAcknowledged(); | ||
} | ||
|
||
/** | ||
|
@@ -200,10 +219,15 @@ public function gc($maxlifetime) | |
* each document. Doing so would require a $where query to work with the | ||
* computed value (modified + lifetime) and be very inefficient. | ||
*/ | ||
$result = $this->mongoCollection->remove([ | ||
$this->options->getModifiedField() => ['$lt' => new MongoDate(time() - $maxlifetime)], | ||
], $this->options->getSaveOptions()); | ||
$microseconds = floor(microtime(true) * 1000) - $maxlifetime; | ||
|
||
$result = $this->mongoCollection->deleteMany( | ||
[ | ||
$this->options->getModifiedField() => ['$lt' => new UTCDateTime($microseconds)], | ||
], | ||
$this->options->getSaveOptions() | ||
); | ||
|
||
return (bool) (isset($result['ok']) ? $result['ok'] : $result); | ||
return $result->isAcknowledged(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -68,6 +68,12 @@ class MongoDBOptions extends AbstractOptions | |
*/ | ||
protected $modifiedField = 'modified'; | ||
|
||
/** | ||
* Use expireAfterSeconds index | ||
* | ||
* @var bool | ||
*/ | ||
protected $useExpireAfterSecondsIndex = false; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems you are adding a feature. Please do it in a different PR There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, as mentioned above. I will do that. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd argue differently. This PR:
As such, it's already targeted at a new minor version. Pushing the feature into a later PR creates more work for @sandrokeil, when we can easily evaluate it in this same pull request. |
||
|
||
/** | ||
* {@inheritdoc} | ||
|
@@ -286,4 +292,23 @@ public function getModifiedField() | |
{ | ||
return $this->modifiedField; | ||
} | ||
|
||
/** | ||
* @return boolean | ||
*/ | ||
public function useExpireAfterSecondsIndex() | ||
{ | ||
return $this->useExpireAfterSecondsIndex; | ||
} | ||
|
||
/** | ||
* Enable expireAfterSeconds index. | ||
* | ||
* @see http://docs.mongodb.org/manual/tutorial/expire-data/ | ||
* @param boolean $useExpireAfterSecondsIndex | ||
*/ | ||
public function setUseExpireAfterSecondsIndex($useExpireAfterSecondsIndex) | ||
{ | ||
$this->useExpireAfterSecondsIndex = (bool) $useExpireAfterSecondsIndex; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's only available in mongo php library. We don't need this. We can do everything with the raw extension here, imho.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can, but it requires far more code and code complexity. The Mongo PHP Library exists to provide the convenience layer that was previously in the extension, but in a way that can be more quickly and easily updated as new features are added and/or bugfixes released. Considering most developers will now be using the extension / PHP library combination if using MongoDB at all on PHP, this is not an unreasonable requirement.