Passed
Push — master ( 90a365...15fd0e )
by Terry
09:12
created

Mongo::connect()   B

Complexity

Conditions 7
Paths 43

Size

Total Lines 43
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 7

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 7
eloc 21
c 1
b 0
f 0
nc 43
nop 1
dl 0
loc 43
ccs 14
cts 14
cp 1
crap 7
rs 8.6506
1
<?php
2
/*
3
 * This file is part of the Shieldon Simple Cache package.
4
 *
5
 * (c) Terry L. <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
declare(strict_types=1);
12
13
namespace Shieldon\SimpleCache\Driver;
14
15
use Shieldon\SimpleCache\CacheProvider;
16
use Shieldon\SimpleCache\Exception\CacheException;
17
use MongoDB\Driver\BulkWrite as MongoWrite;
18
use MongoDB\Driver\Exception\BulkWriteException;
19
use MongoDB\Driver\Manager as MongoServer;
20
use MongoDB\Driver\Query as MongoQuery;
21
use MongoDB\Driver\WriteConcern;
22
use Exception;
23
use function array_keys;
24
use function extension_loaded;
25
use function unserialize;
26
use function serialize;
27
28
/**
29
 * A cache driver class used for MongoDB.
30
 */
31
class Mongo extends CacheProvider
32
{
33
    /**
34
     * The MongoDB Manager instance.
35
     *
36
     * @var \MongoDB\Driver\Manager|null
37
     */
38
    protected $mongo = null;
39
40
    /**
41
     * Use the default "test" dbname, if not specify any.
42
     *
43
     * @var string
44
     */
45
    protected $dbname = 'test';
46
47
    /**
48
     * Collection name.
49
     *
50
     * @var string
51
     */
52
    protected $collection = 'cache_data';
53
54
    /**
55
     * The write concern.
56
     *
57
     * @var \MongoDB\Driver\WriteConcern|null
58
     * @see https://www.php.net/manual/en/class.mongodb-driver-writeconcern.php
59
     */
60
    protected $concern;
61
62
    /**
63
     * Constructor.
64
     *
65
     * @param array $setting The settings.
66
     * 
67
     * @throws CacheException
68
     */
69 14
    public function __construct(array $setting = [])
70
    {
71
        $config = [
72 14
            'host'       => '127.0.0.1',
73
            'port'       => 27017,
74
            'user'       => null,
75
            'pass'       => null,
76
            'dbname'     => 'test',
77
            'collection' => 'cache_data',
78
79
            // If the UNIX socket is set, host, port will be ignored.
80
            'unix_socket' => '',
81
        ];
82
83 14
        foreach (array_keys($config) as $key) {
84 14
            if (isset($setting[$key])) {
85 14
                $config[$key] = $setting[$key];
86
            }
87
        }
88
89 14
        $this->connect($config);
90
91 14
        $this->dbname     = $config['dbname'];
92 14
        $this->collection = $config['collection'];
93 14
    }
94
95
    /**
96
     * Connect to MongoDB server.
97
     *
98
     * @param array $config The settings.
99
     * 
100
     * @return void
101
     * 
102
     * @throws CacheException
103
     */
104 14
    protected function connect(array $config): void
105
    {
106 14
        if (extension_loaded('mongodb')) {
107
            try {
108
109 14
                $auth = '';
110 14
                $dababase = '';
111
112
                if (
113 14
                    !empty($config['user']) && 
114 14
                    !empty($config['pass'])
115
                ) {
116 2
                    $auth = $config['user'] . ':' . $config['pass'] . '@';
117
                }
118
119 14
                if (!empty($config['dbname'])) {
120 14
                    $dababase = '/' . $config['dbname'];
121
                }
122
123 14
                if (!empty($config['unix_socket'])) {
124
                    // @codeCoverageIgnoreStart
125
                    $command = 'mongodb://' . $auth . rawurlencode($config['unix_socket']) . $dababase;
126
                    // @codeCoverageIgnoreEnd
127
                } else {
128
                    // Basic => mongodb://127.0.0.1:27017
129
                    // mongodb://user:[email protected]:27017/dbname
130 14
                    $command = 'mongodb://' . $auth . $config['host'] . ':' . $config['port'] . $dababase;
131
                }
132
133 14
                $this->mongo = new MongoServer($command);
134 14
                $this->concern = new WriteConcern(WriteConcern::MAJORITY, 1000);
135
136
            // @codeCoverageIgnoreStart
137
            } catch (Exception $e) {
138
                throw new CacheException($e->getMessage());
139
            }
140
            // @codeCoverageIgnoreEnd
141 14
            return;
142
        }
143
144
        // @codeCoverageIgnoreStart
145
        throw new CacheException(
146
            'PHP MongoDB extension is not installed on your system.'
147
        );
148
        // @codeCoverageIgnoreEnd
149
    }
150
151
    /**
152
     * Fetch a cache by an extended Cache Driver.
153
     *
154
     * @param string $key The key of a cache.
155
     *
156
     * @return array
157
     */
158 8
    protected function doGet(string $key): array
159
    {
160
        $filter = [
161 8
            '_id' => $this->getKeyName($key),
162
        ];
163
164 8
        $option = [];
165
166 8
        $query = new MongoQuery($filter, $option);
167 8
        $cursor = $this->mongo->executeQuery($this->getCollectionName(), $query);
0 ignored issues
show
Bug introduced by
The method executeQuery() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

167
        /** @scrutinizer ignore-call */ 
168
        $cursor = $this->mongo->executeQuery($this->getCollectionName(), $query);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
168
169 8
        $data = [];
170 8
        foreach($cursor as $document) {
171 8
            $data[] = unserialize($document->content);
172
        }
173
174 8
        if (empty($data)) {
175 6
            return [];
176
        }
177
178 8
        return $data[0];
179
    }
180
181
    /**
182
     * Set a cache by an extended Cache Driver.
183
     *
184
     * @param string $key       The key of a cache.
185
     * @param mixed  $value     The value of a cache. (serialized)
186
     * @param int    $ttl       The time to live for a cache.
187
     * @param int    $timestamp The time to store a cache.
188
     *
189
     * @return bool
190
     */
191 12
    protected function doSet(string $key, $value, int $ttl, int $timestamp): bool
192
    {
193
        $contents = [
194 12
            'timestamp' => $timestamp,
195 12
            'ttl'       => $ttl,
196 12
            'value'     => $value,
197
        ];
198
199
        $filter = [
200 12
            '_id'=> $this->getKeyName($key),
201
        ];
202
203
        $data = [
204 12
            'content'  => serialize($contents),
205 12
            'sc_cache' => 1,
206
        ];
207
208
        $option = [
209 12
            'multi'  => false, 
210
            'upsert' => true,
211
        ];
212
213 12
        $bulk = new MongoWrite();
214 12
        $bulk->update($filter, $data, $option);
215
216 12
        return $this->doWriteOperation($bulk);
217
    }
218
219
    /**
220
     * Delete a cache by an extended Cache Driver.
221
     *
222
     * @param string $key The key of a cache.
223
     * 
224
     * @return bool
225
     */
226 6
    protected function doDelete(string $key): bool
227
    {
228 6
        $bulk = new MongoWrite();
229
230 6
        $bulk->delete([
231 6
            '_id' => $this->getKeyName($key),
232
        ]);
233
234 6
        return $this->doWriteOperation($bulk);
235
    }
236
237
    /**
238
     * Delete all caches by an extended Cache Driver.
239
     * 
240
     * @return bool
241
     */
242 6
    protected function doClear(): bool
243
    {
244 6
        $bulk = new MongoWrite();
245
246 6
        $bulk->delete([
247 6
            'sc_cache' => 1,
248
        ]);
249
250 6
        return $this->doWriteOperation($bulk);
251
    }
252
253
    /**
254
     * Check if the cache exists or not.
255
     *
256
     * @param string $key The key of a cache.
257
     *
258
     * @return bool
259
     */
260 4
    protected function doHas(string $key): bool
261
    {
262
        $filter = [
263 4
            '_id' => $this->getKeyName($key),
264
        ];
265
266 4
        $option = [];
267
268 4
        $query = new MongoQuery($filter, $option);
269 4
        $cursor = $this->mongo->executeQuery($this->getCollectionName(), $query);
270 4
        $results = $cursor->toArray();
271
272 4
        if (empty($results)) {
273 4
            return false;
274
        }
275
276 4
        return true;
277
    }
278
279
    /**
280
     * Fetch all cache items.
281
     *
282
     * @return array
283
     */
284 4
    protected function getAll(): array
285
    {
286 4
        $list = [];
287
288 4
        $query = new MongoQuery([]);
289 4
        $cursor = $this->mongo->executeQuery($this->getCollectionName(), $query);
290
291 4
        foreach ($cursor as $document) {
292 4
            $key = str_replace('sc_', '', $document->_id);
293 4
            $value = unserialize($document->content);
294
295 4
            $list[$key] = $value;
296
        }
297 4
        return $list;
298
    }
299
300
    /**
301
     * Perform the write operation and return the result.
302
     * 
303
     * @param object $bulk The \MongoDB\Driver\BulkWrite instance.
304
     *
305
     * @return bool
306
     */
307 12
    private function doWriteOperation(MongoWrite $bulk): bool
308
    {
309
        try {
310 12
            $this->mongo->executeBulkWrite(
311 12
                $this->getCollectionName(),
312 12
                $bulk,
313 12
                $this->concern
314
            );
315
        // @codeCoverageIgnoreStart
316
        } catch (BulkWriteException $e) {
317
            return false;
318
        }
319
        // @codeCoverageIgnoreEnd
320
321 10
        return true;
322
    }
323
324
    /**
325
     * Get the key name of a cache.
326
     *
327
     * @param string $key The key of a cache.
328
     *
329
     * @return string
330
     */
331 12
    private function getKeyName(string $key): string
332
    {
333 12
        return 'sc_' . $key;
334
    }
335
336
    /**
337
     * Get the collection name.
338
     *
339
     * @return string
340
     */
341 12
    private function getCollectionName(): string
342
    {
343 12
        return $this->dbname . '.' . $this->collection; 
344
    }
345
}
346