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

XoopsCache::isInitialized()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 4
dl 0
loc 8
rs 10
c 0
b 0
f 0
cc 3
nc 2
nop 1
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
            if (isset($name['name']) && is_string($name['name'])) {
113
                $name = $name['name'];
114
            }
115
            if (isset($name['settings']) && is_array($name['settings'])) {
116
                $settings = array_merge($settings, $name['settings']);
117
            }
118
        }
119
120
        if (!is_string($name) || $name === '') {
121
            $name = 'default';
122
        }
123
124
        if (isset($_this->configs[$name])) {
125
            $settings = array_merge($_this->configs[$name], $settings);
126
        } elseif (!empty($settings)) {
127
            $_this->configs[$name] = $settings;
128
        } elseif (
129
            $_this->configs !== null
130
            && is_string($_this->name)
131
            && $_this->name !== ''
132
            && isset($_this->configs[$_this->name])
133
        ) {
134
            $name     = $_this->name;
135
            $settings = $_this->configs[$_this->name];
136
        } else {
137
            $name = 'default';
138
            if (!empty($_this->configs['default'])) {
139
                $settings = $_this->configs['default'];
140
            } else {
141
                $settings = [
142
                    'engine' => 'file',
143
                ];
144
            }
145
        }
146
147
        $engine = !empty($settings['engine']) ? $settings['engine'] : 'file';
148
149
        if ($name !== $_this->name) {
150
            if ($_this->engine($engine, $settings) === false) {
151
                trigger_error("Cache Engine {$engine} is not set", E_USER_WARNING);
152
153
                return false;
154
            }
155
            $_this->name           = $name;
156
            $_this->configs[$name] = $_this->settings($engine);
157
        }
158
159
        $settings = $_this->configs[$name];
160
161
        return compact('engine', 'settings');
162
    }
163
164
    /**
165
     * Set the cache engine to use or modify settings for one instance
166
     *
167
     * @param  string $name     Name of the engine (without 'Engine')
168
     * @param  array  $settings Optional associative array of settings passed to the engine
169
     * @return boolean True on success, false on failure
170
     * @access public
171
     */
172
    public function engine($name = 'file', $settings = [])
173
    {
174
        if (!$name) {
175
            return false;
176
        }
177
178
        $cacheClass = 'XoopsCache' . ucfirst($name);
179
        $_this      = XoopsCache::getInstance();
180
        if (!isset($_this->engine[$name])) {
181
            if ($_this->loadEngine($name) === false) {
182
                trigger_error("Cache Engine {$name} is not loaded", E_USER_WARNING);
183
184
                return false;
185
            }
186
            $_this->engine[$name] = new $cacheClass();
187
        }
188
189
        if ($_this->engine[$name]->init($settings)) {
190
            if (time() % $_this->engine[$name]->settings['probability'] == 0) {
191
                $_this->engine[$name]->gc();
192
            }
193
194
            return true;
195
        }
196
        $_this->engine[$name] = null;
197
        trigger_error("Cache Engine {$name} is not initialized", E_USER_WARNING);
198
199
        return false;
200
    }
201
202
    /**
203
     * Garbage collection
204
     *
205
     * Permanently remove all expired and deleted data
206
     *
207
     * @access public
208
     */
209
    public function gc()
210
    {
211
        $_this  = XoopsCache::getInstance();
212
        $config = $_this->config();
213
        extract($config);
214
        $_this->engine[$engine]->gc();
215
    }
216
217
    /**
218
     * Write data for key into cache
219
     *
220
     * @param  string $key       Identifier for the data
221
     * @param  mixed  $value     Data to be cached - anything except a resource
222
     * @param  mixed  $duration  Optional - string configuration name OR how long to cache the data, either in seconds or a
223
     *                           string that can be parsed by the strtotime() function OR array('config' => 'default', 'duration' => '3600')
224
     * @return boolean True if the data was successfully cached, false on failure
225
     * @access public
226
     */
