1: <?php
2: /**
3: * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
4: * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
5: *
6: * Licensed under The MIT License
7: * For full copyright and license information, please see the LICENSE.txt
8: * Redistributions of files must retain the above copyright notice.
9: *
10: * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
11: * @link https://cakephp.org CakePHP(tm) Project
12: * @since 1.2.0
13: * @license https://opensource.org/licenses/mit-license.php MIT License
14: */
15: namespace Cake\Cache;
16:
17: use Cake\Cache\Engine\NullEngine;
18: use Cake\Core\ObjectRegistry;
19: use Cake\Core\StaticConfigTrait;
20: use InvalidArgumentException;
21: use RuntimeException;
22:
23: /**
24: * Cache provides a consistent interface to Caching in your application. It allows you
25: * to use several different Cache engines, without coupling your application to a specific
26: * implementation. It also allows you to change out cache storage or configuration without effecting
27: * the rest of your application.
28: *
29: * ### Configuring Cache engines
30: *
31: * You can configure Cache engines in your application's `Config/cache.php` file.
32: * A sample configuration would be:
33: *
34: * ```
35: * Cache::config('shared', [
36: * 'className' => 'Cake\Cache\Engine\ApcEngine',
37: * 'prefix' => 'my_app_'
38: * ]);
39: * ```
40: *
41: * This would configure an APC cache engine to the 'shared' alias. You could then read and write
42: * to that cache alias by using it for the `$config` parameter in the various Cache methods.
43: *
44: * In general all Cache operations are supported by all cache engines.
45: * However, Cache::increment() and Cache::decrement() are not supported by File caching.
46: *
47: * There are 5 built-in caching engines:
48: *
49: * - `FileEngine` - Uses simple files to store content. Poor performance, but good for
50: * storing large objects, or things that are not IO sensitive. Well suited to development
51: * as it is an easy cache to inspect and manually flush.
52: * - `ApcEngine` - Uses the APC object cache, one of the fastest caching engines.
53: * - `MemcacheEngine` - Uses the PECL::Memcache extension and Memcached for storage.
54: * Fast reads/writes, and benefits from memcache being distributed.
55: * - `XcacheEngine` - Uses the Xcache extension, an alternative to APC.
56: * - `WincacheEngine` - Uses Windows Cache Extension for PHP. Supports wincache 1.1.0 and higher.
57: * This engine is recommended to people deploying on windows with IIS.
58: * - `RedisEngine` - Uses redis and php-redis extension to store cache data.
59: *
60: * See Cache engine documentation for expected configuration keys.
61: *
62: * @see config/app.php for configuration settings
63: */
64: class Cache
65: {
66:
67: use StaticConfigTrait;
68:
69: /**
70: * An array mapping url schemes to fully qualified caching engine
71: * class names.
72: *
73: * @var array
74: */
75: protected static $_dsnClassMap = [
76: 'apc' => 'Cake\Cache\Engine\ApcEngine',
77: 'file' => 'Cake\Cache\Engine\FileEngine',
78: 'memcached' => 'Cake\Cache\Engine\MemcachedEngine',
79: 'null' => 'Cake\Cache\Engine\NullEngine',
80: 'redis' => 'Cake\Cache\Engine\RedisEngine',
81: 'wincache' => 'Cake\Cache\Engine\WincacheEngine',
82: 'xcache' => 'Cake\Cache\Engine\XcacheEngine',
83: ];
84:
85: /**
86: * Flag for tracking whether or not caching is enabled.
87: *
88: * @var bool
89: */
90: protected static $_enabled = true;
91:
92: /**
93: * Group to Config mapping
94: *
95: * @var array
96: */
97: protected static $_groups = [];
98:
99: /**
100: * Cache Registry used for creating and using cache adapters.
101: *
102: * @var \Cake\Core\ObjectRegistry
103: */
104: protected static $_registry;
105:
106: /**
107: * Returns the Cache Registry instance used for creating and using cache adapters.
108: *
109: * @return \Cake\Core\ObjectRegistry
110: */
111: public static function getRegistry()
112: {
113: if (!static::$_registry) {
114: static::$_registry = new CacheRegistry();
115: }
116:
117: return static::$_registry;
118: }
119:
120: /**
121: * Sets the Cache Registry instance used for creating and using cache adapters.
122: *
123: * Also allows for injecting of a new registry instance.
124: *
125: * @param \Cake\Core\ObjectRegistry $registry Injectable registry object.
126: * @return void
127: */
128: public static function setRegistry(ObjectRegistry $registry)
129: {
130: static::$_registry = $registry;
131: }
132:
133: /**
134: * Returns the Cache Registry instance used for creating and using cache adapters.
135: * Also allows for injecting of a new registry instance.
136: *
137: * @param \Cake\Core\ObjectRegistry|null $registry Injectable registry object.
138: * @return \Cake\Core\ObjectRegistry
139: * @deprecated Deprecated since 3.5. Use getRegistry() and setRegistry() instead.
140: */
141: public static function registry(ObjectRegistry $registry = null)
142: {
143: if ($registry) {
144: static::setRegistry($registry);
145: }
146:
147: return static::getRegistry();
148: }
149:
150: /**
151: * Finds and builds the instance of the required engine class.
152: *
153: * @param string $name Name of the config array that needs an engine instance built
154: * @return void
155: * @throws \InvalidArgumentException When a cache engine cannot be created.
156: */
157: protected static function _buildEngine($name)
158: {
159: $registry = static::getRegistry();
160:
161: if (empty(static::$_config[$name]['className'])) {
162: throw new InvalidArgumentException(
163: sprintf('The "%s" cache configuration does not exist.', $name)
164: );
165: }
166:
167: $config = static::$_config[$name];
168:
169: try {
170: $registry->load($name, $config);
171: } catch (RuntimeException $e) {
172: if (!array_key_exists('fallback', $config)) {
173: $registry->set($name, new NullEngine());
174: trigger_error($e->getMessage(), E_USER_WARNING);
175:
176: return;
177: }
178:
179: if ($config['fallback'] === $name) {
180: throw new InvalidArgumentException(
181: sprintf('"%s" cache configuration cannot fallback to itself.', $name)
182: );
183: }
184:
185: $fallbackEngine = clone static::engine($config['fallback']);
186: $newConfig = $config + ['groups' => [], 'prefix' => null];
187: $fallbackEngine->setConfig('groups', $newConfig['groups'], false);
188: if ($newConfig['prefix']) {
189: $fallbackEngine->setConfig('prefix', $newConfig['prefix'], false);
190: }
191: $registry->set($name, $fallbackEngine);
192: }
193:
194: if ($config['className'] instanceof CacheEngine) {
195: $config = $config['className']->getConfig();
196: }
197:
198: if (!empty($config['groups'])) {
199: foreach ($config['groups'] as $group) {
200: static::$_groups[$group][] = $name;
201: static::$_groups[$group] = array_unique(static::$_groups[$group]);
202: sort(static::$_groups[$group]);
203: }
204: }
205: }
206:
207: /**
208: * Fetch the engine attached to a specific configuration name.
209: *
210: * If the cache engine & configuration are missing an error will be
211: * triggered.
212: *
213: * @param string $config The configuration name you want an engine for.
214: * @return \Cake\Cache\CacheEngine When caching is disabled a null engine will be returned.
215: */
216: public static function engine($config)
217: {
218: if (!static::$_enabled) {
219: return new NullEngine();
220: }
221:
222: $registry = static::getRegistry();
223:
224: if (isset($registry->{$config})) {
225: return $registry->{$config};
226: }
227:
228: static::_buildEngine($config);
229:
230: return $registry->{$config};
231: }
232:
233: /**
234: * Garbage collection
235: *
236: * Permanently remove all expired and deleted data
237: *
238: * @param string $config [optional] The config name you wish to have garbage collected. Defaults to 'default'
239: * @param int|null $expires [optional] An expires timestamp. Defaults to NULL
240: * @return void
241: */
242: public static function gc($config = 'default', $expires = null)
243: {
244: $engine = static::engine($config);
245: $engine->gc($expires);
246: }
247:
248: /**
249: * Write data for key into cache.
250: *
251: * ### Usage:
252: *
253: * Writing to the active cache config:
254: *
255: * ```
256: * Cache::write('cached_data', $data);
257: * ```
258: *
259: * Writing to a specific cache config:
260: *
261: * ```
262: * Cache::write('cached_data', $data, 'long_term');
263: * ```
264: *
265: * @param string $key Identifier for the data
266: * @param mixed $value Data to be cached - anything except a resource
267: * @param string $config Optional string configuration name to write to. Defaults to 'default'
268: * @return bool True if the data was successfully cached, false on failure
269: */
270: public static function write($key, $value, $config = 'default')
271: {
272: $engine = static::engine($config);
273: if (is_resource($value)) {
274: return false;
275: }
276:
277: $success = $engine->write($key, $value);
278: if ($success === false && $value !== '') {
279: trigger_error(
280: sprintf(
281: "%s cache was unable to write '%s' to %s cache",
282: $config,
283: $key,
284: get_class($engine)
285: ),
286: E_USER_WARNING
287: );
288: }
289:
290: return $success;
291: }
292:
293: /**
294: * Write data for many keys into cache.
295: *
296: * ### Usage:
297: *
298: * Writing to the active cache config:
299: *
300: * ```
301: * Cache::writeMany(['cached_data_1' => 'data 1', 'cached_data_2' => 'data 2']);
302: * ```
303: *
304: * Writing to a specific cache config:
305: *
306: * ```
307: * Cache::writeMany(['cached_data_1' => 'data 1', 'cached_data_2' => 'data 2'], 'long_term');
308: * ```
309: *
310: * @param array $data An array of data to be stored in the cache
311: * @param string $config Optional string configuration name to write to. Defaults to 'default'
312: * @return array of bools for each key provided, indicating true for success or false for fail
313: * @throws \RuntimeException
314: */
315: public static function writeMany($data, $config = 'default')
316: {
317: $engine = static::engine($config);
318: $return = $engine->writeMany($data);
319: foreach ($return as $key => $success) {
320: if ($success === false && $data[$key] !== '') {
321: throw new RuntimeException(sprintf(
322: '%s cache was unable to write \'%s\' to %s cache',
323: $config,
324: $key,
325: get_class($engine)
326: ));
327: }
328: }
329:
330: return $return;
331: }
332:
333: /**
334: * Read a key from the cache.
335: *
336: * ### Usage:
337: *
338: * Reading from the active cache configuration.
339: *
340: * ```
341: * Cache::read('my_data');
342: * ```
343: *
344: * Reading from a specific cache configuration.
345: *
346: * ```
347: * Cache::read('my_data', 'long_term');
348: * ```
349: *
350: * @param string $key Identifier for the data
351: * @param string $config optional name of the configuration to use. Defaults to 'default'
352: * @return mixed The cached data, or false if the data doesn't exist, has expired, or if there was an error fetching it
353: */
354: public static function read($key, $config = 'default')
355: {
356: $engine = static::engine($config);
357:
358: return $engine->read($key);
359: }
360:
361: /**
362: * Read multiple keys from the cache.
363: *
364: * ### Usage:
365: *
366: * Reading multiple keys from the active cache configuration.
367: *
368: * ```
369: * Cache::readMany(['my_data_1', 'my_data_2]);
370: * ```
371: *
372: * Reading from a specific cache configuration.
373: *
374: * ```
375: * Cache::readMany(['my_data_1', 'my_data_2], 'long_term');
376: * ```
377: *
378: * @param array $keys an array of keys to fetch from the cache
379: * @param string $config optional name of the configuration to use. Defaults to 'default'
380: * @return array An array containing, for each of the given $keys, the cached data or false if cached data could not be
381: * retrieved.
382: */
383: public static function readMany($keys, $config = 'default')
384: {
385: $engine = static::engine($config);
386:
387: return $engine->readMany($keys);
388: }
389:
390: /**
391: * Increment a number under the key and return incremented value.
392: *
393: * @param string $key Identifier for the data
394: * @param int $offset How much to add
395: * @param string $config Optional string configuration name. Defaults to 'default'
396: * @return mixed new value, or false if the data doesn't exist, is not integer,
397: * or if there was an error fetching it.
398: */
399: public static function increment($key, $offset = 1, $config = 'default')
400: {
401: $engine = static::engine($config);
402: if (!is_int($offset) || $offset < 0) {
403: return false;
404: }
405:
406: return $engine->increment($key, $offset);
407: }
408:
409: /**
410: * Decrement a number under the key and return decremented value.
411: *
412: * @param string $key Identifier for the data
413: * @param int $offset How much to subtract
414: * @param string $config Optional string configuration name. Defaults to 'default'
415: * @return mixed new value, or false if the data doesn't exist, is not integer,
416: * or if there was an error fetching it
417: */
418: public static function decrement($key, $offset = 1, $config = 'default')
419: {
420: $engine = static::engine($config);
421: if (!is_int($offset) || $offset < 0) {
422: return false;
423: }
424:
425: return $engine->decrement($key, $offset);
426: }
427:
428: /**
429: * Delete a key from the cache.
430: *
431: * ### Usage:
432: *
433: * Deleting from the active cache configuration.
434: *
435: * ```
436: * Cache::delete('my_data');
437: * ```
438: *
439: * Deleting from a specific cache configuration.
440: *
441: * ```
442: * Cache::delete('my_data', 'long_term');
443: * ```
444: *
445: * @param string $key Identifier for the data
446: * @param string $config name of the configuration to use. Defaults to 'default'
447: * @return bool True if the value was successfully deleted, false if it didn't exist or couldn't be removed
448: */
449: public static function delete($key, $config = 'default')
450: {
451: $engine = static::engine($config);
452:
453: return $engine->delete($key);
454: }
455:
456: /**
457: * Delete many keys from the cache.
458: *
459: * ### Usage:
460: *
461: * Deleting multiple keys from the active cache configuration.
462: *
463: * ```
464: * Cache::deleteMany(['my_data_1', 'my_data_2']);
465: * ```
466: *
467: * Deleting from a specific cache configuration.
468: *
469: * ```
470: * Cache::deleteMany(['my_data_1', 'my_data_2], 'long_term');
471: * ```
472: *
473: * @param array $keys Array of cache keys to be deleted
474: * @param string $config name of the configuration to use. Defaults to 'default'
475: * @return array of boolean values that are true if the value was successfully deleted, false if it didn't exist or
476: * couldn't be removed
477: */
478: public static function deleteMany($keys, $config = 'default')
479: {
480: $engine = static::engine($config);
481:
482: return $engine->deleteMany($keys);
483: }
484:
485: /**
486: * Delete all keys from the cache.
487: *
488: * @param bool $check if true will check expiration, otherwise delete all
489: * @param string $config name of the configuration to use. Defaults to 'default'
490: * @return bool True if the cache was successfully cleared, false otherwise
491: */
492: public static function clear($check = false, $config = 'default')
493: {
494: $engine = static::engine($config);
495:
496: return $engine->clear($check);
497: }
498:
499: /**
500: * Delete all keys from the cache from all configurations.
501: *
502: * @param bool $check if true will check expiration, otherwise delete all
503: * @return array Status code. For each configuration, it reports the status of the operation
504: */
505: public static function clearAll($check = false)
506: {
507: $status = [];
508:
509: foreach (self::configured() as $config) {
510: $status[$config] = self::clear($check, $config);
511: }
512:
513: return $status;
514: }
515:
516: /**
517: * Delete all keys from the cache belonging to the same group.
518: *
519: * @param string $group name of the group to be cleared
520: * @param string $config name of the configuration to use. Defaults to 'default'
521: * @return bool True if the cache group was successfully cleared, false otherwise
522: */
523: public static function clearGroup($group, $config = 'default')
524: {
525: $engine = static::engine($config);
526:
527: return $engine->clearGroup($group);
528: }
529:
530: /**
531: * Retrieve group names to config mapping.
532: *
533: * ```
534: * Cache::config('daily', ['duration' => '1 day', 'groups' => ['posts']]);
535: * Cache::config('weekly', ['duration' => '1 week', 'groups' => ['posts', 'archive']]);
536: * $configs = Cache::groupConfigs('posts');
537: * ```
538: *
539: * $configs will equal to `['posts' => ['daily', 'weekly']]`
540: * Calling this method will load all the configured engines.
541: *
542: * @param string|null $group group name or null to retrieve all group mappings
543: * @return array map of group and all configuration that has the same group
544: * @throws \InvalidArgumentException
545: */
546: public static function groupConfigs($group = null)
547: {
548: foreach (array_keys(static::$_config) as $config) {
549: static::engine($config);
550: }
551: if ($group === null) {
552: return static::$_groups;
553: }
554:
555: if (isset(self::$_groups[$group])) {
556: return [$group => self::$_groups[$group]];
557: }
558:
559: throw new InvalidArgumentException(sprintf('Invalid cache group %s', $group));
560: }
561:
562: /**
563: * Re-enable caching.
564: *
565: * If caching has been disabled with Cache::disable() this method will reverse that effect.
566: *
567: * @return void
568: */
569: public static function enable()
570: {
571: static::$_enabled = true;
572: }
573:
574: /**
575: * Disable caching.
576: *
577: * When disabled all cache operations will return null.
578: *
579: * @return void
580: */
581: public static function disable()
582: {
583: static::$_enabled = false;
584: }
585:
586: /**
587: * Check whether or not caching is enabled.
588: *
589: * @return bool
590: */
591: public static function enabled()
592: {
593: return static::$_enabled;
594: }
595:
596: /**
597: * Provides the ability to easily do read-through caching.
598: *
599: * When called if the $key is not set in $config, the $callable function
600: * will be invoked. The results will then be stored into the cache config
601: * at key.
602: *
603: * Examples:
604: *
605: * Using a Closure to provide data, assume `$this` is a Table object:
606: *
607: * ```
608: * $results = Cache::remember('all_articles', function () {
609: * return $this->find('all');
610: * });
611: * ```
612: *
613: * @param string $key The cache key to read/store data at.
614: * @param callable $callable The callable that provides data in the case when
615: * the cache key is empty. Can be any callable type supported by your PHP.
616: * @param string $config The cache configuration to use for this operation.
617: * Defaults to default.
618: * @return mixed If the key is found: the cached data, false if the data
619: * missing/expired, or an error. If the key is not found: boolean of the
620: * success of the write
621: */
622: public static function remember($key, $callable, $config = 'default')
623: {
624: $existing = self::read($key, $config);
625: if ($existing !== false) {
626: return $existing;
627: }
628: $results = call_user_func($callable);
629: self::write($key, $results, $config);
630:
631: return $results;
632: }
633:
634: /**
635: * Write data for key into a cache engine if it doesn't exist already.
636: *
637: * ### Usage:
638: *
639: * Writing to the active cache config:
640: *
641: * ```
642: * Cache::add('cached_data', $data);
643: * ```
644: *
645: * Writing to a specific cache config:
646: *
647: * ```
648: * Cache::add('cached_data', $data, 'long_term');
649: * ```
650: *
651: * @param string $key Identifier for the data.
652: * @param mixed $value Data to be cached - anything except a resource.
653: * @param string $config Optional string configuration name to write to. Defaults to 'default'.
654: * @return bool True if the data was successfully cached, false on failure.
655: * Or if the key existed already.
656: */
657: public static function add($key, $value, $config = 'default')
658: {
659: $engine = static::engine($config);
660: if (is_resource($value)) {
661: return false;
662: }
663:
664: return $engine->add($key, $value);
665: }
666: }
667: