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 3.5.4
13: * @license https://opensource.org/licenses/mit-license.php MIT License
14: */
15: namespace Cake\Cache\Engine;
16:
17: use APCuIterator;
18: use Cake\Cache\CacheEngine;
19:
20: /**
21: * APCu storage engine for cache
22: */
23: class ApcuEngine extends CacheEngine
24: {
25: /**
26: * Contains the compiled group names
27: * (prefixed with the global configuration prefix)
28: *
29: * @var string[]
30: */
31: protected $_compiledGroupNames = [];
32:
33: /**
34: * Initialize the Cache Engine
35: *
36: * Called automatically by the cache frontend
37: *
38: * @param array $config array of setting for the engine
39: * @return bool True if the engine has been successfully initialized, false if not
40: */
41: public function init(array $config = [])
42: {
43: if (!extension_loaded('apcu')) {
44: return false;
45: }
46:
47: return parent::init($config);
48: }
49:
50: /**
51: * Write data for key into cache
52: *
53: * @param string $key Identifier for the data
54: * @param mixed $value Data to be cached
55: * @return bool True if the data was successfully cached, false on failure
56: * @link https://secure.php.net/manual/en/function.apcu-store.php
57: */
58: public function write($key, $value)
59: {
60: $key = $this->_key($key);
61: $duration = $this->_config['duration'];
62:
63: return apcu_store($key, $value, $duration);
64: }
65:
66: /**
67: * Read a key from the cache
68: *
69: * @param string $key Identifier for the data
70: * @return mixed The cached data, or false if the data doesn't exist,
71: * has expired, or if there was an error fetching it
72: * @link https://secure.php.net/manual/en/function.apcu-fetch.php
73: */
74: public function read($key)
75: {
76: $key = $this->_key($key);
77:
78: return apcu_fetch($key);
79: }
80:
81: /**
82: * Increments the value of an integer cached key
83: *
84: * @param string $key Identifier for the data
85: * @param int $offset How much to increment
86: * @return int|false New incremented value, false otherwise
87: * @link https://secure.php.net/manual/en/function.apcu-inc.php
88: */
89: public function increment($key, $offset = 1)
90: {
91: $key = $this->_key($key);
92:
93: return apcu_inc($key, $offset);
94: }
95:
96: /**
97: * Decrements the value of an integer cached key
98: *
99: * @param string $key Identifier for the data
100: * @param int $offset How much to subtract
101: * @return int|false New decremented value, false otherwise
102: * @link https://secure.php.net/manual/en/function.apcu-dec.php
103: */
104: public function decrement($key, $offset = 1)
105: {
106: $key = $this->_key($key);
107:
108: return apcu_dec($key, $offset);
109: }
110:
111: /**
112: * Delete a key from the cache
113: *
114: * @param string $key Identifier for the data
115: * @return bool True if the value was successfully deleted, false if it didn't exist or couldn't be removed
116: * @link https://secure.php.net/manual/en/function.apcu-delete.php
117: */
118: public function delete($key)
119: {
120: $key = $this->_key($key);
121:
122: return apcu_delete($key);
123: }
124:
125: /**
126: * Delete all keys from the cache. This will clear every cache config using APC.
127: *
128: * @param bool $check If true, nothing will be cleared, as entries are removed
129: * from APC as they expired. This flag is really only used by FileEngine.
130: * @return bool True Returns true.
131: * @link https://secure.php.net/manual/en/function.apcu-cache-info.php
132: * @link https://secure.php.net/manual/en/function.apcu-delete.php
133: */
134: public function clear($check)
135: {
136: if ($check) {
137: return true;
138: }
139: if (class_exists('APCuIterator', false)) {
140: $iterator = new APCuIterator(
141: '/^' . preg_quote($this->_config['prefix'], '/') . '/',
142: APC_ITER_NONE
143: );
144: apcu_delete($iterator);
145:
146: return true;
147: }
148:
149: $cache = apcu_cache_info(); // Raises warning by itself already
150: foreach ($cache['cache_list'] as $key) {
151: if (strpos($key['info'], $this->_config['prefix']) === 0) {
152: apcu_delete($key['info']);
153: }
154: }
155:
156: return true;
157: }
158:
159: /**
160: * Write data for key into cache if it doesn't exist already.
161: * If it already exists, it fails and returns false.
162: *
163: * @param string $key Identifier for the data.
164: * @param mixed $value Data to be cached.
165: * @return bool True if the data was successfully cached, false on failure.
166: * @link https://secure.php.net/manual/en/function.apcu-add.php
167: */
168: public function add($key, $value)
169: {
170: $key = $this->_key($key);
171: $duration = $this->_config['duration'];
172:
173: return apcu_add($key, $value, $duration);
174: }
175:
176: /**
177: * Returns the `group value` for each of the configured groups
178: * If the group initial value was not found, then it initializes
179: * the group accordingly.
180: *
181: * @return array
182: * @link https://secure.php.net/manual/en/function.apcu-fetch.php
183: * @link https://secure.php.net/manual/en/function.apcu-store.php
184: */
185: public function groups()
186: {
187: if (empty($this->_compiledGroupNames)) {
188: foreach ($this->_config['groups'] as $group) {
189: $this->_compiledGroupNames[] = $this->_config['prefix'] . $group;
190: }
191: }
192:
193: $success = false;
194: $groups = apcu_fetch($this->_compiledGroupNames, $success);
195: if ($success && count($groups) !== count($this->_config['groups'])) {
196: foreach ($this->_compiledGroupNames as $group) {
197: if (!isset($groups[$group])) {
198: $value = 1;
199: if (apcu_store($group, $value) === false) {
200: $this->warning(
201: sprintf('Failed to store key "%s" with value "%s" into APCu cache.', $group, $value)
202: );
203: }
204: $groups[$group] = $value;
205: }
206: }
207: ksort($groups);
208: }
209:
210: $result = [];
211: $groups = array_values($groups);
212: foreach ($this->_config['groups'] as $i => $group) {
213: $result[] = $group . $groups[$i];
214: }
215:
216: return $result;
217: }
218:
219: /**
220: * Increments the group value to simulate deletion of all keys under a group
221: * old values will remain in storage until they expire.
222: *
223: * @param string $group The group to clear.
224: * @return bool success
225: * @link https://secure.php.net/manual/en/function.apcu-inc.php
226: */
227: public function clearGroup($group)
228: {
229: $success = false;
230: apcu_inc($this->_config['prefix'] . $group, 1, $success);
231:
232: return $success;
233: }
234: }
235: