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