1: <?php
2: /**
3: * APC storage engine for cache.
4: *
5: * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
6: * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
7: *
8: * Licensed under The MIT License
9: * For full copyright and license information, please see the LICENSE.txt
10: * Redistributions of files must retain the above copyright notice.
11: *
12: * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
13: * @link https://cakephp.org CakePHP(tm) Project
14: * @package Cake.Cache.Engine
15: * @since CakePHP(tm) v 1.2.0.4933
16: * @license https://opensource.org/licenses/mit-license.php MIT License
17: */
18:
19: /**
20: * APC storage engine for cache
21: *
22: * @package Cake.Cache.Engine
23: */
24: class ApcEngine extends CacheEngine {
25:
26: /**
27: * Contains the compiled group names
28: * (prefixed with the global configuration prefix)
29: *
30: * @var array
31: */
32: protected $_compiledGroupNames = array();
33:
34: /**
35: * APC or APCu extension
36: *
37: * @var string
38: */
39: protected $_apcExtension = 'apc';
40:
41: /**
42: * Initialize the Cache Engine
43: *
44: * Called automatically by the cache frontend
45: * To reinitialize the settings call Cache::engine('EngineName', [optional] settings = array());
46: *
47: * @param array $settings array of setting for the engine
48: * @return bool True if the engine has been successfully initialized, false if not
49: * @see CacheEngine::__defaults
50: */
51: public function init($settings = array()) {
52: if (!isset($settings['prefix'])) {
53: $settings['prefix'] = Inflector::slug(APP_DIR) . '_';
54: }
55: $settings += array('engine' => 'Apc');
56: parent::init($settings);
57: if (function_exists('apcu_dec')) {
58: $this->_apcExtension = 'apcu';
59: return true;
60: }
61: return function_exists('apc_dec');
62: }
63:
64: /**
65: * Write data for key into cache
66: *
67: * @param string $key Identifier for the data
68: * @param mixed $value Data to be cached
69: * @param int $duration How long to cache the data, in seconds
70: * @return bool True if the data was successfully cached, false on failure
71: */
72: public function write($key, $value, $duration) {
73: $expires = 0;
74: if ($duration) {
75: $expires = time() + $duration;
76: }
77: $func = $this->_apcExtension . '_store';
78: $func($key . '_expires', $expires, $duration);
79: return $func($key, $value, $duration);
80: }
81:
82: /**
83: * Read a key from the cache
84: *
85: * @param string $key Identifier for the data
86: * @return mixed The cached data, or false if the data doesn't exist, has expired, or if there was an error fetching it
87: */
88: public function read($key) {
89: $time = time();
90: $func = $this->_apcExtension . '_fetch';
91: $cachetime = (int)$func($key . '_expires');
92: if ($cachetime !== 0 && ($cachetime < $time || ($time + $this->settings['duration']) < $cachetime)) {
93: return false;
94: }
95: return $func($key);
96: }
97:
98: /**
99: * Increments the value of an integer cached key
100: *
101: * @param string $key Identifier for the data
102: * @param int $offset How much to increment
103: * @return New incremented value, false otherwise
104: */
105: public function increment($key, $offset = 1) {
106: $func = $this->_apcExtension . '_inc';
107: return $func($key, $offset);
108: }
109:
110: /**
111: * Decrements the value of an integer cached key
112: *
113: * @param string $key Identifier for the data
114: * @param int $offset How much to subtract
115: * @return New decremented value, false otherwise
116: */
117: public function decrement($key, $offset = 1) {
118: $func = $this->_apcExtension . '_dec';
119: return $func($key, $offset);
120: }
121:
122: /**
123: * Delete a key from the cache
124: *
125: * @param string $key Identifier for the data
126: * @return bool True if the value was successfully deleted, false if it didn't exist or couldn't be removed
127: */
128: public function delete($key) {
129: $func = $this->_apcExtension . '_delete';
130: return $func($key);
131: }
132:
133: /**
134: * Delete all keys from the cache. This will clear every cache config using APC.
135: *
136: * @param bool $check If true, nothing will be cleared, as entries are removed
137: * from APC as they expired. This flag is really only used by FileEngine.
138: * @return bool True Returns true.
139: */
140: public function clear($check) {
141: if ($check) {
142: return true;
143: }
144: $func = $this->_apcExtension . '_delete';
145: if (class_exists('APCIterator', false)) {
146: $iterator = new APCIterator(
147: 'user',
148: '/^' . preg_quote($this->settings['prefix'], '/') . '/',
149: APC_ITER_NONE
150: );
151: $func($iterator);
152: return true;
153: }
154: $cache = $this->_apcExtension === 'apc' ? apc_cache_info('user') : apcu_cache_info();
155: foreach ($cache['cache_list'] as $key) {
156: if (strpos($key['info'], $this->settings['prefix']) === 0) {
157: $func($key['info']);
158: }
159: }
160: return true;
161: }
162:
163: /**
164: * Returns the `group value` for each of the configured groups
165: * If the group initial value was not found, then it initializes
166: * the group accordingly.
167: *
168: * @return array
169: */
170: public function groups() {
171: if (empty($this->_compiledGroupNames)) {
172: foreach ($this->settings['groups'] as $group) {
173: $this->_compiledGroupNames[] = $this->settings['prefix'] . $group;
174: }
175: }
176:
177: $fetchFunc = $this->_apcExtension . '_fetch';
178: $storeFunc = $this->_apcExtension . '_store';
179: $groups = $fetchFunc($this->_compiledGroupNames);
180: if (count($groups) !== count($this->settings['groups'])) {
181: foreach ($this->_compiledGroupNames as $group) {
182: if (!isset($groups[$group])) {
183: $storeFunc($group, 1);
184: $groups[$group] = 1;
185: }
186: }
187: ksort($groups);
188: }
189:
190: $result = array();
191: $groups = array_values($groups);
192: foreach ($this->settings['groups'] as $i => $group) {
193: $result[] = $group . $groups[$i];
194: }
195: return $result;
196: }
197:
198: /**
199: * Increments the group value to simulate deletion of all keys under a group
200: * old values will remain in storage until they expire.
201: *
202: * @param string $group The group to clear.
203: * @return bool success
204: */
205: public function clearGroup($group) {
206: $func = $this->_apcExtension . '_inc';
207: $func($this->settings['prefix'] . $group, 1, $success);
208: return $success;
209: }
210:
211: /**
212: * Write data for key into cache if it doesn't exist already.
213: * If it already exists, it fails and returns false.
214: *
215: * @param string $key Identifier for the data.
216: * @param mixed $value Data to be cached.
217: * @param int $duration How long to cache the data, in seconds.
218: * @return bool True if the data was successfully cached, false on failure.
219: * @link http://php.net/manual/en/function.apc-add.php
220: */
221: public function add($key, $value, $duration) {
222: $expires = 0;
223: if ($duration) {
224: $expires = time() + $duration;
225: }
226: $func = $this->_apcExtension . '_add';
227: $func($key . '_expires', $expires, $duration);
228: return $func($key, $value, $duration);
229: }
230: }
231: