Completed
Push — master ( 4e7427...4a94bb )
by Georges
12s
created

Driver   A

Complexity

Total Complexity 39

Size/Duplication

Total Lines 271
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
dl 0
loc 271
rs 9.28
c 0
b 0
f 0
wmc 39

10 Methods

Rating   Name   Duplication   Size   Complexity  
A driverConnect() 0 20 5
A driverCheck() 0 10 4
B driverWrite() 0 36 8
A driverDelete() 0 15 2
A driverClear() 0 3 1
A driverRead() 0 24 3
B getStats() 0 50 6
A buildConnectionURI() 0 15 6
A collectionExists() 0 9 3
A getCollection() 0 3 1
1
<?php
2
/**
3
 *
4
 * This file is part of phpFastCache.
5
 *
6
 * @license MIT License (MIT)
7
 *
8
 * For full copyright and license information, please see the docs/CREDITS.txt file.
9
 *
10
 * @author Khoa Bui (khoaofgod)  <[email protected]> https://www.phpfastcache.com
11
 * @author Georges.L (Geolim4)  <[email protected]>
12
 * @author Fabio Covolo Mazzo (fabiocmazzo) <[email protected]>
13
 *
14
 */
15
declare(strict_types=1);
16
17
namespace Phpfastcache\Drivers\Mongodb;
18
19
use LogicException;
20
use MongoDB\{
21
    BSON\Binary, BSON\UTCDateTime, Client, Collection, Database, DeleteResult, Driver\Command, Driver\Exception\Exception as MongoDBException
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\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...
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...
22
};
23
use Phpfastcache\Core\Pool\{
24
    DriverBaseTrait, ExtendedCacheItemPoolInterface
25
};
26
use Phpfastcache\Entities\DriverStatistic;
27
use Phpfastcache\Exceptions\{
28
    PhpfastcacheDriverException, PhpfastcacheInvalidArgumentException
29
};
30
use Psr\Cache\CacheItemInterface;
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
40
{
41
    const MONGODB_DEFAULT_DB_NAME = 'phpfastcache';
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(\MongoDB\Driver\Manager::class);
61
62
        if (!$mongoExtensionExists && class_exists(\MongoClient::class)) {
63
            \trigger_error('This driver is used to support the pecl MongoDb extension with mongo-php-library.
64
            For MongoDb with Mongo PECL support use Mongo Driver.', E_USER_ERROR);
65
        }
66
67
        return $mongoExtensionExists && class_exists(\MongoDB\Collection::class);
68
    }
69
70
    /**
71
     * @param \Psr\Cache\CacheItemInterface $item
72
     * @return null|array
73
     */
74
    protected function driverRead(CacheItemInterface $item)
75
    {
76
        $document = $this->getCollection()->findOne(['_id' => $item->getEncodedKey()]);
77
78
        if ($document) {
79
            $return = [
80
                self::DRIVER_DATA_WRAPPER_INDEX => $this->decode($document[self::DRIVER_DATA_WRAPPER_INDEX]->getData()),
81
                self::DRIVER_TAGS_WRAPPER_INDEX => $this->decode($document[self::DRIVER_TAGS_WRAPPER_INDEX]->getData()),
82
                self::DRIVER_EDATE_WRAPPER_INDEX => (new \DateTime())->setTimestamp($document[self::DRIVER_EDATE_WRAPPER_INDEX]->toDateTime()->getTimestamp()),
83
            ];
84
85
            if (!empty($this->getConfig()->isItemDetailedDate())) {
86
                $return += [
87
                    self::DRIVER_MDATE_WRAPPER_INDEX => (new \DateTime())->setTimestamp($document[self::DRIVER_MDATE_WRAPPER_INDEX]->toDateTime()
88
                        ->getTimestamp()),
89
                    self::DRIVER_CDATE_WRAPPER_INDEX => (new \DateTime())->setTimestamp($document[self::DRIVER_CDATE_WRAPPER_INDEX]->toDateTime()
90
                        ->getTimestamp()),
91
                ];
92
            }
93
94
            return $return;
95
        }
96
97
        return null;
98
    }
99
100
    /**
101
     * @param \Psr\Cache\CacheItemInterface $item
102
     * @return mixed
103
     * @throws PhpfastcacheInvalidArgumentException
104
     * @throws PhpfastcacheDriverException
105
     */
106
    protected function driverWrite(CacheItemInterface $item): bool
107
    {
108
        /**
109
         * Check for Cross-Driver type confusion
110
         */
111
        if ($item instanceof Item) {
112
            try {
113
                $set = [
114
                    self::DRIVER_DATA_WRAPPER_INDEX => new Binary($this->encode($item->get()), Binary::TYPE_GENERIC),
115
                    self::DRIVER_TAGS_WRAPPER_INDEX => new Binary($this->encode($item->getTags()), Binary::TYPE_GENERIC),
116
                    self::DRIVER_EDATE_WRAPPER_INDEX => ($item->getTtl() > 0 ? new UTCDateTime((\time() + $item->getTtl()) * 1000) : new UTCDateTime(\time() * 1000)),
117
                ];
118
119
                if (!empty($this->getConfig()->isItemDetailedDate())) {
120
                    $set += [
121
                        self::DRIVER_MDATE_WRAPPER_INDEX => ($item->getModificationDate() ? new UTCDateTime(($item->getModificationDate()
122
                                ->getTimestamp()) * 1000) : new UTCDateTime(\time() * 1000)),
123
                        self::DRIVER_CDATE_WRAPPER_INDEX => ($item->getCreationDate() ? new UTCDateTime(($item->getCreationDate()
124
                                ->getTimestamp()) * 1000) : new UTCDateTime(\time() * 1000)),
125
                    ];
126
                }
127
                $result = (array)$this->getCollection()->updateOne(
128
                    ['_id' => $item->getEncodedKey()],
129
                    [
130
                        '$set' => $set,
131
                    ],
132
                    ['upsert' => true, 'multiple' => false]
133
                );
134
            } catch (MongoDBException $e) {
135
                throw new PhpfastcacheDriverException('Got an exception while trying to write data to MongoDB server', null, $e);
136
            }
137
138
            return isset($result['ok']) ? $result['ok'] == 1 : true;
139
        }
140
141
        throw new PhpfastcacheInvalidArgumentException('Cross-Driver type confusion detected');
142
    }
143
144
    /**
145
     * @param \Psr\Cache\CacheItemInterface $item
146
     * @return bool
147
     * @throws PhpfastcacheInvalidArgumentException
148
     */
149
    protected function driverDelete(CacheItemInterface $item): bool
150
    {
151
        /**
152
         * Check for Cross-Driver type confusion
153
         */
154
        if ($item instanceof Item) {
155
            /**
156
             * @var DeleteResult $deletionResult
157
             */
158
            $deletionResult = $this->getCollection()->deleteOne(['_id' => $item->getEncodedKey()]);
159
160
            return $deletionResult->isAcknowledged();
161
        }
162
163
        throw new PhpfastcacheInvalidArgumentException('Cross-Driver type confusion detected');
164
    }
165
166
    /**
167
     * @return bool
168
     */
169
    protected function driverClear(): bool
170
    {
171
        return $this->collection->deleteMany([])->isAcknowledged();
172
    }
173
174
    /**
175
     * @return bool
176
     * @throws MongodbException
177
     * @throws LogicException
178
     */
179
    protected function driverConnect(): bool
180
    {
181
        if ($this->instance instanceof Client) {
182
            throw new LogicException('Already connected to Mongodb server');
183
        }
184
185
        $timeout = $this->getConfig()->getTimeout() * 1000;
186
        $collectionName = $this->getConfig()->getCollectionName();
187
        $databaseName = $this->getConfig()->getDatabaseName();
188
189
        $this->instance = $this->instance ?: new Client($this->buildConnectionURI($databaseName), ['connectTimeoutMS' => $timeout]);
190
        $this->database = $this->database ?: $this->instance->selectDatabase($databaseName);
191
192
        if (!$this->collectionExists($collectionName)) {
193
            $this->database->createCollection($collectionName);
194
        }
195
196
        $this->collection = $this->database->selectCollection($collectionName);
197
198
        return true;
199
    }
200
201
    /**
202
     * Checks if a collection name exists on the Mongo database.
203
     *
204
     * @param string $collectionName The collection name to check.
205
     *
206
     * @return bool True if the collection exists, false if not.
207
     */
208
    protected function collectionExists($collectionName): bool
209
    {
210
        foreach ($this->database->listCollections() as $collection) {
211
            if ($collection->getName() === $collectionName) {
212
                return true;
213
            }
214
        }
215
216
        return false;
217
    }
218
219
    /**
220
     * Builds the connection URI from the given parameters.
221
     *
222
     * @param string $databaseName
223
     * @return string The connection URI.
224
     */
225
    protected function buildConnectionURI($databaseName = ''): string
226
    {
227
        $host = $this->getConfig()->getHost();
228
        $port = $this->getConfig()->getPort();
229
        $username = $this->getConfig()->getUsername();
230
        $password = $this->getConfig()->getPassword();
231
232
        return implode('', [
233
            'mongodb://',
234
            ($username ?: ''),
235
            ($password ? ":{$password}" : ''),
236
            ($username ? '@' : ''),
237
            $host,
238
            ($port !== 27017 ? ":{$port}" : ''),
239
            ($databaseName ? "/{$databaseName}" : ''),
240
        ]);
241
    }
242
243
    /**
244
     * @return Collection
245
     */
246
    protected function getCollection(): Collection
247
    {
248
        return $this->collection;
249
    }
250
251
    /********************
252
     *
253
     * PSR-6 Extended Methods
254
     *
255
     *******************/
256
257
    /**
258
     * @return DriverStatistic
259
     */
260
    public function getStats(): DriverStatistic
261
    {
262
        $serverStats = $this->instance->getManager()->executeCommand('phpFastCache', new Command([
263
            'serverStatus' => 1,
264
            'recordStats' => 0,
265
            'repl' => 0,
266
            'metrics' => 0,
267
        ]))->toArray()[0];
268
269
        $collectionStats = $this->instance->getManager()->executeCommand('phpFastCache', new Command([
270
            'collStats' => (isset($this->getConfig()['collectionName']) ? $this->getConfig()['collectionName'] : 'Cache'),
271
            'verbose' => true,
272
        ]))->toArray()[0];
273
274
        $array_filter_recursive = function ($array, callable $callback = null) use (&$array_filter_recursive) {
275
            $array = $callback($array);
276
277
            if (\is_object($array) || \is_array($array)) {
278
                foreach ($array as &$value) {
279
                    $value = \call_user_func($array_filter_recursive, $value, $callback);
280
                }
281
            }
282
283
            return $array;
284
        };
285
286
        $callback = function ($item) {
287
            /**
288
             * Remove unserializable properties
289
             */
290
            if ($item instanceof \MongoDB\BSON\UTCDateTime) {
291
                return (string)$item;
292
            }
293
            return $item;
294
        };
295
296
        $serverStats = $array_filter_recursive($serverStats, $callback);
297
        $collectionStats = $array_filter_recursive($collectionStats, $callback);
298
299
        $stats = (new DriverStatistic())
300
            ->setInfo('MongoDB version ' . $serverStats->version . ', Uptime (in days): ' . \round($serverStats->uptime / 86400,
301
                    1) . "\n For more information see RawData.")
302
            ->setSize($collectionStats->size)
303
            ->setData(\implode(', ', \array_keys($this->itemInstances)))
304
            ->setRawData([
305
                'serverStatus' => $serverStats,
306
                'collStats' => $collectionStats,
307
            ]);
308
309
        return $stats;
310
    }
311
}
312