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