Passed
Pull Request — final (#592)
by Georges
02:49
created

Driver::driverCheck()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 12
rs 9.2
c 0
b 0
f 0
cc 4
eloc 6
nc 4
nop 0
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]> http://www.phpfastcache.com
11
 * @author Georges.L (Geolim4)  <[email protected]>
12
 * @author Fabio Covolo Mazzo (fabiocmazzo) <[email protected]>
13
 *
14
 */
15
16
namespace phpFastCache\Drivers\Mongodb;
17
18
use LogicException;
19
use MongoDB\BSON\Binary;
20
use MongoDB\BSON\UTCDateTime;
21
use MongoDB\Client;
22
use MongoDB\Collection;
23
use MongoDB\DeleteResult;
24
use MongoDB\Driver\Command;
25
use MongoDB\Driver\Exception\Exception as MongoDBException;
26
use phpFastCache\Core\Pool\DriverBaseTrait;
27
use phpFastCache\Core\Pool\ExtendedCacheItemPoolInterface;
28
use phpFastCache\Entities\DriverStatistic;
29
use phpFastCache\Exceptions\phpFastCacheDriverCheckException;
30
use phpFastCache\Exceptions\phpFastCacheDriverException;
31
use phpFastCache\Exceptions\phpFastCacheInvalidArgumentException;
32
use Psr\Cache\CacheItemInterface;
33
34
/**
35
 * Class Driver
36
 * @package phpFastCache\Drivers
37
 * @property MongodbManager $instance Instance of driver service
38
 */
