Passed
Pull Request — master (#1596)
by Michael
09:35
created

XoopsCache::delete()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 20
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 11
dl 0
loc 20
rs 9.9
c 0
b 0
f 0
cc 3
nc 3
nop 2
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-2025 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
20
defined('XOOPS_ROOT_PATH') || exit('Restricted access');
21
22
/**
23
 * Caching for CakePHP.
24
 *
25
 * @package    cake
26
 * @subpackage cake.cake.libs
27
 */
28
class XoopsCache
29
{
30
    /**
31
     * Cache engine to use
32
     *
33
     * @var object
34
     * @access protected
35
     */
36
37
    protected $engine;
38
39
    /**
40
     * Cache configuration stack
41
     *
42
     * @var array
43
     * @access private
44
     */
45
    private $configs = [];
46
47
    /**
48
     * Holds name of the current configuration being used
49
     *
50
     * @var array
51
     * @access private
52
     */
53
    private $name;
54
55
    /**
56
     * XoopsCache::__construct()
57
     */
58
    public function __construct() {}
59
60
    /**
61
     * Returns a singleton instance
62
     *
63
     * @return object
64
     * @access public
65
     */
66
    public static function getInstance()
67
    {
68
        static $instance;
69
        if (!isset($instance)) {
70
            $class    = self::class;
71
            $instance = new $class();
72
        }
73
74
        return $instance;
75
    }
76
77
    /**
78
     * Tries to find and include a file for a cache engine and returns object instance
79
     *
80
     * @param  string $name Name of the engine
81
     * @return mixed $engine object or null
82
     * @access private
83
     */
84
    private function loadEngine($name)
85
    {
86
        if (!class_exists('XoopsCache' . ucfirst($name))) {
87
            if (file_exists($file = __DIR__ . '/' . strtolower($name) . '.php')) {
88
                include $file;
89
            } else {
90
                trigger_error('File :' . $file . ' not found in file : ' . __FILE__ . ' at line: ' . __LINE__, E_USER_WARNING);
91
92
                return false;
93
            }
94
        }
95
96
        return true;
97
    }
98
99
    /**
100
     * Set the cache configuration to use
101
     *
102
     * @param  string|array $name     Name of the configuration
103
     * @param  array  $settings Optional associative array of settings passed to the engine
104
     * @return array|false  (engine, settings) on success, false on failure
105
     * @access public
106
     */
107
    public function config($name = 'default', $settings = [])
108
    {
109
        $_this = XoopsCache::getInstance();
110
111
        if (is_array($name)) {
112
            $config = $name;
113
114
            if (isset($config['name']) && is_string($config['name'])) {
115
                $name = $config['name'];
116
            }
117
118
            if (isset($config['settings']) && is_array($config['settings'])) {
119
                $settings = array_merge($settings, $config['settings']);
120
            }
121
        }
122
123
        if (!is_string($name) || $name === '') {
124
            $name = 'default';
125
        }
126
127
        if (isset($_this->configs[$name])) {
128
            $settings = array_merge($_this->configs[$name], $settings);
129
        } elseif (!empty($settings)) {
130
            $_this->configs[$name] = $settings;
131
        } elseif (
132
            $_this->configs !== null
133
            && is_string($_this->name)
134
            && $_this->name !== ''
135
            && isset($_this->configs[$_this->name])
136
        ) {
137
            $name     = $_this->name;
138
            $settings = $_this->configs[$_this->name];
139
        } else {
140
            $name = 'default';
141
            if (!empty($_this->configs['default'])) {
142
                $settings = $_this->configs['default'];
143
            } else {
144
                $settings = [
145
                    'engine' => 'file',
146
                ];
147
            }
148
        }
149
150
        $engine = !empty($settings['engine']) ? $settings['engine'] : 'file';
151
152
        if ($name !== $_this->name) {
153
            if ($_this->engine($engine, $settings) === false) {
154
                trigger_error("Cache Engine {$engine} is not set", E_USER_WARNING);
155
156
                return false;
157
            }
158
            $_this->name           = $name;
159
            $_this->configs[$name] = $_this->settings($engine);
160
        }
161
162
        $settings = $_this->configs[$name];
163
164
        return compact('engine', 'settings');
165
    }
166
167
    /**
168
     * Set the cache engine to use or modify settings for one instance
169
     *
170
     * @param  string $name     Name of the engine (without 'Engine')
171
     * @param  array  $settings Optional associative array of settings passed to the engine
172
     * @return boolean True on success, false on failure
173
     * @access public
174
     */
175
    public function engine($name = 'file', $settings = [])
176
    {
177
        if (!$name) {
178
            return false;
179
        }
180
181
        $cacheClass = 'XoopsCache' . ucfirst($name);
182
        $_this      = XoopsCache::getInstance();
183
        if (!isset($_this->engine[$name])) {
184
            if ($_this->loadEngine($name) === false) {
185
                trigger_error("Cache Engine {$name} is not loaded", E_USER_WARNING);
186
187
                return false;
188
            }
189
            $_this->engine[$name] = new $cacheClass();
190
        }
191
192
        if ($_this->engine[$name]->init($settings)) {
193
            if (time() % $_this->engine[$name]->settings['probability'] == 0) {
194
                $_this->engine[$name]->gc();
195
            }
196
197
            return true;
198
        }
199
        $_this->engine[$name] = null;
200
        trigger_error("Cache Engine {$name} is not initialized", E_USER_WARNING);
201
202
        return false;
203
    }
204
205
    /**
206
     * Garbage collection
207
     *
208
     * Permanently remove all expired and deleted data
209
     *
210
     * @access public
211
     */
212
    public function gc()
213
    {
214
        $_this  = XoopsCache::getInstance();
215
        $config = $_this->config();
216
        extract($config);
217
        $_this->engine[$engine]->gc();
218
    }
219
220
    /**
221
     * Write data for key into cache
222
     *
223
     * @param  string $key       Identifier for the data
224
     * @param  mixed  $value     Data to be cached - anything except a resource
225
     * @param  mixed  $duration  Optional - string configuration name OR how long to cache the data, either in seconds or a
226
     *                           string that can be parsed by the strtotime() function OR array('config' => 'default', 'duration' => '3600')
227
     * @return boolean True if the data was successfully cached, false on failure
228
     * @access public
229
     */
230
    public static function write($key, $value, $duration = null)
231
    {
232
        $key    = substr(md5(XOOPS_URL), 0, 8) . '_' . $key;
233
        $_this  = XoopsCache::getInstance();
234
235
        $config = null;
236
237
        if (is_array($duration)) {
238
            if (isset($duration['config']) && is_string($duration['config'])) {
239
                $config = $duration['config'];
240
            }
241
            if (isset($duration['duration'])) {
242
                $duration = $duration['duration'];
243
            } else {
244
                $duration = null;
245
            }
246
        } elseif (
247
            is_string($duration)
248
            && $duration !== ''
249
            && isset($_this->configs[$duration])
250
        ) {
251
            $config   = $duration;
252
            $duration = null;
253
        }
254
255
        $config = $_this->config($config);
256
257
258
        if (!is_array($config)) {
259
            return null;
260
        }
261
        extract($config);
262
263
        if (!$_this->isInitialized($engine)) {
264
            trigger_error('Cache write not initialized: ' . $engine);
265
266
            return false;
267
        }
268
269
        if (!$key = $_this->key($key)) {
270
            return false;
271
        }
272
273
        if (is_resource($value)) {
274
            return false;
275
        }
276
277
        if (!$duration) {
278
            $duration = $settings['duration'];
279
        }
280
        $duration = is_numeric($duration) ? (int) $duration : strtotime($duration) - time();
281
282
        if ($duration < 1) {
283
            return false;
284
        }
285
        $_this->engine[$engine]->init($settings);
286
        $success = $_this->engine[$engine]->write($key, $value, $duration);
287
288
        return $success;
289
    }
290
291
    /**
292
     * Read a key from the cache
293
     *
294
     * @param  string $key    Identifier for the data
295
     * @param  string|array $config name of the configuration to use
296
     * @return mixed  The cached data, or false if the data doesn't exist, has expired, or if there was an error fetching it
297
     * @access public
298
     */
299
    public static function read($key, $config = null)
300
    {
301
        $key    = substr(md5(XOOPS_URL), 0, 8) . '_' . $key;
302
        $_this  = XoopsCache::getInstance();
303
        $config = $_this->config($config);
304
305
        if (!is_array($config)) {
306
            return null;
307
        }
308
309
        extract($config);
310
311
        if (!$_this->isInitialized($engine)) {
312
            return false;
313
        }
314
        if (!$key = $_this->key($key)) {
315
            return false;
316
        }
317
        $_this->engine[$engine]->init($settings);
318
        $success = $_this->engine[$engine]->read($key);
319
320
        return $success;
321
    }
322
323
    /**
324
     * Delete a key from the cache
325
     *
326
     * @param  string $key    Identifier for the data
327
     * @param string|null $config name of the configuration to use
328
     * @return boolean True if the value was successfully deleted, false if it didn't exist or couldn't be removed
329
     * @access public
330
     */
331
    public static function delete($key, $config = null)
332
    {
333
        $key   = substr(md5(XOOPS_URL), 0, 8) . '_' . $key;
334
        $_this = XoopsCache::getInstance();
335
336
        $config = $_this->config($config);
337
        extract($config);
338
339
        if (!$_this->isInitialized($engine)) {
340
            return false;
341
        }
342
343
        if (!$key = $_this->key($key)) {
344
            return false;
345
        }
346
347
        $_this->engine[$engine]->init($settings);
348
        $success = $_this->engine[$engine]->delete($key);
349
350
        return $success;
351
    }
352
353
    /**
354
     * Delete all keys from the cache
355
     *
356
     * @param  boolean $check  if true will check expiration, otherwise delete all
357
     * @param string|null $config name of the configuration to use
358
     * @return boolean True if the cache was successfully cleared, false otherwise
359
     * @access public
360
     */
361
    public function clear($check = false, $config = null)
362
    {
363
        $_this  = XoopsCache::getInstance();
364
        $config = $_this->config($config);
365
        extract($config);
366
367
        if (!$_this->isInitialized($engine)) {
368
            return false;
369
        }
370
        $success = $_this->engine[$engine]->clear($check);
371
        $_this->engine[$engine]->init($settings);
372
373
        return $success;
374
    }
375
376
    /**
377
     * Check if Cache has initialized a working storage engine
378
     *
379
     * @param  string|null $engine Name of the engine
380
     * @return bool
381
     * @internal param string $configs Name of the configuration setting
382
     * @access   public
383
     */
384
    public function isInitialized($engine = null)
385
    {
386
        $_this = XoopsCache::getInstance();
387
        if (!$engine && isset($_this->configs[$_this->name]['engine'])) {
388
            $engine = $_this->configs[$_this->name]['engine'];
389
        }
390
391
        return isset($_this->engine[$engine]);
392
    }
393
394
    /**
395
     * Return the settings for current cache engine
396
     *
397
     * @param  string|null $engine Name of the engine
398
     * @return array  list of settings for this engine
399
     * @access public
400
     */
401
    public function settings($engine = null)
402
    {
403
        $_this = XoopsCache::getInstance();
404
        if (!$engine && isset($_this->configs[$_this->name]['engine'])) {
405
            $engine = $_this->configs[$_this->name]['engine'];
406
        }
407
        if (isset($_this->engine[$engine]) && null !== $_this->engine[$engine]) {
408
            return $_this->engine[$engine]->settings();
409
        }
410
411
        return [];
412
    }
413
414
    /**
415
     * generates a safe key
416
     *
417
     * @param  string $key the key passed over
418
     * @return mixed  string $key or false
419
     * @access private
420
     */
421
    public function key($key)
422
    {
423
        if (empty($key)) {
424
            return false;
425
        }
426
        $key = str_replace(['/', '.'], '_', (string) $key);
427
428
        return $key;
429
    }
430
}
431
432
/**
433
 * Abstract class for storage engine for caching
434
 *
435
 * @package    core
436
 * @subpackage cache
437
 */
438
class XoopsCacheEngine
439
{
440
    /**
441
     * settings of current engine instance
442
     *
443
     * @var array
444
     * @access public
445
     */
446
    public $settings;
447
448
    /**
449
     * Initialize the cache engine
450
     *
451
     * Called automatically by the cache frontend
452
     *
453
     * @param  array $settings Associative array of parameters for the engine
454
     * @return boolean True if the engine has been successfully initialized, false if not
455
     * @access   public
456
     */
457
    public function init($settings = [])
458
    {
459
        $this->settings = array_merge(
460
            [
461
                'duration'    => 31556926,
462
                'probability' => 100,
463
            ],
464
            $settings,
465
        );
466
467
        return true;
468
    }
469
470
    /**
471
     * Garbage collection
472
     *
473
     * Permanently remove all expired and deleted data
474
     *
475
     * @access public
476
     */
477
    public function gc() {}
478
479
    /**
480
     * Write value for a key into cache
481
     *
482
     * @param  string $key      Identifier for the data
483
     * @param  mixed  $value    Data to be cached
484
     * @param  mixed  $duration How long to cache the data, in seconds
485
     * @return boolean True if the data was successfully cached, false on failure
486
     * @access public
487
     */
488
    public function write($key, $value, $duration = null)
489
    {
490
        trigger_error(sprintf(__('Method write() not implemented in %s', true), get_class($this)), E_USER_ERROR);
0 ignored issues
show
Bug introduced by
The function __ was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

490
        trigger_error(sprintf(/** @scrutinizer ignore-call */ __('Method write() not implemented in %s', true), get_class($this)), E_USER_ERROR);
Loading history...
491
    }
492
493
    /**
494
     * Read a key from the cache
495
     *
496
     * @param  string $key Identifier for the data
497
     * @return mixed  The cached data, or false if the data doesn't exist, has expired, or if there was an error fetching it
498
     * @access public
499
     */
500
    public function read($key)
501
    {
502
        trigger_error(sprintf(__('Method read() not implemented in %s', true), get_class($this)), E_USER_ERROR);
0 ignored issues
show
Bug introduced by
The function __ was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

502
        trigger_error(sprintf(/** @scrutinizer ignore-call */ __('Method read() not implemented in %s', true), get_class($this)), E_USER_ERROR);
Loading history...
503
    }
504
505
    /**
506
     * Delete a key from the cache
507
     *
508
     * @param  string $key Identifier for the data
509
     * @return boolean True if the value was successfully deleted, false if it didn't exist or couldn't be removed
510
     * @access public
511
     */
512
    public function delete($key) {}
513
514
    /**
515
     * Delete all keys from the cache
516
     *
517
     * @param  boolean $check if true will check expiration, otherwise delete all
518
     * @return boolean True if the cache was successfully cleared, false otherwise
519
     * @access public
520
     */
521
    public function clear($check) {}
522
523
    /**
524
     * Cache Engine settings
525
     *
526
     * @return array settings
527
     * @access public
528
     */
529
    public function settings()
530
    {
531
        return $this->settings;
532
    }
533
}
534