Completed
Push — v5 ( 2766dd...bcb79b )
by Georges
02:36
created

Driver::getRequiredOptions()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 1
eloc 2
c 1
b 0
f 1
nc 1
nop 0
dl 0
loc 4
rs 10
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\Drivers\Files;
16
17
use phpFastCache\Cache\ExtendedCacheItemInterface;
18
use phpFastCache\Core\DriverAbstract;
19
use phpFastCache\Core\PathSeekerTrait;
20
use phpFastCache\Core\StandardPsr6StructureTrait;
21
use phpFastCache\Entities\driverStatistic;
22
use phpFastCache\Exceptions\phpFastCacheDriverCheckException;
23
use phpFastCache\Exceptions\phpFastCacheDriverException;
24
use Psr\Cache\CacheItemInterface;
25
26
/**
27
 * Class Driver
28
 * @package phpFastCache\Drivers
29
 */
30
class Driver extends DriverAbstract
31
{
32
    use PathSeekerTrait, StandardPsr6StructureTrait;
33
34
    /**
35
     * Driver constructor.
36
     * @param array $config
37
     * @throws phpFastCacheDriverException
38
     */
39
    public function __construct(array $config = [])
40
    {
41
        $this->setup($config);
42
43
        if (!$this->driverCheck()) {
44
            throw new phpFastCacheDriverCheckException(sprintf(self::DRIVER_CHECK_FAILURE, $this->getDriverName()));
45
        }
46
    }
47
48
    /**
49
     * @return bool
50
     */
51
    public function driverCheck()
52
    {
53
        return is_writable($this->getPath());
54
    }
55
56
    /**
57
     * @param \Psr\Cache\CacheItemInterface $item
58
     * @return mixed
59
     * @throws \InvalidArgumentException
60
     */
61
    public function driverWrite(CacheItemInterface $item)
62
    {
63
        /**
64
         * Check for Cross-Driver type confusion
65
         */
66
        if ($item instanceof Item) {
67
            $file_path = $this->getFilePath($item->getKey());
68
            $data = $this->encode($this->driverPreWrap($item));
69
70
            $toWrite = true;
71
            /*
72
             * Skip if Existing Caching in Options
73
             */
74
            if (isset($option[ 'skipExisting' ]) && $option[ 'skipExisting' ] == true && file_exists($file_path)) {
0 ignored issues
show
Bug introduced by
The variable $option seems to never exist, and therefore isset should always return false. Did you maybe rename this variable?

This check looks for calls to isset(...) or empty() on variables that are yet undefined. These calls will always produce the same result and can be removed.

This is most likely caused by the renaming of a variable or the removal of a function/method parameter.

Loading history...
75
                $content = $this->readfile($file_path);
76
                $old = $this->decode($content);
77
                $toWrite = false;
78
                if ($old->isExpired()) {
79
                    $toWrite = true;
80
                }
81
            }
82
83
            // Force write
84
            try {
85
                if ($toWrite == true) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
86
                    $f = fopen($file_path, 'w+');
87
                    fwrite($f, $data);
88
                    fclose($f);
89
90
                    return true;
91
                }
92
            } catch (\Exception $e) {
93
                return false;
94
            }
95
        } else {
96
            throw new \InvalidArgumentException('Cross-Driver type confusion detected');
97
        }
98
    }
99
100
    /**
101
     * @param \Psr\Cache\CacheItemInterface $key
102
     * @return mixed
103
     * @throws \InvalidArgumentException
104
     */
105
    public function driverRead($key)
106
    {
107
        /**
108
         * Check for Cross-Driver type confusion
109
         */
110
        $file_path = $this->getFilePath($key);
111
        if (!file_exists($file_path)) {
112
            return null;
113
        }
114
115
        $content = $this->readfile($file_path);
116
        $object = $this->decode($content);
117
118
        if ($this->driverUnwrapTime($object)->getTimestamp() < time()) {
119
            @unlink($file_path);
0 ignored issues
show
Security File Manipulation introduced by
$file_path can contain request data and is used in file manipulation 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...
120
121
            return null;
122
        }
123
124
        return $object;
125
126
    }
127
128
    /**
129
     * @param \Psr\Cache\CacheItemInterface $item
130
     * @return bool
131
     * @throws \InvalidArgumentException
132
     */
133
    public function driverDelete(CacheItemInterface $item)
134
    {
135
        /**
136
         * Check for Cross-Driver type confusion
137
         */
138
        if ($item instanceof Item) {
139
            $file_path = $this->getFilePath($item->getKey(), true);
140
            if (file_exists($file_path) && @unlink($file_path)) {
0 ignored issues
show
Security File Manipulation introduced by
$file_path can contain request data and is used in file manipulation 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...
141
                return true;
142
            } else {
143
                return false;
144
            }
145
        } else {
146
            throw new \InvalidArgumentException('Cross-Driver type confusion detected');
147
        }
148
    }
149
150
    /**
151
     * @return bool
152
     */
153
    public function driverClear()
