Completed
Push — v5 ( e03f3a...66bc70 )
by Georges
02:44
created

DriverAbstract::decode()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 5
Bugs 1 Features 1
Metric Value
cc 2
eloc 6
c 5
b 1
f 1
nc 2
nop 1
dl 0
loc 9
rs 9.6666
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 $keyword
83
     * @return bool
84
     */
85
    public function isExisting($keyword)
86
    {
87
        if (method_exists($this, 'driver_isExisting')) {
88
            return $this->driver_isExisting($keyword);
0 ignored issues
show
Bug introduced by
The method driver_isExisting() does not exist on phpFastCache\Core\DriverAbstract. Did you maybe mean isExisting()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
89
        }
90
91
        $data = $this->get($keyword);
0 ignored issues
show
Bug introduced by
The method get() does not seem to exist on object<phpFastCache\Core\DriverAbstract>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
92
        if ($data == null) {
93
            return false;
94
        } else {
95
            return true;
96
        }
97
98
    }
99
100
    /**
101
     * @param $config_name
102
     * @param string $value
103
     */
104
    public function setup($config_name, $value = '')
105
    {
106
        /**
107
         * Config for class
108
         */
109
        if (is_array($config_name)) {
110
            $this->config = array_merge($this->config, $config_name);
111
        } else {
112
            $this->config[ $config_name ] = $value;
113
        }
114
    }
115
116
117
    /**
118
     * @param $file
119
     * @return string
120
     * @throws \Exception
121
     */
122
    protected function readfile($file)
123
    {
124
        if (function_exists('file_get_contents')) {
125
            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...
126
        } else {
127
            $string = '';
128
129
            $file_handle = @fopen($file, 'r');
130
            if (!$file_handle) {
131
                throw new phpFastCacheDriverException("Can't Read File", 96);
132
133
            }
134
            while (!feof($file_handle)) {
135
                $line = fgets($file_handle);
136
                $string .= $line;
137
            }
138
            fclose($file_handle);
139
140
            return $string;
141
        }
142
    }
143
144
    /**
145
     * Encode data types such as object/array
146
     * for driver that does not support
147
     * non-scalar value
148
     * @param $data
149
     * @return string
150
     */
151
    protected function encode($data)
152
    {
153
        return serialize($data);
154
    }
155
156
    /**
157
     * Decode data types such as object/array
158
     * for driver that does not support
159
     * non-scalar value
160
     * @param $value
161
     * @return mixed
162
     */
163
    protected function decode($value)
164
    {
165
        $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...
166
        if ($x == false) {
167
            return $value;
168
        } else {
169
            return $x;
170
        }
171
    }
172
173
    /**
174
     * Check phpModules or CGI
175
     * @return bool
176
     */
177
    protected function isPHPModule()
178
    {
179
        if (PHP_SAPI === 'apache2handler') {
180
            return true;
181
        } else {
182
            if (strpos(PHP_SAPI, 'handler') !== false) {
183
                return true;
184
            }
185
        }
186
187
        return false;
188
    }
189
190
191
    /**
192
     * @param $class
193
     * @return bool
194
     */
195
    protected function isExistingDriver($class)
196
    {
197
        return class_exists("\\phpFastCache\\Drivers\\{$class}");
198
    }
199
200
201
    /**
202
     * @param $tag
203
     * @return string
204
     */
205
    protected function _getTagName($tag)
206
    {
207
        return "__tag__" . $tag;
208
    }
209
210
    /**
211
     * @param \phpFastCache\Cache\ExtendedCacheItemInterface $item
212
     * @return array
213
     */
214
    public function driverPreWrap(ExtendedCacheItemInterface $item)
215
    {
216
        return [
217
          self::DRIVER_DATA_WRAPPER_INDEX => $item->get(),
218
          self::DRIVER_TIME_WRAPPER_INDEX => $item->getExpirationDate(),
219
          self::DRIVER_TAGS_WRAPPER_INDEX => $item->getTags(),
220
        ];
221
    }
222
223
    /**
224
     * @param array $wrapper
225
     * @return mixed
226
     */
227
    public function driverUnwrapData(array $wrapper)
228
    {
229
        return $wrapper[ self::DRIVER_DATA_WRAPPER_INDEX ];
230
    }
231
232
    /**
233
     * @param array $wrapper
234
     * @return mixed
235
     */
236
    public function driverUnwrapTags(array $wrapper)
237
    {
238
        return $wrapper[ self::DRIVER_TAGS_WRAPPER_INDEX ];
239
    }
240
241
242
    /**
243
     * @param array $wrapper
244
     * @return \DateTime
245
     */
246
    public function driverUnwrapTime(array $wrapper)
247
    {
248
        return $wrapper[ self::DRIVER_TIME_WRAPPER_INDEX ];
249
    }
250
251
    /**
252
     * @return string
253
     */
254
    public function getDriverName()
255
    {
256
        static $driverName;
257
258
        return ($driverName ?: $driverName = ucfirst(substr(strrchr((new \ReflectionObject($this))->getNamespaceName(), '\\'), 1)));
259
    }
260
261
    /**
262
     * @param \phpFastCache\Cache\ExtendedCacheItemInterface $item
263
     * @return bool
264
     */
265
    public function driverWriteTags(ExtendedCacheItemInterface $item)