227
    public static function write($key, $value, $duration = null)
228
    {
229
        $key    = substr(md5(XOOPS_URL), 0, 8) . '_' . $key;
230
        $_this  = XoopsCache::getInstance();
231
232
        $config = null;
233
234
        if (is_array($duration)) {
235
            if (isset($duration['config']) && is_string($duration['config'])) {
236
                $config = $duration['config'];
237
            }
238
            if (isset($duration['duration'])) {
239
                $duration = $duration['duration'];
240
            } else {
241
                $duration = null;
242
            }
243
        } elseif (
244
            is_string($duration)
245
            && $duration !== ''
246
            && isset($_this->configs[$duration])
247
        ) {
248
            $config   = $duration;
249
            $duration = null;
250
        }
251
252
        $config = $_this->config($config);
253
254
255
        if (!is_array($config)) {
256
            return null;
257
        }
258
        extract($config);
259
260
        if (!$_this->isInitialized($engine)) {
261
            trigger_error('Cache write not initialized: ' . $engine);
262
263
            return false;
264
        }
265
266
        if (!$key = $_this->key($key)) {
267
            return false;
268
        }
269
270
        if (is_resource($value)) {
271
            return false;
272
        }
273
274
        if (!$duration) {
275
            $duration = $settings['duration'];
276
        }
277
        $duration = is_numeric($duration) ? (int) $duration : strtotime($duration) - time();
278
279
        if ($duration < 1) {
280
            return false;
281
        }
282
        $_this->engine[$engine]->init($settings);
283
        $success = $_this->engine[$engine]->write($key, $value, $duration);
284
285
        return $success;
286
    }
287
288
    /**
289
     * Read a key from the cache
290
     *
291
     * @param  string $key    Identifier for the data
292
     * @param  string|array $config name of the configuration to use
293
     * @return mixed  The cached data, or false if the data doesn't exist, has expired, or if there was an error fetching it
294
     * @access public
295
     */
296
    public static function read($key, $config = null)
297
    {
298
        $key    = substr(md5(XOOPS_URL), 0, 8) . '_' . $key;
299
        $_this  = XoopsCache::getInstance();
300
        $config = $_this->config($config);
301
302
        if (!is_array($config)) {
303
            return null;
304
        }
305
306
        extract($config);
307
308
        if (!$_this->isInitialized($engine)) {
309
            return false;
310
        }
311
        if (!$key = $_this->key($key)) {
312
            return false;
313
        }
314
        $_this->engine[$engine]->init($settings);
315
        $success = $_this->engine[$engine]->read($key);
316
317
        return $success;
318
    }
319
320
    /**
321
     * Delete a key from the cache
322
     *
323
     * @param  string $key    Identifier for the data
324
     * @param string|null $config name of the configuration to use
325
     * @return boolean True if the value was successfully deleted, false if it didn't exist or couldn't be removed
326
     * @access public
327
     */
328
    public static function delete($key, $config = null)
329
    {
330
        $key   = substr(md5(XOOPS_URL), 0, 8) . '_' . $key;
331
        $_this = XoopsCache::getInstance();
332
333
        $config = $_this->config($config);
334
        extract($config);
335
336
        if (!$_this->isInitialized($engine)) {
337
            return false;
338
        }
339
340
        if (!$key = $_this->key($key)) {
341
            return false;
342
        }
343
344
        $_this->engine[$engine]->init($settings);
345
        $success = $_this->engine[$engine]->delete($key);
346
347
        return $success;
348
    }
349
350
    /**
351
     * Delete all keys from the cache
352
     *
353
     * @param  boolean $check  if true will check expiration, otherwise delete all
354
     * @param string|null $config name of the configuration to use
355
     * @return boolean True if the cache was successfully cleared, false otherwise
356
     * @access public
357
     */
358
    public function clear($check = false, $config = null)
