Passed
Push — v7 ( a69bc6...41e516 )
by Georges
02:04
created

IOHelperTrait::getPath()   D

Complexity

Conditions 23
Paths 208

Size

Total Lines 87
Code Lines 36

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 23
eloc 36
nc 208
nop 1
dl 0
loc 87
rs 4.2873
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
declare(strict_types=1);
15
16
namespace Phpfastcache\Core\Pool\IO;
17
18
use Phpfastcache\Core\Item\ExtendedCacheItemInterface;
19
use Phpfastcache\Core\Pool\ExtendedCacheItemPoolInterface;
20
use Phpfastcache\Entities\DriverStatistic;
21
use Phpfastcache\EventManager;
22
use Phpfastcache\Exceptions\PhpfastcacheIOException;
23
use Phpfastcache\Util\Directory;
24
use\Phpfastcache\Drivers\Files\Config;
0 ignored issues
show
Coding Style introduced by
There must be a single space after the USE keyword
Loading history...
25
26
/**
27
 * Trait IOHelperTrait
28
 * @package phpFastCache\Core\Pool\IO
29
 * @property array $config The configuration array passed via DriverBaseTrait
30
 * @property ExtendedCacheItemInterface[] $itemInstances The item instance passed via CacheItemPoolTrait
31
 * @property EventManager $eventManager The event manager passed via CacheItemPoolTrait
32
 * @method Config getConfig() Return the config object
33
 */
