Cache.php 20 KB

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