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\Core\InstanceConfigTrait;
18: use InvalidArgumentException;
19:
20: /**
21: * Storage engine for CakePHP caching
22: */
23: abstract class CacheEngine
24: {
25: use InstanceConfigTrait;
26:
27: /**
28: * The default cache configuration is overridden in most cache adapters. These are
29: * the keys that are common to all adapters. If overridden, this property is not used.
30: *
31: * - `duration` Specify how long items in this cache configuration last.
32: * - `groups` List of groups or 'tags' associated to every key stored in this config.
33: * handy for deleting a complete group from cache.
34: * - `prefix` Prefix appended to all entries. Good for when you need to share a keyspace
35: * with either another cache config or another application.
36: * - `probability` Probability of hitting a cache gc cleanup. Setting to 0 will disable
37: * cache::gc from ever being called automatically.
38: * - `warnOnWriteFailures` Some engines, such as ApcuEngine, may raise warnings on
39: * write failures.
40: *
41: * @var array
42: */
43: protected $_defaultConfig = [
44: 'duration' => 3600,
45: 'groups' => [],
46: 'prefix' => 'cake_',
47: 'probability' => 100,
48: 'warnOnWriteFailures' => true,
49: ];
50:
51: /**
52: * Contains the compiled string with all groups
53: * prefixes to be prepended to every key in this cache engine
54: *
55: * @var string
56: */
57: protected $_groupPrefix;
58:
59: /**
60: * Initialize the cache engine
61: *
62: * Called automatically by the cache frontend. Merge the runtime config with the defaults
63: * before use.
64: *
65: * @param array $config Associative array of parameters for the engine
66: * @return bool True if the engine has been successfully initialized, false if not
67: */
68: public function init(array $config = [])
69: {
70: $this->setConfig($config);
71:
72: if (!empty($this->_config['groups'])) {
73: sort($this->_config['groups']);
74: $this->_groupPrefix = str_repeat('%s_', count($this->_config['groups']));
75: }
76: if (!is_numeric($this->_config['duration'])) {
77: $this->_config['duration'] = strtotime($this->_config['duration']) - time();
78: }
79:
80: return true;
81: }
82:
83: /**
84: * Garbage collection
85: *
86: * Permanently remove all expired and deleted data
87: *
88: * @param int|null $expires [optional] An expires timestamp, invalidating all data before.
89: * @return void
90: */
91: public function gc($expires = null)
92: {
93: }
94:
95: /**
96: * Write value for a key into cache
97: *
98: * @param string $key Identifier for the data
99: * @param mixed $value Data to be cached
100: * @return bool True if the data was successfully cached, false on failure
101: */
102: abstract public function write($key, $value);
103:
104: /**
105: * Write data for many keys into cache
106: *
107: * @param array $data An array of data to be stored in the cache
108: * @return array of bools for each key provided, true if the data was successfully cached, false on failure
109: */
110: public function writeMany($data)
111: {
112: $return = [];
113: foreach ($data as $key => $value) {
114: $return[$key] = $this->write($key, $value);
115: }
116:
117: return $return;
118: }
119:
120: /**
121: * Read a key from the cache
122: *
123: * @param string $key Identifier for the data
124: * @return mixed The cached data, or false if the data doesn't exist, has expired, or if there was an error fetching it
125: */
126: abstract public function read($key);
127:
128: /**
129: * Read multiple keys from the cache
130: *
131: * @param array $keys An array of identifiers for the data
132: * @return array For each cache key (given as the array key) the cache data associated or false if the data doesn't
133: * exist, has expired, or if there was an error fetching it
134: */
135: public function readMany($keys)
136: {
137: $return = [];
138: foreach ($keys as $key) {
139: $return[$key] = $this->read($key);
140: }
141:
142: return $return;
143: }
144:
145: /**
146: * Increment a number under the key and return incremented value
147: *
148: * @param string $key Identifier for the data
149: * @param int $offset How much to add
150: * @return bool|int New incremented value, false otherwise
151: */
152: abstract public function increment($key, $offset = 1);
153:
154: /**
155: * Decrement a number under the key and return decremented value
156: *
157: * @param string $key Identifier for the data
158: * @param int $offset How much to subtract
159: * @return bool|int New incremented value, false otherwise
160: */
161: abstract public function decrement($key, $offset = 1);
162:
163: /**
164: * Delete a key from the cache
165: *
166: * @param string $key Identifier for the data
167: * @return bool True if the value was successfully deleted, false if it didn't exist or couldn't be removed
168: */
169: abstract public function delete($key);
170:
171: /**
172: * Delete all keys from the cache
173: *
174: * @param bool $check if true will check expiration, otherwise delete all
175: * @return bool True if the cache was successfully cleared, false otherwise
176: */
177: abstract public function clear($check);
178:
179: /**
180: * Deletes keys from the cache
181: *
182: * @param array $keys An array of identifiers for the data
183: * @return array For each provided cache key (given back as the array key) true if the value was successfully deleted,
184: * false if it didn't exist or couldn't be removed
185: */
186: public function deleteMany($keys)
187: {
188: $return = [];
189: foreach ($keys as $key) {
190: $return[$key] = $this->delete($key);
191: }
192:
193: return $return;
194: }
195:
196: /**
197: * Add a key to the cache if it does not already exist.
198: *
199: * Defaults to a non-atomic implementation. Subclasses should
200: * prefer atomic implementations.
201: *
202: * @param string $key Identifier for the data.
203: * @param mixed $value Data to be cached.
204: * @return bool True if the data was successfully cached, false on failure.
205: */
206: public function add($key, $value)
207: {
208: $cachedValue = $this->read($key);
209: if ($cachedValue === false) {
210: return $this->write($key, $value);
211: }
212:
213: return false;
214: }
215:
216: /**
217: * Clears all values belonging to a group. Is up to the implementing engine
218: * to decide whether actually delete the keys or just simulate it to achieve
219: * the same result.
220: *
221: * @param string $group name of the group to be cleared
222: * @return bool
223: */
224: public function clearGroup($group)
225: {
226: return false;
227: }
228:
229: /**
230: * Does whatever initialization for each group is required
231: * and returns the `group value` for each of them, this is
232: * the token representing each group in the cache key
233: *
234: * @return array
235: */
236: public function groups()
237: {
238: return $this->_config['groups'];
239: }
240:
241: /**
242: * Generates a safe key for use with cache engine storage engines.
243: *
244: * @param string $key the key passed over
245: * @return bool|string string key or false
246: */
247: public function key($key)
248: {
249: if (!$key) {
250: return false;
251: }
252:
253: $prefix = '';
254: if ($this->_groupPrefix) {
255: $prefix = md5(implode('_', $this->groups()));
256: }
257:
258: $key = preg_replace('/[\s]+/', '_', strtolower(trim(str_replace([DIRECTORY_SEPARATOR, '/', '.'], '_', (string)$key))));
259:
260: return $prefix . $key;
261: }
262:
263: /**
264: * Generates a safe key, taking account of the configured key prefix
265: *
266: * @param string $key the key passed over
267: * @return mixed string $key or false
268: * @throws \InvalidArgumentException If key's value is empty
269: */
270: protected function _key($key)
271: {
272: $key = $this->key($key);
273: if ($key === false) {
274: throw new InvalidArgumentException('An empty value is not valid as a cache key');
275: }
276:
277: return $this->_config['prefix'] . $key;
278: }
279:
280: /**
281: * Cache Engines may trigger warnings if they encounter failures during operation,
282: * if option warnOnWriteFailures is set to true.
283: *
284: * @param string $message The warning message.
285: * @return void
286: */
287: protected function warning($message)
288: {
289: if ($this->getConfig('warnOnWriteFailures') !== true) {
290: return;
291: }
292:
293: triggerWarning($message);
294: }
295: }
296: