XoopsCacheFile   A
last analyzed

Complexity

Total Complexity 42

Size/Duplication

Total Lines 268
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 42
eloc 104
dl 0
loc 268
rs 9.0399
c 0
b 0
f 0

8 Methods

Rating   Name   Duplication   Size   Complexity  
A gc() 0 3 1
A delete() 0 7 3
A active() 0 10 4
A setKey() 0 10 2
B clear() 0 33 9
B write() 0 39 9
A init() 0 21 3
B read() 0 33 11

How to fix   Complexity   

Complex Class

Complex classes like XoopsCacheFile 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.

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 XoopsCacheFile, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Cache engine For XOOPS
4
 *
5
 * You may not change or alter any portion of this comment or credits
6
 * of supporting developers from this source code or any supporting source code
7
 * which is considered copyrighted (c) material of the original comment or credit authors.
8
 * This program is distributed in the hope that it will be useful,
9
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11
 *
12
 * @copyright       (c) 2000-2021 XOOPS Project (https://xoops.org)
13
 * @license             GNU GPL 2 (https://www.gnu.org/licenses/gpl-2.0.html)
14
 * @package             class
15
 * @subpackage          cache
16
 * @since               2.3.0
17
 * @author              Taiwen Jiang <[email protected]>
18
 */
19
defined('XOOPS_ROOT_PATH') || exit('Restricted access');
20
21
/**
22
 * File Storage engine for cache
23
 *
24
 *
25
 * PHP versions 4 and 5
26
 *
27
 * CakePHP(tm) :  Rapid Development Framework <https://www.cakephp.org/>
28
 * Copyright 2005-2008, Cake Software Foundation, Inc.
29
 *                                       1785 E. Sahara Avenue, Suite 490-204
30
 *                                       Las Vegas, Nevada 89104
31
 *
32
 * Licensed under The MIT License
33
 * Redistributions of files must retain the above copyright notice.
34
 *
35
 * @filesource
36
 * @copyright  Copyright 2005-2008, Cake Software Foundation, Inc.
37
 * @link       https://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
38
 * @package    cake
39
 * @subpackage cake.cake.libs.cache
40
 * @since      CakePHP(tm) v 1.2.0.4933
41
 * @modifiedby $LastChangedBy$
42
 * @lastmodified $Date$
43
 * @license    https://www.opensource.org/licenses/mit-license.php The MIT License
44
 */
45
46
/**
47
 * File Storage engine for cache
48
 *
49
 * @todo       use the File and Folder classes (if it's not a too big performance hit)
50
 * @package    cake
51
 * @subpackage cake.cake.libs.cache
52
 */
53
class XoopsCacheFile extends XoopsCacheEngine
54
{
55
    /**
56
     * Instance of File class
57
     *
58
     * @var object
59
     * @access private
60
     */
61
    private $file;
62
63
    /**
64
     * settings
65
     *                path = absolute path to cache directory, default => CACHE
66
     *                prefix = string prefix for filename, default => xoops_
67
     *                lock = enable file locking on write, default => false
68
     *                serialize = serialize the data, default => false
69
     *
70
     * @var array
71
     * @see    CacheEngine::__defaults
72
     * @access public
73
     */
74
    public $settings = array();
75
76
    /**
77
     * Set to true if FileEngine::init(); and FileEngine::active(); do not fail.
78
     *
79
     * @var boolean
80
     * @access private
81
     */
82
    private $active = false;
83
84
    /**
85
     * True unless FileEngine::active(); fails
86
     *
87
     * @var boolean
88
     * @access private
89
     */
90
    private $init = true;
91
92
    /**
93
     * Initialize the Cache Engine
94
     *
95
     * Called automatically by the cache frontend
96
     * To reinitialize the settings call Cache::engine('EngineName', [optional] settings = array());
97
     *
98
     * @param  array $settings array of setting for the engine
99
     * @return boolean True if the engine has been successfully initialized, false if not
100
     * @access   public
101
     */
102
    public function init($settings = array())
103
    {
104
        parent::init($settings);
105
        $defaults       = array(
106
            'path'      => XOOPS_VAR_PATH . '/caches/xoops_cache',
107
            'extension' => '.php',
108
            'prefix'    => 'xoops_',
109
            'lock'      => false,
110
            'serialize' => false,
111
            'duration'  => 31556926);
112
        $this->settings = array_merge($defaults, $this->settings);
113
        if (!isset($this->file)) {
114
            XoopsLoad::load('XoopsFile');
115
            $this->file = XoopsFile::getHandler('file', $this->settings['path'] . '/index.php', true);
116
        }
117
        $this->settings['path'] = $this->file->folder->cd($this->settings['path']);
118
        if (empty($this->settings['path'])) {
119
            return false;
120
        }
121
122
        return $this->active();
123
    }
124
125
    /**
126
     * Garbage collection. Permanently remove all expired and deleted data
127
     *
128
     * @return boolean True if garbage collection was successful, false on failure
129
     * @access public
130
     */
131
    public function gc()
132
    {
133
        return $this->clear(true);
134
    }
135
136
    /**
137
     * Write data for key into cache
138
     *
139
     * @param  string $key      Identifier for the data
140
     * @param  mixed  $data     Data to be cached
141
     * @param  mixed  $duration How long to cache the data, in seconds
142
     * @return boolean True if the data was successfully cached, false on failure
143
     * @access public
144
     */
145
    public function write($key, $data = null, $duration = null)
146
    {
147
        if (!isset($data) || !$this->init) {
148
            return false;
149
        }
150
151
        if ($this->setKey($key) === false) {
152
            return false;
153
        }
154
155
        if ($duration == null) {
156
            $duration = $this->settings['duration'];
157
        }
158
        $windows   = false;
159
        $lineBreak = "\n";
160
161
        if (substr(PHP_OS, 0, 3) === 'WIN') {
162
            $lineBreak = "\r\n";
163
            $windows   = true;
164
        }
165
        $expires = time() + $duration;
166
        if (!empty($this->settings['serialize'])) {
167
            if ($windows) {
168
                $data = str_replace('\\', '\\\\\\\\', serialize($data));
169
            } else {
170
                $data = serialize($data);
171
            }
172
            $contents = $expires . $lineBreak . $data . $lineBreak;
173
        } else {
174
            $contents = $expires . $lineBreak . 'return ' . var_export($data, true) . ';' . $lineBreak;
175
        }
176
177
        if ($this->settings['lock']) {
178
            $this->file->lock = true;
179
        }
180
        $success = $this->file->write($contents);
181
        $this->file->close();
182
183
        return $success;
184
    }
185
186
    /**
187
     * Read a key from the cache
188
     *
189
     * @param  string $key Identifier for the data
190
     * @return mixed  The cached data, or false if the data doesn't exist, has expired, or if there was an error fetching it
191
     * @access public
192
     */
193
    public function read($key)
194
    {
195
        if ($this->setKey($key) === false || !$this->init) {
196
            return false;
197
        }
198
        if ($this->settings['lock']) {
199
            $this->file->lock = true;
200
        }
201
        $cachetime = $this->file->read(11);
202
203
        if ($cachetime !== false && (int)$cachetime < time()) {
204
            $this->file->close();
205
            $this->file->delete();
206
207
            return false;
208
        }
209
210
        $data = $this->file->read(true);
211
        if (!empty($data) && !empty($this->settings['serialize'])) {
212
            $data = stripslashes($data);
213
            // $data = preg_replace('!s:(\d+):"(.*?)";!se', "'s:'.strlen('$2').':\"$2\";'", $data);
214
            $data = preg_replace_callback('!s:(\d+):"(.*?)";!s', function ($m) { return 's:' . strlen($m[2]) . ':"' . $m[2] . '";'; }, $data);
215
            $data = unserialize($data, array('allowed_classes' => false));
216
            if (is_array($data)) {
217
                XoopsLoad::load('XoopsUtility');
218
                $data = XoopsUtility::recursive('stripslashes', $data);
219
            }
220
        } elseif ($data && empty($this->settings['serialize'])) {
221
            $data = eval($data);
0 ignored issues
show
introduced by
The use of eval() is discouraged.
Loading history...
222
        }
223
        $this->file->close();
224
225
        return $data;
226
    }
227
228
    /**
229
     * Delete a key from the cache
230
     *
231
     * @param  string $key Identifier for the data
232
     * @return boolean True if the value was successfully deleted, false if it didn't exist or couldn't be removed
233
     * @access public
234
     */
235
    public function delete($key)
236
    {
237
        if ($this->setKey($key) === false || !$this->init) {
238
            return false;
239
        }
240
241
        return $this->file->delete();
242
    }
243
244
    /**
245
     * Delete all values from the cache
246
     *
247
     * @param  boolean $check Optional - only delete expired cache items
248
     * @return boolean True if the cache was successfully cleared, false otherwise
249
     * @access public
250
     */
251
    public function clear($check = true)
252
    {
253
        if (!$this->init) {
254
            return false;
255
        }
256
        $dir = dir($this->settings['path']);
257
        if ($check) {
258
            $now       = time();
259
            $threshold = $now - $this->settings['duration'];
260
        }
261
        while (($entry = $dir->read()) !== false) {
262
            if ($this->setKey(str_replace($this->settings['prefix'], '', $entry)) === false) {
263
                continue;
264
            }
265
            if ($check) {
266
                $mtime = $this->file->lastChange();
267
268
                if ($mtime === false || $mtime > $threshold) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $threshold does not seem to be defined for all execution paths leading up to this point.
Loading history...
269
                    continue;
270
                }
271
272
                $expires = $this->file->read(11);
273
                $this->file->close();
274
275
                if ($expires > $now) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $now does not seem to be defined for all execution paths leading up to this point.
Loading history...
276
                    continue;
277
                }
278
            }
279
            $this->file->delete();
280
        }
281
        $dir->close();
282
283
        return true;
284
    }
285
286
    /**
287
     * Get absolute file for a given key
288
     *
289
     * @param  string $key The key
290
     * @return mixed  Absolute cache file for the given key or false if erroneous
291
     * @access private
292
     */
293
    private function setKey($key)
294
    {
295
        $this->file->folder->cd($this->settings['path']);
296
        $this->file->name   = $this->settings['prefix'] . $key . $this->settings['extension'];
297
        $this->file->handle = null;
298
        $this->file->info   = null;
299
        if (!$this->file->folder->inPath($this->file->pwd(), true)) {
300
            return false;
301
        }
302
        return null;
303
    }
304
305
    /**
306
     * Determine is cache directory is writable
307
     *
308
     * @return boolean
309
     * @access private
310
     */
311
    private function active()
312
    {
313
        if (!$this->active && $this->init && !is_writable($this->settings['path'])) {
314
            $this->init = false;
315
            trigger_error(sprintf('%s is not writable', $this->settings['path']), E_USER_WARNING);
316
        } else {
317
            $this->active = true;
318
        }
319
320
        return true;
321
    }
322
}
323