39
class Driver implements ExtendedCacheItemPoolInterface
40
{
41
    use DriverBaseTrait;
42
43
    /**
44
     * @var Database
45
     */
46
    public $database;
47
48
    /**
49
     * @var Collection
50
     */
51
    public $collection;
52
53
    /**
54
     * Driver constructor.
55
     * @param array $config
56
     * @throws phpFastCacheDriverCheckException
57
     */
58 View Code Duplication
    public function __construct(array $config = [])
59
    {
60
        $this->setup($config);
61
62
        if (!$this->driverCheck()) {
63
            throw new phpFastCacheDriverCheckException(sprintf(self::DRIVER_CHECK_FAILURE, $this->getDriverName()));
64
        } else {
65
            $this->driverConnect();
66
        }
67
    }
68
69
    /**
70
     * @return bool
71
     */
72
    public function driverCheck()
73
    {
74
        // Get if the (new) extension is installed by checking its class
75
        $mongoExtensionExists = class_exists('MongoDB\Driver\Manager');
76
77
        if (!$mongoExtensionExists && class_exists('MongoClient')) {
78
            trigger_error('This driver is used to support the pecl MongoDb extension with mongo-php-library.
79
            For MongoDb with Mongo PECL support use Mongo Driver.', E_USER_ERROR);
80
        }
81
        
82
        return $mongoExtensionExists && class_exists('MongoDB\Collection');
83
    }
84
85
    /**
86
     * @param \Psr\Cache\CacheItemInterface $item
87
     * @return mixed
88
     * @throws phpFastCacheInvalidArgumentException
89
     * @throws phpFastCacheDriverException
90
     */
91
    protected function driverWrite(CacheItemInterface $item)
92
    {
93
        /**
94
         * Check for Cross-Driver type confusion
95
         */
96
        if ($item instanceof Item) {
97
            try {
98
                $set = [
99
                  self::DRIVER_DATA_WRAPPER_INDEX => new Binary($this->encode($item->get()), Binary::TYPE_GENERIC),
100
                  self::DRIVER_TAGS_WRAPPER_INDEX => new Binary($this->encode($item->getTags()), Binary::TYPE_GENERIC),
101
                  self::DRIVER_EDATE_WRAPPER_INDEX => ($item->getTtl() > 0 ? new UTCDateTime((time() + $item->getTtl()) * 1000) : new UTCDateTime(time() * 1000)),
102
                ];
103
104
                if(!empty($this->config[ 'itemDetailedDate' ])){
105
                    $set += [
106
                      self::DRIVER_MDATE_WRAPPER_INDEX => ($item->getModificationDate() ? new UTCDateTime(($item->getModificationDate()->getTimestamp()) * 1000) : new UTCDateTime(time() * 1000)),
107
                      self::DRIVER_CDATE_WRAPPER_INDEX => ($item->getCreationDate() ? new UTCDateTime(($item->getCreationDate()->getTimestamp()) * 1000) : new UTCDateTime(time() * 1000)),
108
                    ];
109
                }
110
111
                $result = (array)$this->getCollection()->updateOne(
112
                  ['_id' => $item->getEncodedKey()],
113
                  ['$set' => $set],
114
                  ['upsert' => true, 'multiple' => false]
115
                );
116
            } catch (MongoDBException $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...
117
                throw new phpFastCacheDriverException('Got an exception while trying to write data to MongoDB server', null, $e);
118
            }
119
120
            return isset($result[ 'ok' ]) ? $result[ 'ok' ] == 1 : true;
121
        } else {
122
            throw new phpFastCacheInvalidArgumentException('Cross-Driver type confusion detected');
123
        }
124
    }
125
126
    /**
127
     * @param \Psr\Cache\CacheItemInterface $item
128
     * @return null|array
129
     */
130
    protected function driverRead(CacheItemInterface $item)
131
    {
132
        $document = $this->getCollection()->findOne(['_id' => $item->getEncodedKey()]);
133
134
        if ($document) {
135
            $return = [
136
              self::DRIVER_DATA_WRAPPER_INDEX => $this->decode($document[ self::DRIVER_DATA_WRAPPER_INDEX ]->getData()),
137
              self::DRIVER_TAGS_WRAPPER_INDEX => $this->decode($document[ self::DRIVER_TAGS_WRAPPER_INDEX ]->getData()),
138
              self::DRIVER_EDATE_WRAPPER_INDEX => (new \DateTime())->setTimestamp($document[ self::DRIVER_EDATE_WRAPPER_INDEX ]->toDateTime()->getTimestamp()),
139
            ];
140
141
            if(!empty($this->config[ 'itemDetailedDate' ])){
142
                $return += [
143
                  self::DRIVER_MDATE_WRAPPER_INDEX => (new \DateTime())->setTimestamp($document[ self::DRIVER_MDATE_WRAPPER_INDEX ]->toDateTime()->getTimestamp()),
144
                  self::DRIVER_CDATE_WRAPPER_INDEX => (new \DateTime())->setTimestamp($document[ self::DRIVER_CDATE_WRAPPER_INDEX ]->toDateTime()->getTimestamp()),
145
                ];
146
            }
147
148
            return $return;
149
        } else {
150
            return null;
151
        }
152
    }
153
154
    /**
155
     * @param \Psr\Cache\CacheItemInterface $item
156
     * @return bool
157
     * @throws phpFastCacheInvalidArgumentException
158
     */
159
    protected function driverDelete(CacheItemInterface $item)
160
    {
161
        /**
162
         * Check for Cross-Driver type confusion
163
         */
164
        if ($item instanceof Item) {
165
            /**
166
             * @var DeleteResult $deletionResult
167
             */
168
            $deletionResult = $this->getCollection()->deleteOne(['_id' => $item->getEncodedKey()]);
169
170
            return $deletionResult->isAcknowledged();
171
        } else {
172
            throw new phpFastCacheInvalidArgumentException('Cross-Driver type confusion detected');
173
        }
174
    }
175
176
    /**
177
     * @return bool
178
     */
179
    protected function driverClear()
180
    {
181
        /**
182
         * @var \MongoDB\Model\BSONDocument $result
183
         */
184
        $result = $this->getCollection()->drop()->getArrayCopy();
185
        $this->collection = new Collection($this->instance, 'phpFastCache', 'Cache');
186
187
        /**
188
         * This will rebuild automatically the Collection indexes
189
         */
190
        $this->save($this->getItem('__PFC_CACHE_CLEARED__')->set(true));
191
192
        return !empty($result[ 'ok' ]);
193
    }
194
195
    /**
196
     * @return bool
197
     * @throws MongodbException
198
     * @throws LogicException
199
     */
200
    protected function driverConnect()
201
    {
202
        if ($this->instance instanceof \MongoDB\Driver\Manager) {
203
            throw new LogicException('Already connected to Mongodb server');
204
        } else {
205
            $timeout = isset($this->config[ 'timeout' ]) ? $this->config[ 'timeout' ] * 1000 : 3000;
206
            $collectionName = isset($this->config[ 'collectionName' ]) ? $this->config[ 'collectionName' ] : 'Cache';
207
            $databaseName = isset($this->config[ 'databaseName' ]) ? $this->config[ 'databaseName' ] : 'phpFastCache';
208
209
            $this->instance = $this->instance ?: (new Client($this->buildConnectionURI(), ['connectTimeoutMS' => $timeout]));
210
            $this->database = $this->database ?: $this->instance->selectDatabase($databaseName);
211
212
            if (!$this->collectionExists($collectionName)) {
213
                $this->database->createCollection($collectionName);
214
            }
215
216
            $this->collection = $this->database->selectCollection($collectionName);
217
218
            return true;
219
        }
220
    }
221
222
    /**
223
     * Checks if a collection name exists on the Mongo database.
224
     *
225
     * @param string $collectionName The collection name to check.
226
     *
227
     * @return bool True if the collection exists, false if not.
228
     */
229
    protected function collectionExists($collectionName)
230
    {
231
        foreach ($this->database->listCollections() as $collection) {
232
            if ($collection->getName() == $collectionName) {
233
                return true;
234
            }
235
        }
236
        
237
        return false;
238
    }
239
240
    /**
241
     * Builds the connection URI from the given parameters.
242
     * 
243
     * @return string The connection URI.
244
     */
245
    protected function buildConnectionURI()
246
    {
247
        $host = isset($this->config[ 'host' ]) ? $this->config[ 'host' ] : '127.0.0.1';
248
        $port = isset($this->config[ 'port' ]) ? $this->config[ 'port' ] : '27017';
249
        $password = isset($this->config[ 'password' ]) ? $this->config[ 'password' ] : '';
250
        $username = isset($this->config[ 'username' ]) ? $this->config[ 'username' ] : '';
251
252
        $parts = [
253
            'mongodb://',
254
            ($username ?: ''),
255
            ($password ? ":{$password}" : ''),
256
            ($username ? '@' : ''),
257
            $host,
258
            ($port != '27017' ? ":{$port}" : '')
259
        ];
260
261
        return implode('', $parts);
262
    }
263
264
    /**
265
     * @return Collection
266
     */
267
    protected function getCollection()
268
    {
269
        return $this->collection;
270
    }
271
272
    /********************
273
     *
274
     * PSR-6 Extended Methods
275
     *
276
     *******************/
277
278
    /**
279
     * @return DriverStatistic
280
     */
281
    public function getStats()
282
    {
283
        $serverStats = $this->instance->getManager()->executeCommand('phpFastCache', new Command([
284
          'serverStatus' => 1,
285
          'recordStats' => 0,
286
          'repl' => 0,
287
          'metrics' => 0,
288
        ]))->toArray()[ 0 ];
289
290
        $collectionStats = $this->instance->getManager()->executeCommand('phpFastCache', new Command([
291
          'collStats' => (isset($this->config[ 'collectionName' ]) ? $this->config[ 'collectionName' ] : 'Cache'),
292
          'verbose' => true,
293
        ]))->toArray()[ 0 ];
294
295
        $array_filter_recursive = function ($array, callable $callback = null) use (&$array_filter_recursive) {
296
            $array = $callback($array);
297
298
            if (is_object($array) || is_array($array)) {
299
                foreach ($array as &$value) {
300
                    $value = call_user_func($array_filter_recursive, $value, $callback);
301
                }
302
            }
303
304
            return $array;
305
        };
306
307
        $callback = function ($item) {
308
            /**
309
             * Remove unserializable properties
310
             */
311
            if ($item instanceof \MongoDB\BSON\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...
312
                return (string)$item;
313
            }
314
            return $item;
315
        };
316
317
        $serverStats = $array_filter_recursive($serverStats, $callback);
318
        $collectionStats = $array_filter_recursive($collectionStats, $callback);
319
320
        $stats = (new DriverStatistic())
321
          ->setInfo('MongoDB version ' . $serverStats->version . ', Uptime (in days): ' . round($serverStats->uptime / 86400,
322
              1) . "\n For more information see RawData.")
323
          ->setSize($collectionStats->size)
324
          ->setData(implode(', ', array_keys($this->itemInstances)))
325
          ->setRawData([
326
            'serverStatus' => $serverStats,
327
            'collStats' => $collectionStats,
328
          ]);
329
330
        return $stats;
331
    }
332
}
333