Completed
Push — master ( d9e44f...357df8 )
by Georges
26s queued 13s
created

Driver::getStats()   A

Complexity

Conditions 5
Paths 1

Size

Total Lines 66
Code Lines 37

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 37
nc 1
nop 0
dl 0
loc 66
rs 9.0168
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 *
5
 * This file is part of phpFastCache.
6
 *
7
 * @license MIT License (MIT)
8
 *
9
 * For full copyright and license information, please see the docs/CREDITS.txt file.
10
 *
11
 * @author Khoa Bui (khoaofgod)  <[email protected]> https://www.phpfastcache.com
12
 * @author Georges.L (Geolim4)  <[email protected]>
13
 * @author Fabio Covolo Mazzo (fabiocmazzo) <[email protected]>
14
 *
15
 */
16
declare(strict_types=1);
17
18
namespace Phpfastcache\Drivers\Mongodb;
19
20
use DateTime;
21
use Exception;
22
use LogicException;
23
use MongoClient;
24
use MongoDB\{BSON\Binary, BSON\UTCDateTime, Client, Collection, Database, DeleteResult, Driver\Command, Driver\Exception\Exception as MongoDBException, Driver\Manager};
0 ignored issues
show
Bug introduced by
The type MongoDB\Client was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
Bug introduced by
The type MongoDB\Database was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
Bug introduced by
The type MongoDB\DeleteResult was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
Bug introduced by
The type MongoDB\Collection was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
25
use Phpfastcache\Cluster\AggregatablePoolInterface;
26
use Phpfastcache\Core\Pool\{DriverBaseTrait, ExtendedCacheItemPoolInterface};
27
use Phpfastcache\Entities\DriverStatistic;
28
use Phpfastcache\Exceptions\{PhpfastcacheDriverException, PhpfastcacheInvalidArgumentException};
29
use Psr\Cache\CacheItemInterface;
30
31
32
/**
33
 * Class Driver
34
 * @package phpFastCache\Drivers
35
 * @property Client $instance Instance of driver service
36
 * @property Config $config Config object
37
 * @method Config getConfig() Return the config object
38
 */
