Cache.php 19 KB

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