Completed
Push — v5 ( 841c67...c7699f )
by Georges
02:42
created

DriverAbstract   B

Complexity

Total Complexity 44

Size/Duplication

Total Lines 418
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 2

Importance

Changes 31
Bugs 3 Features 6
Metric Value
c 31
b 3
f 6
dl 0
loc 418
rs 8.3396
wmc 44
lcom 2
cbo 2

27 Methods

Rating   Name   Duplication   Size   Complexity  
A __destruct() 0 7 3
A encodeFilename() 0 6 1
A setup() 0 11 2
A readfile() 0 21 4
A encode() 0 4 1
A decode() 0 9 2
A isPHPModule() 0 12 3
A isExistingDriver() 0 4 1
A _getTagName() 0 4 1
A driverPreWrap() 0 8 1
A driverUnwrapData() 0 4 1
A driverUnwrapTags() 0 4 1
A driverUnwrapTime() 0 4 1
A getDriverName() 0 6 2
B driverWriteTags() 0 57 5
A getTagKey() 0 4 1
A getTagKeys() 0 8 2
A getItemsByTag() 0 15 3
A getItemsByTags() 0 9 2
A deleteItemsByTag() 0 16 4
A deleteItemsByTags() 0 12 3
driverRead() 0 1 ?
driverWrite() 0 1 ?
driverClear() 0 1 ?
driverConnect() 0 1 ?
driverDelete() 0 1 ?
driverIsHit() 0 1 ?

How to fix   Complexity   

Complex Class

Complex classes like DriverAbstract often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use DriverAbstract, and based on these observations, apply Extract Interface, too.

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
 *
13
 */
14
15
namespace phpFastCache\Core;
16
17
use InvalidArgumentException;
18
use phpFastCache\Cache\ExtendedCacheItemInterface;
19
use phpFastCache\Cache\ExtendedCacheItemPoolInterface;
20
use phpFastCache\Exceptions\phpFastCacheDriverException;
21
use phpFastCache\CacheManager;
22
use Psr\Cache\CacheItemInterface;
23
24
/**
25
 * Class DriverAbstract
26
 * @package phpFastCache\Core
27
 */