39
class Driver implements ExtendedCacheItemPoolInterface, AggregatablePoolInterface
40
{
41
    public const MONGODB_DEFAULT_DB_NAME = 'phpfastcache'; // Public because used in config
42
43
    use DriverBaseTrait;
44
45
    /**
46
     * @var Collection
47
     */
48
    public $collection;
49
50
    /**
51
     * @var Database
52
     */
53
    public $database;
54
55
    /**
56
     * @return bool
57
     */
58
    public function driverCheck(): bool
59
    {
60
        $mongoExtensionExists = class_exists(Manager::class);
61
62
        if (!$mongoExtensionExists && class_exists(MongoClient::class)) {
63
            trigger_error(
64
                'This driver is used to support the pecl MongoDb extension with mongo-php-library.
65
            For MongoDb with Mongo PECL support use Mongo Driver.',
66
                E_USER_ERROR
67
            );
68
        }
69
70
        return $mongoExtensionExists && class_exists(Collection::class);
71
    }
72
73
    /**
74
     * @return DriverStatistic
75
     */
76
    public function getStats(): DriverStatistic
77
    {
78
        $serverStats = $this->instance->getManager()->executeCommand(
79
            $this->getConfig()->getDatabaseName(),
80
            new Command(
81
                [
82
                    'serverStatus' => 1,
83
                    'recordStats' => 0,
84
                    'repl' => 0,
85
                    'metrics' => 0,
86
                ]
87
            )
88
        )->toArray()[0];
89
90
        $collectionStats = $this->instance->getManager()->executeCommand(
91
            $this->getConfig()->getDatabaseName(),
92
            new Command(
93
                [
94
                    'collStats' => $this->getConfig()->getCollectionName(),
95
                    'verbose' => true,
96
                ]
97
            )
98
        )->toArray()[0];
99
100
        $array_filter_recursive = static function ($array, callable $callback = null) use (&$array_filter_recursive) {
101
            $array = $callback($array);
102
103
            if (\is_object($array) || \is_array($array)) {
104
                foreach ($array as &$value) {
105
                    $value = $array_filter_recursive($value, $callback);
106
                }
107
            }
108
109
            return $array;
110
        };
111
112
        $callback = static function ($item) {
113
            /**
114
             * Remove unserializable properties
115
             */
116
            if ($item instanceof UTCDateTime) {
117
                return (string)$item;
118
            }
119
            return $item;
120
        };
121
122
        $serverStats = $array_filter_recursive($serverStats, $callback);
123
        $collectionStats = $array_filter_recursive($collectionStats, $callback);
124
125
        $stats = (new DriverStatistic())
126
            ->setInfo(
127
                'MongoDB version ' . $serverStats->version . ', Uptime (in days): ' . round(
128
                    $serverStats->uptime / 86400,
129
                    1
130
                ) . "\n For more information see RawData."
131
            )
132
            ->setSize($collectionStats->size)
133
            ->setData(implode(', ', array_keys($this->itemInstances)))
134
            ->setRawData(
135
                [
136
                    'serverStatus' => $serverStats,
137
                    'collStats' => $collectionStats,
138
                ]
139
            );
140
141
        return $stats;
142
    }
143
144
    /**
145
     * @param CacheItemInterface $item
146
     * @return null|array
147
     */
148
    protected function driverRead(CacheItemInterface $item)
149
    {
150
        $document = $this->getCollection()->findOne(['_id' => $this->getMongoDbItemKey($item)]);
151
152
        if ($document) {
153
            $return = [
154
                self::DRIVER_DATA_WRAPPER_INDEX => $this->decode($document[self::DRIVER_DATA_WRAPPER_INDEX]->getData()),
155
                self::DRIVER_TAGS_WRAPPER_INDEX => $this->decode($document[self::DRIVER_TAGS_WRAPPER_INDEX]->getData()),
156
                self::DRIVER_EDATE_WRAPPER_INDEX => $document[self::DRIVER_EDATE_WRAPPER_INDEX]->toDateTime(),
157
            ];
158
159
            if (!empty($this->getConfig()->isItemDetailedDate())) {
160
                $return += [
161
                    self::DRIVER_MDATE_WRAPPER_INDEX => $document[self::DRIVER_MDATE_WRAPPER_INDEX]->toDateTime(),
162
                    self::DRIVER_CDATE_WRAPPER_INDEX => $document[self::DRIVER_CDATE_WRAPPER_INDEX]->toDateTime(),
163
                ];
164
            }
165
166
            return $return;
167
        }
168
169
        return null;
170
    }
171
172
    /**
173
     * @return Collection
174
     */
175
    protected function getCollection(): Collection
176
    {
177
        return $this->collection;
178
    }
179
180
    /**
181
     * @param CacheItemInterface $item
182
     * @return mixed
183
     * @throws PhpfastcacheInvalidArgumentException
184
     * @throws PhpfastcacheDriverException
185
     */
186
    protected function driverWrite(CacheItemInterface $item): bool
187
    {
188
        /**
189
         * Check for Cross-Driver type confusion
190
         */
191
        if ($item instanceof Item) {
192
            try {
193
                $set = [
194
                    self::DRIVER_KEY_WRAPPER_INDEX => $item->getKey(),
195
                    self::DRIVER_DATA_WRAPPER_INDEX => new Binary($this->encode($item->get()), Binary::TYPE_GENERIC),
196
                    self::DRIVER_TAGS_WRAPPER_INDEX => new Binary($this->encode($item->getTags()), Binary::TYPE_GENERIC),
197
                    self::DRIVER_EDATE_WRAPPER_INDEX => ($item->getTtl() > 0 ? new UTCDateTime((time() + $item->getTtl()) * 1000) : new UTCDateTime(time() * 1000)),
198
                ];
199
200
                if (!empty($this->getConfig()->isItemDetailedDate())) {
201
                    $set += [
202
                        self::DRIVER_MDATE_WRAPPER_INDEX => ($item->getModificationDate() ? new UTCDateTime(
203
                            ($item->getModificationDate()
204
                                ->getTimestamp()) * 1000
205
                        ) : new UTCDateTime(time() * 1000)),
206
                        self::DRIVER_CDATE_WRAPPER_INDEX => ($item->getCreationDate() ? new UTCDateTime(
207
                            ($item->getCreationDate()
208
                                ->getTimestamp()) * 1000
209
                        ) : new UTCDateTime(time() * 1000)),
210
                    ];
211
                }
212
                $result = (array)$this->getCollection()->updateOne(
213
                    ['_id' => $this->getMongoDbItemKey($item)],
214
                    [
215
                        '$set' => $set,
216
                    ],
217
                    ['upsert' => true, 'multiple' => false]
218
                );
219
            } catch (MongoDBException $e) {
220
                throw new PhpfastcacheDriverException('Got an exception while trying to write data to MongoDB server', 0, $e);
221
            }
222
223
            return isset($result['ok']) ? $result['ok'] == 1 : true;
224
        }
225
226
        throw new PhpfastcacheInvalidArgumentException('Cross-Driver type confusion detected');
227
    }
228
229
    /**
230
     * @param CacheItemInterface $item
231
     * @return bool
232
     * @throws PhpfastcacheInvalidArgumentException
233
     */
234
    protected function driverDelete(CacheItemInterface $item): bool
235
    {
236
        /**
237
         * Check for Cross-Driver type confusion
238
         */
239
        if ($item instanceof Item) {
240
            /**
241
             * @var DeleteResult $deletionResult
242
             */
243
            $deletionResult = $this->getCollection()->deleteOne(['_id' =>  $this->getMongoDbItemKey($item)]);
244
245
            return $deletionResult->isAcknowledged();
246
        }
247
248
        throw new PhpfastcacheInvalidArgumentException('Cross-Driver type confusion detected');
249
    }
250
251
    /**
252
     * @return bool
253
     */
254
    protected function driverClear(): bool
255
    {
256
        try {
257
           return $this->collection->deleteMany([])->isAcknowledged();
258
        } catch (MongoDBException $e) {
259
            throw new PhpfastcacheDriverException('Got error while trying to empty the collection: ' . $e->getMessage(), 0, $e);
260
        }
261
    }
262
263
    /**
264
     * @return bool
265
     * @throws MongodbException
266
     * @throws LogicException
267
     */
268
    protected function driverConnect(): bool
269
    {
270
        if ($this->instance instanceof Client) {
271
            throw new LogicException('Already connected to Mongodb server');
272
        }
273
274
        $timeout = $this->getConfig()->getTimeout() * 1000;
275
        $collectionName = $this->getConfig()->getCollectionName();
276
        $databaseName = $this->getConfig()->getDatabaseName();
277
        $driverOptions = $this->getConfig()->getDriverOptions();
278
279
        $this->instance = $this->instance ?: new Client($this->buildConnectionURI($databaseName), ['connectTimeoutMS' => $timeout], $driverOptions);
280
        $this->database = $this->database ?: $this->instance->selectDatabase($databaseName);
281
282
        if (!$this->collectionExists($collectionName)) {
283
            $this->database->createCollection($collectionName);
284
        }
285
286
        $this->collection = $this->database->selectCollection($collectionName);
287
288
        return true;
289
    }
290
291
    /**
292
     * Builds the connection URI from the given parameters.
293
     *
294
     * @param string $databaseName
295
     * @return string The connection URI.
296
     */
297
    protected function buildConnectionURI(string $databaseName): string
298
    {
299
        $databaseName = \urlencode($databaseName);
300
        $servers = $this->getConfig()->getServers();
301
        $options = $this->getConfig()->getOptions();
302
303
        $protocol = $this->getConfig()->getProtocol();
304
        $host = $this->getConfig()->getHost();
305
        $port = $this->getConfig()->getPort();
306
        $username = $this->getConfig()->getUsername();
307
        $password = $this->getConfig()->getPassword();
308
309
        if (count($servers) > 0) {
310
            $host = array_reduce(
311
                $servers,
312
                static function ($carry, $data) {
313
                    $carry .= ($carry === '' ? '' : ',') . $data['host'] . ':' . $data['port'];
314
                    return $carry;
315
                },
316
                ''
317
            );
318
            $port = false;
319
        }
320
321
        return implode(
322
            '',
323
            [
324
                "{$protocol}://",
325
                $username ?: '',
326
                $password ? ":{$password}" : '',
327
                $username ? '@' : '',
328
                $host,
329
                $port !== 27017 && $port !== false ? ":{$port}" : '',
330
                $databaseName ? "/{$databaseName}" : '',
331
                count($options) > 0 ? '?' . http_build_query($options) : '',
332
            ]
333
        );
334
    }
335
336
    protected function getMongoDbItemKey(CacheItemInterface $item)
337
    {
338
        return 'pfc_' . $item->getEncodedKey();
339
    }
340
341
    /********************
342
     *
343
     * PSR-6 Extended Methods
344
     *
345
     *******************/
346
347
    /**
348
     * Checks if a collection name exists on the Mongo database.
349
     *
350
     * @param string $collectionName The collection name to check.
351
     *
352
     * @return bool True if the collection exists, false if not.
353
     */
354
    protected function collectionExists($collectionName): bool
355
    {
356
        foreach ($this->database->listCollections() as $collection) {
357
            if ($collection->getName() === $collectionName) {
358
                return true;
359
            }
360
        }
361
362
        return false;
363
    }
364
}
365