359
    {
360
        $_this  = XoopsCache::getInstance();
361
        $config = $_this->config($config);
362
        extract($config);
363
364
        if (!$_this->isInitialized($engine)) {
365
            return false;
366
        }
367
        $success = $_this->engine[$engine]->clear($check);
368
        $_this->engine[$engine]->init($settings);
369
370
        return $success;
371
    }
372
373
    /**
374
     * Check if Cache has initialized a working storage engine
375
     *
376
     * @param  string|null $engine Name of the engine
377
     * @return bool
378
     * @internal param string $configs Name of the configuration setting
379
     * @access   public
380
     */
381
    public function isInitialized($engine = null)
382
    {
383
        $_this = XoopsCache::getInstance();
384
        if (!$engine && isset($_this->configs[$_this->name]['engine'])) {
385
            $engine = $_this->configs[$_this->name]['engine'];
386
        }
387
388
        return isset($_this->engine[$engine]);
389
    }
390
391
    /**
392
     * Return the settings for current cache engine
393
     *
394
     * @param  string|null $engine Name of the engine
395
     * @return array  list of settings for this engine
396
     * @access public
397
     */
398
    public function settings($engine = null)
399
    {
400
        $_this = XoopsCache::getInstance();
401
        if (!$engine && isset($_this->configs[$_this->name]['engine'])) {
402
            $engine = $_this->configs[$_this->name]['engine'];
403
        }
404
        if (isset($_this->engine[$engine]) && null !== $_this->engine[$engine]) {
405
            return $_this->engine[$engine]->settings();
406
        }
407
408
        return [];
409
    }
410
411
    /**
412
     * generates a safe key
413
     *
414
     * @param  string $key the key passed over
415
     * @return mixed  string $key or false
416
     * @access private
417
     */
418
    public function key($key)
419
    {
420
        if (empty($key)) {
421
            return false;
422
        }
423
        $key = str_replace(['/', '.'], '_', (string) $key);
424
425
        return $key;
426
    }
427
}
428
429
/**
430
 * Abstract class for storage engine for caching
431
 *
432
 * @package    core
433
 * @subpackage cache
434
 */
435
class XoopsCacheEngine
436
{
437
    /**
438
     * settings of current engine instance
439
     *
440
     * @var array
441
     * @access public
442
     */
443
    public $settings;
444
445
    /**
446
     * Initialize the cache engine
447
     *
448
     * Called automatically by the cache frontend
449
     *
450
     * @param  array $settings Associative array of parameters for the engine
451
     * @return boolean True if the engine has been successfully initialized, false if not
452
     * @access   public
453
     */
454
    public function init($settings = [])
455
    {
456
        $this->settings = array_merge(
457
            [
458
                'duration'    => 31556926,
459
                'probability' => 100,
460
            ],
461
            $settings,
462
        );
463
464
        return true;
465
    }
466
467
    /**
468
     * Garbage collection
469
     *
470
     * Permanently remove all expired and deleted data
471
     *
472
     * @access public
473
     */
474
    public function gc() {}
475
476
    /**
477
     * Write value for a key into cache
478
     *
479
     * @param  string $key      Identifier for the data
480
     * @param  mixed  $value    Data to be cached
481
     * @param  mixed  $duration How long to cache the data, in seconds
482
     * @return boolean True if the data was successfully cached, false on failure
483
     * @access public
484
     */
485
    public function write($key, $value, $duration = null)
486
    {
487
        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

487
        trigger_error(sprintf(/** @scrutinizer ignore-call */ __('Method write() not implemented in %s', true), get_class($this)), E_USER_ERROR);
Loading history...
488
    }
489
490
    /**
491
     * Read a key from the cache
492
     *
493
     * @param  string $key Identifier for the data
494
     * @return mixed  The cached data, or false if the data doesn't exist, has expired, or if there was an error fetching it
495
     * @access public
496
     */
497
    public function read($key)
498
    {
499
        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

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