266
    {
267
        $tagsItems = $this->getItems($this->getTagKeys($item->getTags()));
268
269
        foreach ($tagsItems as $tagsItem) {
270
            $data = $tagsItem->get();
271
            $expTimestamp = $item->getExpirationDate()->getTimestamp();
272
273
            /**
274
             * Using the key will
275
             * avoid to use array_unique
276
             * that has slow performances
277
             */
278
279
            $tagsItem->set(array_merge((array) $data, [$item->getKey() => $expTimestamp]));
280
281
            /**
282
             * Set the expiration date
283
             * of the $tagsItem based
284
             * on the older $item
285
             * expiration date
286
             */
287
            if ($expTimestamp > $tagsItem->getExpirationDate()->getTimestamp()) {
288
                $tagsItem->expiresAt($item->getExpirationDate());
289
            }
290
            $this->driverWrite($tagsItem);
291
        }
292
293
        /**
294
         * Also update removed tags to
295
         * keep the index up to date
296
         */
297
        $tagsItems = $this->getItems($this->getTagKeys($item->getRemovedTags()));
298
299
        foreach ($tagsItems as $tagsItem) {
300
            $data = (array) $tagsItem->get();
301
302
            unset($data[ $item->getKey() ]);
303
            $tagsItem->set($data);
304
305
            /**
306
             * Recalculate the expiration date
307
             *
308
             * If the $tagsItem does not have
309
             * any cache item references left
310
             * then remove it from tagsItems index
311
             */
312
            if (count($data)) {
313
                $tagsItem->expiresAt(max($data));
314
                $this->driverWrite($tagsItem);
315
            } else {
316
                $this->driverDelete($tagsItem);
317
            }
318
        }
319
320
        return true;
321
    }
322
323
    /**
324
     * @param $key
325
     * @return string
326
     */
327
    public function getTagKey($key)
328
    {
329
        return self::DRIVER_TAGS_KEY_PREFIX . $key;
330
    }
331
332
    /**
333
     * @param $key
334
     * @return string
335
     */
336
    public function getTagKeys(array $keys)
337
    {
338
        foreach ($keys as &$key) {
339
            $key = $this->getTagKey($key);
340
        }
341
342
        return $keys;
343
    }
344
345
    /**
346
     * @param string $tagName
347
     * @return \phpFastCache\Cache\ExtendedCacheItemInterface[]
348
     * @throws InvalidArgumentException
349
     */
350
    public function getItemsByTag($tagName)
351
    {
352
        if (is_string($tagName)) {
353
            $driverResponse = $this->driverRead($this->getTagKey($tagName));
354
            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...
355
                $items = (array) $this->driverUnwrapData($driverResponse);
356
357
                return $this->getItems(array_unique(array_keys($items)));
358
            } else {
359
                return [];
360
            }
361
        } else {
362
            throw new InvalidArgumentException('$tagName must be a string');
363
        }
364
    }
365
366
    /**
367
     * @param array $tagNames
368
     * @return \phpFastCache\Cache\ExtendedCacheItemInterface[]
369
     * @throws InvalidArgumentException
370
     */
371
    public function getItemsByTags(array $tagNames)
372
    {
373
        $items = [];
374
        foreach (array_unique($tagNames) as $tagName) {
375
            $items = array_merge($items, $this->getItemsByTag($tagName));
376
        }
377
378
        return $items;
379
    }
380
381
    /**
382
     * @param string $tagName
383
     * @return bool|null
384
     * @throws InvalidArgumentException
385
     */
386
    public function deleteItemsByTag($tagName)
387
    {
388
        if (is_string($tagName)) {
389
            $return = null;
390
            foreach ($this->getItemsByTag($tagName) as $item) {
391
                $result = $this->driverDelete($item);
392
                if ($return !== false) {
393
                    $return = $result;
394
                }
395
            }
396
397
            return $return;
398
        } else {
399
            throw new InvalidArgumentException('$tagName must be a string');
400
        }
401
    }
402
403
    /**
404
     * @param array $tagNames
405
     * @return bool|null
406
     * @throws InvalidArgumentException
407
     */
408
    public function deleteItemsByTags(array $tagNames)
409
    {
410
        $return = null;
411
        foreach ($tagNames as $tagName) {
412
            $result = $this->deleteItemsByTag($tagName);
413
            if ($return !== false) {
414
                $return = $result;
415
            }
416
        }
417
418
        return $return;
419
    }
420
421
    /**
422
     * Abstract Drivers Methods
423
     */
424
425
    /**
426
     * @param string $key
427
     * @return array [
428
     *      'd' => 'THE ITEM DATA'
429
     *      't' => 'THE ITEM DATE EXPIRATION'
430
     *      'g' => 'THE ITEM TAGS'
431
     * ]
432
     *
433
     */
434
    abstract public function driverRead($key);
435
436
    /**
437
     * @param \Psr\Cache\CacheItemInterface $item
438
     * @return mixed
439
     */
440
    abstract public function driverWrite(CacheItemInterface $item);
441
442
    /**
443
     * @return bool
444
     */
445
    abstract public function driverClear();
446
447
    /**
448
     * @return bool
449
     */
450
    abstract public function driverConnect();
451
452
    /**
453
     * @param \Psr\Cache\CacheItemInterface $item
454
     * @return bool
455
     */
456
    abstract public function driverDelete(CacheItemInterface $item);
457
458
    /**
459
     * @param \Psr\Cache\CacheItemInterface $item
460
     * @return bool
461
     */
462
    abstract public function driverIsHit(CacheItemInterface $item);
463
464
}