3 declare(strict_types=1);
6 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
7 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
8 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
9 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
10 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
11 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
12 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
13 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
14 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
15 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
16 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
18 * This software consists of voluntary contributions made by many individuals
19 * and is licensed under the MIT license. For more information, see
20 * <http://www.doctrine-project.org>.
23 namespace Doctrine\Common\Cache;
25 use MongoDB\BSON\Binary;
26 use MongoDB\BSON\UTCDateTime;
27 use MongoDB\Collection;
29 use MongoDB\Driver\Exception\Exception;
30 use MongoDB\Model\BSONDocument;
33 * MongoDB cache provider for ext-mongodb
35 * @internal Do not use - will be removed in 2.0. Use MongoDBCache instead
37 class ExtMongoDBCache extends CacheProvider
52 private $expirationIndexCreated = false;
57 * This provider will default to the write concern and read preference
58 * options set on the Database instance (or inherited from MongoDB or
59 * Client). Using an unacknowledged write concern (< 1) may make the return
60 * values of delete() and save() unreliable. Reading from secondaries may
61 * make contain() and fetch() unreliable.
63 * @see http://www.php.net/manual/en/mongo.readpreferences.php
64 * @see http://www.php.net/manual/en/mongo.writeconcerns.php
65 * @param Collection $collection
67 public function __construct(Collection $collection)
69 // Ensure there is no typemap set - we want to use our own
70 $this->collection = $collection->withOptions(['typeMap' => null]);
71 $this->database = new Database($collection->getManager(), $collection->getDatabaseName());
77 protected function doFetch($id)
79 $document = $this->collection->findOne(['_id' => $id], [MongoDBCache::DATA_FIELD, MongoDBCache::EXPIRATION_FIELD]);
81 if ($document === null) {
85 if ($this->isExpired($document)) {
86 $this->createExpirationIndex();
91 return unserialize($document[MongoDBCache::DATA_FIELD]->getData());
97 protected function doContains($id)
99 $document = $this->collection->findOne(['_id' => $id], [MongoDBCache::EXPIRATION_FIELD]);
101 if ($document === null) {
105 if ($this->isExpired($document)) {
106 $this->createExpirationIndex();
107 $this->doDelete($id);
117 protected function doSave($id, $data, $lifeTime = 0)
120 $this->collection->updateOne(
123 MongoDBCache::EXPIRATION_FIELD => ($lifeTime > 0 ? new UTCDateTime((time() + $lifeTime) * 1000): null),
124 MongoDBCache::DATA_FIELD => new Binary(serialize($data), Binary::TYPE_GENERIC),
128 } catch (Exception $e) {
138 protected function doDelete($id)
141 $this->collection->deleteOne(['_id' => $id]);
142 } catch (Exception $e) {
152 protected function doFlush()
155 // Use remove() in lieu of drop() to maintain any collection indexes
156 $this->collection->deleteMany([]);
157 } catch (Exception $e) {
167 protected function doGetStats()
173 $serverStatus = $this->database->command([
180 $uptime = $serverStatus['uptime'] ?? null;
181 } catch (Exception $e) {
185 $collStats = $this->database->command(['collStats' => $this->collection->getCollectionName()])->toArray()[0];
186 $memoryUsage = $collStats['size'] ?? null;
187 } catch (Exception $e) {
191 Cache::STATS_HITS => null,
192 Cache::STATS_MISSES => null,
193 Cache::STATS_UPTIME => $uptime,
194 Cache::STATS_MEMORY_USAGE => $memoryUsage,
195 Cache::STATS_MEMORY_AVAILABLE => null,
200 * Check if the document is expired.
202 * @param BSONDocument $document
206 private function isExpired(BSONDocument $document): bool
208 return isset($document[MongoDBCache::EXPIRATION_FIELD]) &&
209 $document[MongoDBCache::EXPIRATION_FIELD] instanceof UTCDateTime &&
210 $document[MongoDBCache::EXPIRATION_FIELD]->toDateTime() < new \DateTime();
213 private function createExpirationIndex(): void
215 if ($this->expirationIndexCreated) {
219 $this->collection->createIndex([MongoDBCache::EXPIRATION_FIELD => 1], ['background' => true, 'expireAfterSeconds' => 0]);