154
    {
155
        $return = null;
156
        $path = $this->getPath();
157
        $dir = @opendir($path);
0 ignored issues
show
Security File Exposure introduced by
$path 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...
158
        if (!$dir) {
159
            throw new phpFastCacheDriverException("Can't read PATH:" . $path);
160
        }
161
162
        while ($file = readdir($dir)) {
163
            if ($file != '.' && $file != '..' && is_dir($path . '/' . $file)) {
164
                // read sub dir
165
                $subdir = @opendir($path . '/' . $file);
0 ignored issues
show
Security File Exposure introduced by
$path . '/' . $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...
166
                if (!$subdir) {
167
                    throw new phpFastCacheDriverException("Can't read path:" . $path . '/' . $file);
168
                }
169
170
                while ($subdirFile = readdir($subdir)) {
171
                    if ($subdirFile != '.' && $subdirFile != '..') {
172
                        $file_path = $path . '/' . $file . '/' . $subdirFile;
173
                        $result = @unlink($file_path);
0 ignored issues
show
Security File Manipulation introduced by
$file_path can contain request data and is used in file manipulation 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...
174
                        if ($return !== false) {
175
                            $return = $result;
176
                        }
177
                    }
178
                }
179
            }
180
        }
181
182
        return (bool) $return;
183
    }
184
185
    /**
186
     * @return bool
187
     */
188
    public function driverConnect()
189
    {
190
        return true;
191
    }
192
193
    /**
194
     * @param \Psr\Cache\CacheItemInterface $item
195
     * @return bool
196
     * @throws \InvalidArgumentException
197
     */
198
    public function driverIsHit(CacheItemInterface $item)
199
    {
200
        $file_path = $this->getFilePath($item->getKey(), true);
201
        if (!file_exists($file_path)) {
202
            return false;
203
        } else {
204
            // check expired or not
205
            $value = $this->driverRead($item->getKey());
0 ignored issues
show
Documentation introduced by
$item->getKey() is of type string, but the function expects a object<Psr\Cache\CacheItemInterface>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
206
207
            return !($value == null);
208
        }
209
    }
210
211
    /**
212
     * @param string $optionName
213
     * @param mixed $optionValue
214
     * @return bool
215
     * @throws \InvalidArgumentException
216
     */
217
    public static function isValidOption($optionName, $optionValue)
218
    {
219
        parent::isValidOption($optionName, $optionValue);
220
        switch($optionName)
221
        {
222
            case 'path':
223
                return is_string($optionValue);
224
                break;
1 ignored issue
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
225
226
            case 'default_chmod':
227
                return is_numeric($optionValue);
228
                break;
1 ignored issue
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
229
230
            case 'securityKey':
231
                return is_string($optionValue);
232
                break;
1 ignored issue
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
233
            case 'htaccess':
234
                return is_bool($optionValue);
235
                break;
1 ignored issue
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
236
            default:
237
                return false;
238
            break;
1 ignored issue
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
239
        }
240
    }
241
    /**
242
     * @return array
243
     */
244
    public static function getValidOptions()
245
    {
246
        return ['path', 'default_chmod', 'securityKey', 'htaccess'];
247
    }
248
249
    /**
250
     * @return array
251
     */
252
    public static function getRequiredOptions()
253
    {
254
        return ['path'];
255
    }
256
257
    /********************
258
     *
259
     * PSR-6 Extended Methods
260
     *
261
     *******************/
262
263
    /**
264
     * @return driverStatistic
265
     * @throws \phpFastCache\Exceptions\phpFastCacheCoreException
266
     * @throws \phpFastCache\Exceptions\phpFastCacheDriverException
267
     */
268
    public function getStats()
269
    {
270
        $stat = new driverStatistic();
271
272
        $path = $this->getPath();
273
        $dir = @opendir($path);
0 ignored issues
show
Security File Exposure introduced by
$path 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...
274
        if (!$dir) {
275
            throw new phpFastCacheDriverException("Can't read PATH:" . $path, 94);
276
        }
277
278
        $total = 0;
279
        $removed = 0;
280
        while ($file = readdir($dir)) {
281
            if ($file != '.' && $file != '..' && is_dir($path . '/' . $file)) {
282
                // read sub dir
283
                $subdir = opendir($path . "/" . $file);
0 ignored issues
show
Security File Exposure introduced by
$path . '/' . $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...
284
                if (!$subdir) {
285
                    throw new phpFastCacheDriverException("Can't read path:" . $path . '/' . $file);
286
                }
287
288
                while ($subdirFile = readdir($subdir)) {
289
                    if ($subdirFile != '.' && $subdirFile != '..') {
290
                        $file_path = $path . '/' . $file . '/' . $subdirFile;
291
                        $size = filesize($file_path);
292
                        $object = $this->decode($this->readfile($file_path));
293
294
                        if (strpos($subdirFile, '.') === false) {
295
                            $key = $subdirFile;
296
                        } else {
297
                            $key = explode('.', $subdirFile)[ 0 ];
298
                        }
299
                        $content[ $key ] = [
0 ignored issues
show
Coding Style Comprehensibility introduced by
$content was never initialized. Although not strictly required by PHP, it is generally a good practice to add $content = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
300
                          'size' => $size,
301
                          'write_time' => (isset($object[ 'write_time' ]) ? $object[ 'write_time' ] : null),
302
                        ];
303
                        if ($object->isExpired()) {
304
                            @unlink($file_path);
0 ignored issues
show
Security File Manipulation introduced by
$file_path can contain request data and is used in file manipulation 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...
305
                            $removed += $size;
306
                        }
307
                        $total += $size;
308
                    }
309
                }
310
            }
311
        }
312
313
        $stat->setData($content)
0 ignored issues
show
Bug introduced by
The variable $content does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
314
          ->setSize($total - $removed)
315
          ->setInfo('Total [bytes]: ' . $total . ', '
316
            . 'Expired and removed [bytes]: ' . $removed . ', '
317
            . 'Current [bytes], ' . $total - $removed
318
          );
319
320
        return $stat;
321
    }
322
}