3 * @see https://github.com/zendframework/zend-cache for the canonical source repository
4 * @copyright Copyright (c) 2018 Zend Technologies USA Inc. (https://www.zend.com)
5 * @license https://github.com/zendframework/zend-cache/blob/master/LICENSE.md New BSD License
8 namespace Zend\Cache\Storage\Adapter
;
11 use MongoDB\Collection
;
12 use MongoDB\BSON\UTCDateTime
as MongoDate
;
13 use MongoDB\Driver\Exception\Exception
as MongoDriverException
;
15 use Zend\Cache\Exception
;
16 use Zend\Cache\Storage\Capabilities
;
17 use Zend\Cache\Storage\FlushableInterface
;
20 * Cache storage adapter for ext-mongodb
22 * If you are using ext-mongo, use the MongoDb adapter instead.
24 class ExtMongoDb
extends AbstractAdapter
implements FlushableInterface
27 * Has this instance be initialized
31 private $initialized = false;
34 * the mongodb resource manager
36 * @var null|ExtMongoDbResourceManager
38 private $resourceManager;
41 * The mongodb resource id
48 * The namespace prefix
52 private $namespacePrefix = '';
57 * @throws Exception\ExtensionNotLoadedException
59 public function __construct($options = null)
61 if (! extension_loaded('mongodb') ||
! class_exists(Client
::class)) {
62 throw new Exception\
ExtensionNotLoadedException(
63 'mongodb extension not loaded or Mongo PHP client library not installed'
67 parent
::__construct($options);
69 $initialized = & $this->initialized
;
71 $this->getEventManager()->attach(
73 function () use (& $initialized) {
80 * get mongodb resource
84 private function getMongoCollection()
86 if (! $this->initialized
) {
87 $options = $this->getOptions();
89 $this->resourceManager
= $options->getResourceManager();
90 $this->resourceId
= $options->getResourceId();
91 $namespace = $options->getNamespace();
92 $this->namespacePrefix
= ($namespace === '' ?
'' : $namespace . $options->getNamespaceSeparator());
93 $this->initialized
= true;
96 return $this->resourceManager
->getResource($this->resourceId
);
102 public function setOptions($options)
104 return parent
::setOptions(
105 $options instanceof ExtMongoDbOptions
107 : new ExtMongoDbOptions($options)
115 * @return ExtMongoDbOptions
117 public function getOptions()
119 return $this->options
;
125 * @throws Exception\RuntimeException
127 protected function internalGetItem(& $normalizedKey, & $success = null, & $casToken = null)
129 $result = $this->fetchFromCollection($normalizedKey);
132 if (null === $result) {
136 if (isset($result['expires'])) {
137 if (! $result['expires'] instanceof MongoDate
) {
138 throw new Exception\
RuntimeException(sprintf(
139 "The found item _id '%s' for key '%s' is not a valid cache item"
140 . ": the field 'expired' isn't an instance of MongoDate, '%s' found instead",
141 (string) $result['_id'],
142 $this->namespacePrefix
. $normalizedKey,
143 is_object($result['expires']) ?
get_class($result['expires']) : gettype($result['expires'])
147 if ($result['expires']->sec
< (new MongoDate())) {
148 $this->internalRemoveItem($normalizedKey);
153 if (! array_key_exists('value', $result)) {
154 throw new Exception\
RuntimeException(sprintf(
155 "The found item _id '%s' for key '%s' is not a valid cache item: missing the field 'value'",
156 (string) $result['_id'],
157 $this->namespacePrefix
. $normalizedKey
163 return $casToken = $result['value'];
169 * @throws Exception\RuntimeException
171 protected function internalSetItem(& $normalizedKey, & $value)
173 $mongo = $this->getMongoCollection();
174 $key = $this->namespacePrefix
. $normalizedKey;
175 $ttl = $this->getOptions()->getTTl();
183 $ttlSeconds = round((microtime(true) +
$ttl) * 1000);
184 $cacheItem['expires'] = new MongoDate($ttlSeconds);
188 $mongo->deleteOne(['key' => $key]);
189 $result = $mongo->insertOne($cacheItem);
190 } catch (MongoDriverException
$e) {
191 throw new Exception\
RuntimeException($e->getMessage(), $e->getCode(), $e);
194 return null !== $result && $result->isAcknowledged();
200 * @throws Exception\RuntimeException
202 protected function internalRemoveItem(& $normalizedKey)
205 $result = $this->getMongoCollection()->deleteOne(['key' => $this->namespacePrefix
. $normalizedKey]);
206 } catch (MongoDriverException
$e) {
207 throw new Exception\
RuntimeException($e->getMessage(), $e->getCode(), $e);
210 return null !== $result && $result->getDeletedCount() > 0;
216 public function flush()
218 $result = $this->getMongoCollection()->drop();
219 return ((float) 1) === $result['ok'];
225 protected function internalGetCapabilities()
227 if ($this->capabilities
) {
228 return $this->capabilities
;
231 return $this->capabilities
= new Capabilities(
233 $this->capabilityMarker
= new stdClass(),
235 'supportedDatatypes' => [
245 'supportedMetadata' => [
250 'maxKeyLength' => 255,
251 'namespaceIsPrefix' => true,
259 * @throws Exception\ExceptionInterface
261 protected function internalGetMetadata(& $normalizedKey)
263 $result = $this->fetchFromCollection($normalizedKey);
264 return null !== $result ?
['_id' => $result['_id']] : false;
268 * Return raw records from MongoCollection
270 * @param string $normalizedKey
274 * @throws Exception\RuntimeException
276 private function fetchFromCollection(& $normalizedKey)
279 return $this->getMongoCollection()->findOne(['key' => $this->namespacePrefix
. $normalizedKey]);
280 } catch (MongoDriverException
$e) {
281 throw new Exception\
RuntimeException($e->getMessage(), $e->getCode(), $e);