28
abstract class DriverAbstract implements ExtendedCacheItemPoolInterface
29
{
30
    const DRIVER_CHECK_FAILURE      = '%s is not installed or misconfigured, cannot continue.';
31
    const DRIVER_TAGS_KEY_PREFIX    = '_TAG_';
32
    const DRIVER_DATA_WRAPPER_INDEX = 'd';
33
    const DRIVER_TIME_WRAPPER_INDEX = 't';
34
    const DRIVER_TAGS_WRAPPER_INDEX = 'g';
35
36
    /**
37
     * @var array
38
     */
39
    public $extension_dir = '_extensions';
40
41
    /**
42
     * @var array
43
     */
44
    public $tmp = [];
45
46
    /**
47
     * @var array default options, this will be merge to Driver's Options
48
     */
49
    public $config = [];
50
51
    /**
52
     * @var bool
53
     */
54
    public $fallback = false;
55
56
    /**
57
     * @var mixed Instance of driver service
58
     */
59
    public $instance;
60
61
62
    public function __destruct()
63
    {
64
        // clean up the memory and don't want for PHP clean for caching method "phpfastcache"
65
        if (isset($this->config[ 'instance' ]) && (int) $this->config[ 'cache_method' ] === 3) {
66
            CacheManager::cleanCachingMethod($this->config[ 'instance' ]);
67
        }
68
    }
69
70
    /**
71
     * @param $keyword
72
     * @return string
73
     */
74
    protected function encodeFilename($keyword)
75
    {
76
        // return trim(trim(preg_replace('/[^a-zA-Z0-9]+/', '_', $keyword), '_'));
0 ignored issues
show
Unused Code Comprehensibility introduced by
66% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
77
        // return rtrim(base64_encode($keyword), '=');
0 ignored issues
show
Unused Code Comprehensibility introduced by
65% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
78
        return md5($keyword);
79
    }
80
81
    /**
82
     * @param $config_name
83
     * @param string $value
84
     */
85
    public function setup($config_name, $value = '')
86
    {
87
        /**
88
         * Config for class
89
         */
90
        if (is_array($config_name)) {
91
            $this->config = array_merge($this->config, $config_name);
92
        } else {
93
            $this->config[ $config_name ] = $value;
94
        }
95
    }
96
97
98
    /**
99
     * @param $file
100
     * @return string
101
     * @throws \Exception
102
     */
103
    protected function readfile($file)
104
    {
105
        if (function_exists('file_get_contents')) {
106
            return file_get_contents($file);
0 ignored issues
show
Security File Exposure introduced by
$file can contain request data and is used in file inclusion context(s) leading to a potential security vulnerability.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
107
        } else {
108
            $string = '';
109
110
            $file_handle = @fopen($file, 'r');
111
            if (!$file_handle) {
112
                throw new phpFastCacheDriverException("Can't Read File", 96);
113
114
            }
115
            while (!feof($file_handle)) {
116
                $line = fgets($file_handle);
117
                $string .= $line;
118
            }
119
            fclose($file_handle);
120
121
            return $string;
122
        }
123
    }
124
125
    /**
126
     * Encode data types such as object/array
127
     * for driver that does not support
128
     * non-scalar value
129
     * @param $data
130
     * @return string
131
     */
132
    protected function encode($data)
133
    {
134
        return serialize($data);
135
    }
136
137
    /**
138
     * Decode data types such as object/array
139
     * for driver that does not support
140
     * non-scalar value
141
     * @param $value
142
     * @return mixed
143
     */
144
    protected function decode($value)
145
    {
146
        $x = @unserialize($value);
0 ignored issues
show
Security Object Injection introduced by
$value can contain request data and is used in unserialized context(s) leading to a potential security vulnerability.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
147
        if ($x == false) {
148
            return $value;
149
        } else {
150
            return $x;
151
        }
152
    }
153
154
    /**
155
     * Check phpModules or CGI
156
     * @return bool
157
     */
158
    protected function isPHPModule()
159
    {
160
        if (PHP_SAPI === 'apache2handler') {
161
            return true;
162
        } else {
163
            if (strpos(PHP_SAPI, 'handler') !== false) {
164
                return true;
165
            }
166
        }
167
168
        return false;
169
    }
170
171
172
    /**
173
     * @param $class
174
     * @return bool
175
     */
176
    protected function isExistingDriver($class)
177
    {
178
        return class_exists("\\phpFastCache\\Drivers\\{$class}");
179
    }
180
181
182
    /**
183
     * @param $tag
184
     * @return string
185
     */
186
    protected function _getTagName($tag)
187
    {
188
        return "__tag__" . $tag;
189
    }
190
191
    /**
192
     * @param \phpFastCache\Cache\ExtendedCacheItemInterface $item
193
     * @return array
194
     */
195
    public function driverPreWrap(ExtendedCacheItemInterface $item)
196
    {
197
        return [
198
          self::DRIVER_DATA_WRAPPER_INDEX => $item->get(),
199
          self::DRIVER_TIME_WRAPPER_INDEX => $item->getExpirationDate(),
200
          self::DRIVER_TAGS_WRAPPER_INDEX => $item->getTags(),
201
        ];
202
    }
203
204
    /**
205
     * @param array $wrapper
206
     * @return mixed
207
     */
208
    public function driverUnwrapData(array $wrapper)
209
    {
210
        return $wrapper[ self::DRIVER_DATA_WRAPPER_INDEX ];
211
    }
212
213
    /**
214
     * @param array $wrapper
215
     * @return mixed
216
     */
217
    public function driverUnwrapTags(array $wrapper)
218
    {
219
        return $wrapper[ self::DRIVER_TAGS_WRAPPER_INDEX ];
220
    }
221
222
223
    /**
224
     * @param array $wrapper
225
     * @return \DateTime
226
     */
227
    public function driverUnwrapTime(array $wrapper)
228
    {
229
        return $wrapper[ self::DRIVER_TIME_WRAPPER_INDEX ];
230
    }
231
232
    /**
233
     * @return string
234
     */
235
    public function getDriverName()
236
    {
237
        static $driverName;
238
239
        return ($driverName ?: $driverName = ucfirst(substr(strrchr((new \ReflectionObject($this))->getNamespaceName(), '\\'), 1)));
240
    }
241
242
    /**
243
     * @param \phpFastCache\Cache\ExtendedCacheItemInterface $item
244
     * @return bool
245
     */
246
    public function driverWriteTags(ExtendedCacheItemInterface $item)
247
    {
248
        $tagsItems = $this->getItems($this->getTagKeys($item->getTags()));
249
250
        foreach ($tagsItems as $tagsItem) {
251
            $data = $tagsItem->get();
252
            $expTimestamp = $item->getExpirationDate()->getTimestamp();
253
254
            /**
255
             * Using the key will
256
             * avoid to use array_unique
257
             * that has slow performances
258
             */
259
260
            $tagsItem->set(array_merge((array) $data, [$item->getKey() => $expTimestamp]));
261
262
            /**
263
             * Set the expiration date
264
             * of the $tagsItem based
265
             * on the older $item
266
             * expiration date
267
             */
268
            if ($expTimestamp > $tagsItem->getExpirationDate()->getTimestamp()) {
269
                $tagsItem->expiresAt($item->getExpirationDate());
270
            }
271
            $this->driverWrite($tagsItem);
272
        }
273
274
        /**
275
         * Also update removed tags to
276
         * keep the index up to date
277
         */
278
        $tagsItems = $this->getItems($this->getTagKeys($item->getRemovedTags()));
279
280
        foreach ($tagsItems as $tagsItem) {
281
            $data = (array) $tagsItem->get();
282
283
            unset($data[ $item->getKey() ]);
284
            $tagsItem->set($data);
285
286
            /**
287
             * Recalculate the expiration date
288
             *
289
             * If the $tagsItem does not have
290
             * any cache item references left
291
             * then remove it from tagsItems index
292
             */
293
            if (count($data)) {
294
                $tagsItem->expiresAt(max($data));
295
                $this->driverWrite($tagsItem);
296
            } else {
297
                $this->driverDelete($tagsItem);
298
            }
299
        }
300
301
        return true;
302
    }
303
304
    /**
305
     * @param $key
306
     * @return string
307
     */
308
    public function getTagKey($key)
309
    {
310
        return self::DRIVER_TAGS_KEY_PREFIX . $key;
311
    }
312
313
    /**
314
     * @param $key
315
     * @return string
316
     */
317
    public function getTagKeys(array $keys)
318
    {
319
        foreach ($keys as &$key) {
320
            $key = $this->getTagKey($key);
321
        }
322
323
        return $keys;
324
    }
325
326
    /**
327
     * @param string $tagName
328
     * @return \phpFastCache\Cache\ExtendedCacheItemInterface[]
329
     * @throws InvalidArgumentException
330
     */
331
    public function getItemsByTag($tagName)
332
    {
333
        if (is_string($tagName)) {
334
            $driverResponse = $this->driverRead($this->getTagKey($tagName));
335
            if ($driverResponse) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $driverResponse of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
336
                $items = (array) $this->driverUnwrapData($driverResponse);
337
338
                return $this->getItems(array_unique(array_keys($items)));
339
            } else {
340
                return [];
341
            }
342
        } else {
343
            throw new InvalidArgumentException('$tagName must be a string');
344
        }
345
    }
