Issues (15)

src/SimpleCache/Driver/Mongo.php (1 issue)

Labels
Severity
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
    protected $type = 'mongo';
34
35
    /**
36
     * The MongoDB Manager instance.
37
     *
38
     * @var \MongoDB\Driver\Manager|null
39
     */
40
    protected $mongo = null;
41
42
    /**
43
     * Use the default "test" dbname, if not specify any.
44
     *
45
     * @var string
46
     */
47
    protected $dbname = 'test';
48
49
    /**
50
     * Collection name.
51
     *
52
     * @var string
53
     */
54
    protected $collection = 'cache_data';
55
56
    /**
57
     * The write concern.
58
     *
59
     * @var \MongoDB\Driver\WriteConcern|null
60
     * @see https://www.php.net/manual/en/class.mongodb-driver-writeconcern.php
61
     */
62
    protected $concern;
63
64
    /**
65
     * Constructor.
66
     *
67
     * @param array $setting The settings.
68
     *
69
     * @throws CacheException
70
     */
71 14
    public function __construct(array $setting = [])
72
    {
73
        $config = [
74 14
            'host'       => '127.0.0.1',
75
            'port'       => 27017,
76
            'user'       => null,
77
            'pass'       => null,
78
            'dbname'     => 'test',
79
            'collection' => 'cache_data',
80
81
            // If the UNIX socket is set, host, port will be ignored.
82
            'unix_socket' => '',
83
        ];
84
85 14
        foreach (array_keys($config) as $key) {
86 14
            if (isset($setting[$key])) {
87 14
                $config[$key] = $setting[$key];
88
            }
89
        }
90
91 14
        $this->connect($config);
92
93 14
        $this->dbname     = $config['dbname'];
94 14
        $this->collection = $config['collection'];
95 14
    }
96
97
    /**
98
     * Connect to MongoDB server.
99
     *
100
     * @param array $config The settings.
101
     *
102
     * @return void
103
     *
104
     * @throws CacheException
105
     */
106 14
    protected function connect(array $config): void
107
    {
108 14
        if (extension_loaded('mongodb')) {
109
            try {
110 14
                $auth = '';
111 14
                $dababase = '';
112
113 14
                if (!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
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