34
trait IOHelperTrait
35
{
36
    /**
37
     * @var array
38
     */
39
    public $tmp = [];
40
41
    /**
42
     * @param bool $readonly
43
     * @return string
44
     * @throws PhpfastcacheIOException
45
     */
46
    public function getPath($readonly = false): string
47
    {
48
        /**
49
         * Get the base system temporary directory
50
         */
51
        $tmp_dir = rtrim(ini_get('upload_tmp_dir') ?: sys_get_temp_dir(), '\\/') . DIRECTORY_SEPARATOR . 'phpfastcache';
52
53
        /**
54
         * Calculate the security key
55
         */
56
        {
57
            $securityKey = $this->getConfig()->getSecurityKey();
58
            if (!$securityKey || mb_strtolower($securityKey) === 'auto') {
59
                if (isset($_SERVER[ 'HTTP_HOST' ])) {
60
                    $securityKey = preg_replace('/^www./', '', \strtolower(\str_replace(':', '_', $_SERVER[ 'HTTP_HOST' ])));
61
                } else {
62
                    $securityKey = ($this->isPHPModule() ? 'web' : 'cli');
0 ignored issues
show
Bug introduced by
It seems like isPHPModule() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

62
                    $securityKey = ($this->/** @scrutinizer ignore-call */ isPHPModule() ? 'web' : 'cli');
Loading history...
63
                }
64
            }
65
66
            if ($securityKey !== '') {
67
                $securityKey .= '/';
68
            }
69
70
            $securityKey = static::cleanFileName($securityKey);
71
        }
72
73
        /**
74
         * Extends the temporary directory
75
         * with the security key and the driver name
76
         */
77
        $tmp_dir = rtrim($tmp_dir, '/') . DIRECTORY_SEPARATOR;
78
79
        if (empty($this->getConfig()->getPath())) {
80
            $path = $tmp_dir;
81
        } else {
82
            $path = rtrim($this->getConfig()->getPath(), '/') . DIRECTORY_SEPARATOR;
83
        }
84
85
        $path_suffix = $securityKey . DIRECTORY_SEPARATOR . $this->getDriverName();
0 ignored issues
show
Bug introduced by
It seems like getDriverName() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

85
        $path_suffix = $securityKey . DIRECTORY_SEPARATOR . $this->/** @scrutinizer ignore-call */ getDriverName();
Loading history...
86
        $full_path = Directory::getAbsolutePath($path . $path_suffix);
87
        $full_path_tmp = Directory::getAbsolutePath($tmp_dir . $path_suffix);
88
        $full_path_hash = \md5($full_path);
89
90
        /**
91
         * In readonly mode we only attempt
92
         * to verify if the directory exists
93
         * or not, if it does not then we
94
         * return the temp dir
95
         */
96
        if ($readonly === true) {
97
            if ($this->getConfig()->isAutoTmpFallback() && (!@\file_exists($full_path) || !@\is_writable($full_path))) {
98
                return $full_path_tmp;
99
            }
100
            return $full_path;
101
        }
102
103
        if (!isset($this->tmp[ $full_path_hash ]) || (!@\file_exists($full_path) || !@\is_writable($full_path))) {
104
            if (!@\file_exists($full_path)) {
105
                @mkdir($full_path, $this->getDefaultChmod(), true);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for mkdir(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

105
                /** @scrutinizer ignore-unhandled */ @mkdir($full_path, $this->getDefaultChmod(), true);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
106
            } else if (!@\is_writable($full_path)) {
107
                if (!@chmod($full_path, $this->getDefaultChmod()) && $this->getConfig()->isAutoTmpFallback()) {
108
                    /**
109
                     * Switch back to tmp dir
110
                     * again if the path is not writable
111
                     */
112
                    $full_path = $full_path_tmp;
113
                    if (!@\file_exists($full_path)) {
114
                        @mkdir($full_path, $this->getDefaultChmod(), true);
115
                    }
116
                }
117
            }
118
119
            /**
120
             * In case there is no directory
121
             * writable including the temporary
122
             * one, we must throw an exception
123
             */
124
            if (!@\file_exists($full_path) || !@\is_writable($full_path)) {
125
                throw new PhpfastcacheIOException('Path "' . $full_path . '" is not writable, please set a chmod 0777 or any writable permission and make sure to make use of an absolute path !');
126
            }
127
128
            $this->tmp[ $full_path_hash ] = $full_path;
129
            $this->htaccessGen($full_path, \array_key_exists('htaccess', $this->getConfig()) ? $this->getConfig()->getHtaccess() : false);
0 ignored issues
show
Bug introduced by
$this->getConfig() of type Phpfastcache\Drivers\Files\Config is incompatible with the type array expected by parameter $search of array_key_exists(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

129
            $this->htaccessGen($full_path, \array_key_exists('htaccess', /** @scrutinizer ignore-type */ $this->getConfig()) ? $this->getConfig()->getHtaccess() : false);
Loading history...
130
        }
131
132
        return realpath($full_path);
133
    }
134
135
136
    /**
137
     * @param $keyword
138
     * @param bool $skip
139
     * @return string
140
     * @throws PhpfastcacheIOException
141
     */
142
    protected function getFilePath($keyword, $skip = false): string
143
    {
144
        $path = $this->getPath();
145
146
        if ($keyword === false) {
147
            return $path;
148
        }
149
150
        $filename = $this->encodeFilename($keyword);
151
        $folder = \substr($filename, 0, 2) . DIRECTORY_SEPARATOR . \substr($filename, 2, 2);
152
        $path = rtrim($path, '/\\') . DIRECTORY_SEPARATOR . $folder;
153
154
        /**
155
         * Skip Create Sub Folders;
156
         */
157
        if (!$skip) {
158
            if (!\file_exists($path)) {
159
                if (@!\mkdir($path, $this->getDefaultChmod(), true)) {
160
                    throw new PhpfastcacheIOException('PLEASE CHMOD ' . $path . ' - ' . $this->getDefaultChmod() . ' OR ANY WRITABLE PERMISSION!');
161
                }
162
            }
163
        }
164
165
        return $path . '/' . $filename . '.' . $this->getConfig()->getCacheFileExtension();
166
    }
167
168
169
    /**
170
     * @param $keyword
171
     * @return string
172
     */
173
    protected function encodeFilename($keyword): string
174
    {
175
        return \md5($keyword);
176
    }
177
178
    /**
179
     * @return int
180
     */
181
    protected function getDefaultChmod(): int
182
    {
183
        if (!$this->getConfig()->getDefaultChmod()) {
184
            return 0777;
185
        }
186
187
        return $this->getConfig()->getDefaultChmod();
188
    }
189
190
    /**
191
     * @param $filename
192
     * @return string
193
     */
194
    protected static function cleanFileName($filename): string
195
    {
196
        $regex = [
197
          '/[\?\[\]\/\\\=\<\>\:\;\,\'\"\&\$\#\*\(\)\|\~\`\!\{\}]/',
198
          '/\.$/',
199
          '/^\./',
200
        ];
201
        $replace = ['-', '', ''];
202
203
        return \trim(preg_replace($regex, $replace, \trim($filename)), '-');
204
    }
205
206
    /**
207
     * @param $path
208
     * @param bool $create
209
     * @throws PhpfastcacheIOException
210
     */
211
    protected function htaccessGen($path, $create = true)
212
    {
213
        if ($create === true) {
214
            if (!\is_writable($path)) {
215
                try {
216
                    if (!\chmod($path, 0777)) {
217
                        throw new PhpfastcacheIOException('Chmod failed on : ' . $path);
218
                    }
219
                } catch (PhpfastcacheIOException $e) {
220
                    throw new PhpfastcacheIOException('PLEASE CHMOD ' . $path . ' - 0777 OR ANY WRITABLE PERMISSION!', 0, $e);
221
                }
222
            }
223
224
            if (!\file_exists($path . "/.htaccess")) {
225
                $content = <<<HTACCESS
226
### This .htaccess is auto-generated by PhpFastCache ###
227
<IfModule mod_authz_host>
228
Require all denied
229
</IfModule>
230
<IfModule !mod_authz_host>
231
Order Allow,Deny
232
Deny from all
233
</IfModule>
234
HTACCESS;
235
236
                $file = @\fopen($path . '/.htaccess', 'w+');
237
                if (!$file) {
0 ignored issues
show
introduced by
$file is of type resource|false, thus it always evaluated to false.
Loading history...
238
                    throw new PhpfastcacheIOException('PLEASE CHMOD ' . $path . ' - 0777 OR ANY WRITABLE PERMISSION!');
239
                }
240
                \fwrite($file, $content);
241
                \fclose($file);
242
            }
243
        }
244
    }
245
246
247
    /**
248
     * @param $file
249
     * @return string
250
     * @throws PhpfastcacheIOException
251
     */
252
    protected function readfile($file): string
253
    {
254
        if (\function_exists('file_get_contents')) {
255
            return \file_get_contents($file);
256
        }
257
258
        $string = '';
259
260
        $file_handle = @\fopen($file, 'r');
261
        if (!$file_handle) {
0 ignored issues
show
introduced by
$file_handle is of type resource|false, thus it always evaluated to false.
Loading history...
262
            throw new PhpfastcacheIOException("Cannot read file located at: {$file}");
263
        }
264
        while (!\feof($file_handle)) {
265
            $line = \fgets($file_handle);
266
            $string .= $line;
267
        }
268
        \fclose($file_handle);
269
270
        return $string;
271
    }
272
273
    /**
274
     * @param string $file
275
     * @param string $data
276
     * @param bool $secureFileManipulation
277
     * @return bool
278
     * @throws PhpfastcacheIOException
279
     */
280
    protected function writefile($file, $data, $secureFileManipulation = false): bool
281
    {
282
        /**
283
         * @eventName CacheWriteFileOnDisk
284
         * @param ExtendedCacheItemPoolInterface $this
285
         * @param string $file
286
         * @param bool $secureFileManipulation
287
         *
288
         */
289
        $this->eventManager->dispatch('CacheWriteFileOnDisk', $this, $file, $secureFileManipulation);
0 ignored issues
show
Bug introduced by
$file of type string is incompatible with the type array expected by parameter $args of phpFastCache\EventManager::dispatch(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

289
        $this->eventManager->dispatch('CacheWriteFileOnDisk', $this, /** @scrutinizer ignore-type */ $file, $secureFileManipulation);
Loading history...
Bug introduced by
$secureFileManipulation of type boolean is incompatible with the type array expected by parameter $args of phpFastCache\EventManager::dispatch(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

289
        $this->eventManager->dispatch('CacheWriteFileOnDisk', $this, $file, /** @scrutinizer ignore-type */ $secureFileManipulation);
Loading history...
Bug introduced by
$this of type Phpfastcache\Core\Pool\IO\IOHelperTrait is incompatible with the type array expected by parameter $args of phpFastCache\EventManager::dispatch(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

289
        $this->eventManager->dispatch('CacheWriteFileOnDisk', /** @scrutinizer ignore-type */ $this, $file, $secureFileManipulation);
Loading history...
290
291
        if ($secureFileManipulation) {
292
            $tmpFilename = Directory::getAbsolutePath(\dirname($file) . '/tmp_' . \md5(
293
                \str_shuffle(\uniqid($this->getDriverName(), false))
294
                . \str_shuffle(\uniqid($this->getDriverName(), false))
295
              ));
296
297
            $f = \fopen($tmpFilename, 'w+');
298
            \flock($f, LOCK_EX);
0 ignored issues
show
Bug introduced by
It seems like $f can also be of type false; however, parameter $handle of flock() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

298
            \flock(/** @scrutinizer ignore-type */ $f, LOCK_EX);
Loading history...
299
            $octetWritten = fwrite($f, $data);
0 ignored issues
show
Bug introduced by
It seems like $f can also be of type false; however, parameter $handle of fwrite() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

299
            $octetWritten = fwrite(/** @scrutinizer ignore-type */ $f, $data);
Loading history...
300
            \flock($f, LOCK_UN);
301
            \fclose($f);
0 ignored issues
show
Bug introduced by
It seems like $f can also be of type false; however, parameter $handle of fclose() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

301
            \fclose(/** @scrutinizer ignore-type */ $f);
Loading history...
302
303
            if (!\rename($tmpFilename, $file)) {
304
                throw new PhpfastcacheIOException(\sprintf('Failed to rename %s to %s', $tmpFilename, $file));
305
            }
306
        } else {
307
            $f = \fopen($file, 'w+');
308
            $octetWritten = \fwrite($f, $data);
309
            \fclose($f);
310
        }
311
312
        return $octetWritten !== false;
313
    }
314
315
    /********************
316
     *
317
     * PSR-6 Extended Methods
318
     *
319
     *******************/
320
321
    /**
322
     * Provide a generic getStats() method
323
     * for files-based drivers
324
     * @return DriverStatistic
325
     * @throws \Phpfastcache\Exceptions\PhpfastcacheIOException
326
     */
327
    public function getStats(): DriverStatistic
328
    {
329
        $stat = new DriverStatistic();
330
        $path = $this->getFilePath(false);
331
332
        if (!\is_dir($path)) {
333
            throw new PhpfastcacheIOException("Can't read PATH:" . $path);
334
        }
335
336
        $stat->setData(\implode(', ', \array_keys($this->itemInstances)))
337
          ->setRawData([
338
            'tmp' => $this->tmp,
339
          ])
340
          ->setSize(Directory::dirSize($path))
341
          ->setInfo('Number of files used to build the cache: ' . Directory::getFileCount($path));
342
343
        return $stat;
344
    }
345
}