346
347
    /**
348
     * @param array $tagNames
349
     * @return \phpFastCache\Cache\ExtendedCacheItemInterface[]
350
     * @throws InvalidArgumentException
351
     */
352
    public function getItemsByTags(array $tagNames)
353
    {
354
        $items = [];
355
        foreach (array_unique($tagNames) as $tagName) {
356
            $items = array_merge($items, $this->getItemsByTag($tagName));
357
        }
358
359
        return $items;
360
    }
361
362
    /**
363
     * @param string $tagName
364
     * @return bool|null
365
     * @throws InvalidArgumentException
366
     */
367
    public function deleteItemsByTag($tagName)
368
    {
369
        if (is_string($tagName)) {
370
            $return = null;
371
            foreach ($this->getItemsByTag($tagName) as $item) {
372
                $result = $this->driverDelete($item);
373
                if ($return !== false) {
374
                    $return = $result;
375
                }
376
            }
377
378
            return $return;
379
        } else {
380
            throw new InvalidArgumentException('$tagName must be a string');
381
        }
382
    }
383
384
    /**
385
     * @param array $tagNames
386
     * @return bool|null
387
     * @throws InvalidArgumentException
388
     */
389
    public function deleteItemsByTags(array $tagNames)
390
    {
391
        $return = null;
392
        foreach ($tagNames as $tagName) {
393
            $result = $this->deleteItemsByTag($tagName);
394
            if ($return !== false) {
395
                $return = $result;
396
            }
397
        }
398
399
        return $return;
400
    }
401
402
    /**
403
     * Abstract Drivers Methods
404
     */
405
406
    /**
407
     * @param string $key
408
     * @return array [
409
     *      'd' => 'THE ITEM DATA'
410
     *      't' => 'THE ITEM DATE EXPIRATION'
411
     *      'g' => 'THE ITEM TAGS'
412
     * ]
413
     *
414
     */
415
    abstract public function driverRead($key);
416
417
    /**
418
     * @param \Psr\Cache\CacheItemInterface $item
419
     * @return mixed
420
     */
421
    abstract public function driverWrite(CacheItemInterface $item);
422
423
    /**
424
     * @return bool
425
     */
426
    abstract public function driverClear();
427
428
    /**
429
     * @return bool
430
     */
431
    abstract public function driverConnect();
432
433
    /**
434
     * @param \Psr\Cache\CacheItemInterface $item
435
     * @return bool
436
     */
437
    abstract public function driverDelete(CacheItemInterface $item);
438
439
    /**
440
     * @param \Psr\Cache\CacheItemInterface $item
441
     * @return bool
442
     */
443
    abstract public function driverIsHit(CacheItemInterface $item);
444
445
}