Passed
Push — master ( 5c1ad9...58f408 )
by Marco
05:40
created

ExtMongoDBCache::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 6
ccs 4
cts 4
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 1
crap 1
1
<?php
2
3
declare(strict_types=1);
4
5
/*
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.
17
 *
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>.
21
 */
22
23
namespace Doctrine\Common\Cache;
24
25
use MongoDB\BSON\Binary;
26
use MongoDB\BSON\UTCDateTime;
27
use MongoDB\Collection;
28
use MongoDB\Database;
29
use MongoDB\Driver\Exception\Exception;
30
use MongoDB\Model\BSONDocument;
31
32
/**
33
 * MongoDB cache provider for ext-mongodb
34
 *
35
 * @internal Do not use - will be removed in 2.0. Use MongoDBCache instead
36
 */
37
class ExtMongoDBCache extends CacheProvider
38
{
39
    /**
40
     * @var Database
41
     */
42
    private $database;
43
44
    /**
45
     * @var Collection
46
     */
47
    private $collection;
48
49
    /**
50
     * @var bool
51
     */
52
    private $expirationIndexCreated = false;
53
54
    /**
55
     * Constructor.
56
     *
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.
62
     *
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
66
     */
67 77
    public function __construct(Collection $collection)
68
    {
69
        // Ensure there is no typemap set - we want to use our own
70 77
        $this->collection = $collection->withOptions(['typeMap' => null]);
71 77
        $this->database = new Database($collection->getManager(), $collection->getDatabaseName());
72 77
    }
73
74
    /**
75
     * {@inheritdoc}
76
     */
77 75
    protected function doFetch($id)
78
    {
79 75
        $document = $this->collection->findOne(['_id' => $id], [MongoDBCache::DATA_FIELD, MongoDBCache::EXPIRATION_FIELD]);
80
81 75
        if ($document === null) {
82 75
            return false;
83
        }
84
85 64
        if ($this->isExpired($document)) {
0 ignored issues
show
Documentation introduced by
$document is of type array|object, but the function expects a object<MongoDB\Model\BSONDocument>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
86
            $this->createExpirationIndex();
87
            $this->doDelete($id);
88
            return false;
89
        }
90
91 64
        return unserialize($document[MongoDBCache::DATA_FIELD]->getData());
92
    }
93
94
    /**
95
     * {@inheritdoc}
96
     */
97 72
    protected function doContains($id)
98
    {
99 72
        $document = $this->collection->findOne(['_id' => $id], [MongoDBCache::EXPIRATION_FIELD]);
100
101 72
        if ($document === null) {
102 52
            return false;
103
        }
104
105 68
        if ($this->isExpired($document)) {
0 ignored issues
show
Documentation introduced by
$document is of type array|object, but the function expects a object<MongoDB\Model\BSONDocument>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
106 1
            $this->createExpirationIndex();
107 1
            $this->doDelete($id);
108 1
            return false;
109
        }
110
111 68
        return true;
112
    }
113
114
    /**
115
     * {@inheritdoc}
116
     */
117 73
    protected function doSave($id, $data, $lifeTime = 0)
118
    {
119
        try {
120 73
            $this->collection->updateOne(
121 73
                ['_id' => $id],
122 73
                ['$set' => [
123 73
                    MongoDBCache::EXPIRATION_FIELD => ($lifeTime > 0 ? new UTCDateTime((time() + $lifeTime) * 1000): null),
124 73
                    MongoDBCache::DATA_FIELD => new Binary(serialize($data), Binary::TYPE_GENERIC),
125
                ]],
126 73
                ['upsert' => true]
127
            );
128
        } catch (Exception $e) {
0 ignored issues
show
Bug introduced by
The class MongoDB\Driver\Exception\Exception does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
129
            return false;
130
        }
131
132 73
        return true;
133
    }
134
135
    /**
136
     * {@inheritdoc}
137
     */
138 46
    protected function doDelete($id)
139
    {
140
        try {
141 46
            $this->collection->deleteOne(['_id' => $id]);
142
        } catch (Exception $e) {
0 ignored issues
show
Bug introduced by
The class MongoDB\Driver\Exception\Exception does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
143
            return false;
144
        }
145
146 46
        return true;
147
    }
148
149
    /**
150
     * {@inheritdoc}
151
     */
152 2
    protected function doFlush()
153
    {
154
        try {
155
            // Use remove() in lieu of drop() to maintain any collection indexes
156 2
            $this->collection->deleteMany([]);
157
        } catch (Exception $e) {
0 ignored issues
show
Bug introduced by
The class MongoDB\Driver\Exception\Exception does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
158
            return false;
159
        }
160
161 2
        return true;
162
    }
163
164
    /**
165
     * {@inheritdoc}
166
     */
167 1
    protected function doGetStats()
168
    {
169 1
        $uptime = null;
170 1
        $memoryUsage = null;
171
172
        try {
173 1
            $serverStatus = $this->database->command([
174 1
                'serverStatus' => 1,
175
                'locks' => 0,
176
                'metrics' => 0,
177
                'recordStats' => 0,
178
                'repl' => 0,
179 1
            ])->toArray()[0];
180 1
            $uptime = $serverStatus['uptime'] ?? null;
181
        } catch (Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
Bug introduced by
The class MongoDB\Driver\Exception\Exception does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
182
        }
183
184
        try {
185 1
            $collStats = $this->database->command(['collStats' => $this->collection->getCollectionName()])->toArray()[0];
186
            $memoryUsage = $collStats['size'] ?? null;
187 1
        } catch (Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
Bug introduced by
The class MongoDB\Driver\Exception\Exception does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
188
        }
189
190
        return [
191 1
            Cache::STATS_HITS => null,
192 1
            Cache::STATS_MISSES => null,
193 1
            Cache::STATS_UPTIME => $uptime,
194 1
            Cache::STATS_MEMORY_USAGE => $memoryUsage,
195 1
            Cache::STATS_MEMORY_AVAILABLE  => null,
196
        ];
197
    }
198
199
    /**
200
     * Check if the document is expired.
201
     *
202
     * @param BSONDocument $document
203
     *
204
     * @return bool
205
     */
206 69
    private function isExpired(BSONDocument $document): bool
207
    {
208 69
        return isset($document[MongoDBCache::EXPIRATION_FIELD]) &&
209 2
            $document[MongoDBCache::EXPIRATION_FIELD] instanceof UTCDateTime &&
0 ignored issues
show
Bug introduced by
The class MongoDB\BSON\UTCDateTime does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
210 69
            $document[MongoDBCache::EXPIRATION_FIELD]->toDateTime() < new \DateTime();
211
    }
212
213 1
    private function createExpirationIndex(): void
214
    {
215 1
        if ($this->expirationIndexCreated) {
216
            return;
217
        }
218
219 1
        $this->collection->createIndex([MongoDBCache::EXPIRATION_FIELD => 1], ['background' => true, 'expireAfterSeconds' => 0]);
220 1
    }
221
}
222