Completed
Push — final ( 7dac80...a0e040 )
by Georges
9s
created

Driver::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 6

Duplication

Lines 10
Ratio 100 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 10
loc 10
rs 9.4285
cc 2
eloc 6
nc 2
nop 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]> 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
        return $this->collection->deleteMany([])->isAcknowledged();
182
    }
183
184
    /**
185
     * @return bool
186
     * @throws MongodbException
187
     * @throws LogicException
188
     */
189
    protected function driverConnect()
190
    {
191
        if ($this->instance instanceof \MongoDB\Driver\Manager) {
192
            throw new LogicException('Already connected to Mongodb server');
193
        } else {
194
            $timeout = isset($this->config[ 'timeout' ]) ? $this->config[ 'timeout' ] * 1000 : 3000;
195
            $collectionName = isset($this->config[ 'collectionName' ]) ? $this->config[ 'collectionName' ] : 'Cache';
196
            $databaseName = isset($this->config[ 'databaseName' ]) ? $this->config[ 'databaseName' ] : 'phpFastCache';
197
198
            $this->instance = $this->instance ?: (new Client($this->buildConnectionURI($databaseName), ['connectTimeoutMS' => $timeout]));
199
            $this->database = $this->database ?: $this->instance->selectDatabase($databaseName);
200
201
            if (!$this->collectionExists($collectionName)) {
202
                $this->database->createCollection($collectionName);
203
            }
204
205
            $this->collection = $this->database->selectCollection($collectionName);
206
207
            return true;
208
        }
209
    }
210
211
    /**
212
     * Checks if a collection name exists on the Mongo database.
213
     *
214
     * @param string $collectionName The collection name to check.
215
     *
216
     * @return bool True if the collection exists, false if not.
217
     */
218
    protected function collectionExists($collectionName)
219
    {
220
        foreach ($this->database->listCollections() as $collection) {
221
            if ($collection->getName() == $collectionName) {
222
                return true;
223
            }
224
        }
225
        
226
        return false;
227
    }
228
229
    /**
230
     * Builds the connection URI from the given parameters.
231
     * 
232
     * @param string $databaseName
233
     * @return string The connection URI.
234
     */
235
    protected function buildConnectionURI($databaseName = '')
236
    {
237
        $host = isset($this->config[ 'host' ]) ? $this->config[ 'host' ] : '127.0.0.1';
238
        $port = isset($this->config[ 'port' ]) ? $this->config[ 'port' ] : '27017';
239
        $password = isset($this->config[ 'password' ]) ? $this->config[ 'password' ] : '';
240
        $username = isset($this->config[ 'username' ]) ? $this->config[ 'username' ] : '';
241
242
        $parts = [
243
            'mongodb://',
244
            ($username ?: ''),
245
            ($password ? ":{$password}" : ''),
246
            ($username ? '@' : ''),
247
            $host,
248
            ($port != '27017' ? ":{$port}" : ''),
249
            ($databaseName ? "/{$databaseName}" : '')
250
        ];
251
252
        return implode('', $parts);
253
    }
254
255
    /**
256
     * @return Collection
257
     */
258
    protected function getCollection()
259
    {
260
        return $this->collection;
261
    }
262
263
    /********************
264
     *
265
     * PSR-6 Extended Methods
266
     *
267
     *******************/
268
269
    /**
270
     * @return DriverStatistic
271
     */
272
    public function getStats()
273
    {
274
        $serverStats = $this->instance->getManager()->executeCommand('phpFastCache', new Command([
275
          'serverStatus' => 1,
276
          'recordStats' => 0,
277
          'repl' => 0,
278
          'metrics' => 0,
279
        ]))->toArray()[ 0 ];
280
281
        $collectionStats = $this->instance->getManager()->executeCommand('phpFastCache', new Command([
282
          'collStats' => (isset($this->config[ 'collectionName' ]) ? $this->config[ 'collectionName' ] : 'Cache'),
283
          'verbose' => true,
284
        ]))->toArray()[ 0 ];
285
286
        $array_filter_recursive = function ($array, callable $callback = null) use (&$array_filter_recursive) {
287
            $array = $callback($array);
288
289
            if (is_object($array) || is_array($array)) {
290
                foreach ($array as &$value) {
291
                    $value = call_user_func($array_filter_recursive, $value, $callback);
292
                }
293
            }
294
295
            return $array;
296
        };
297
298
        $callback = function ($item) {
299
            /**
300
             * Remove unserializable properties
301
             */
302
            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...
303
                return (string)$item;
304
            }
305
            return $item;
306
        };
307
308
        $serverStats = $array_filter_recursive($serverStats, $callback);
309
        $collectionStats = $array_filter_recursive($collectionStats, $callback);
310
311
        $stats = (new DriverStatistic())
312
          ->setInfo('MongoDB version ' . $serverStats->version . ', Uptime (in days): ' . round($serverStats->uptime / 86400,
313
              1) . "\n For more information see RawData.")
314
          ->setSize($collectionStats->size)
315
          ->setData(implode(', ', array_keys($this->itemInstances)))
316
          ->setRawData([
317
            'serverStatus' => $serverStats,
318
            'collStats' => $collectionStats,
319
          ]);
320
321
        return $stats;
322
    }
323
}
324