1: <?php
2: /**
3: * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
4: * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
5: *
6: * Licensed under The MIT License
7: * Redistributions of files must retain the above copyright notice.
8: *
9: * @copyright Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
10: * @link http://cakephp.org CakePHP(tm) Project
11: * @package Cake.Cache
12: * @since CakePHP(tm) v 1.2.0.4933
13: * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
14: */
15:
16: App::uses('Inflector', 'Utility');
17: App::uses('CacheEngine', 'Cache');
18:
19: /**
20: * Cache provides a consistent interface to Caching in your application. It allows you
21: * to use several different Cache engines, without coupling your application to a specific
22: * implementation. It also allows you to change out cache storage or configuration without effecting
23: * the rest of your application.
24: *
25: * You can configure Cache engines in your application's `bootstrap.php` file. A sample configuration would
26: * be
27: *
28: * {{{
29: * Cache::config('shared', array(
30: * 'engine' => 'Apc',
31: * 'prefix' => 'my_app_'
32: * ));
33: * }}}
34: *
35: * This would configure an APC cache engine to the 'shared' alias. You could then read and write
36: * to that cache alias by using it for the `$config` parameter in the various Cache methods. In
37: * general all Cache operations are supported by all cache engines. However, Cache::increment() and
38: * Cache::decrement() are not supported by File caching.
39: *
40: * @package Cake.Cache
41: */
42: class Cache {
43:
44: /**
45: * Cache configuration stack
46: * Keeps the permanent/default settings for each cache engine.
47: * These settings are used to reset the engines after temporary modification.
48: *
49: * @var array
50: */
51: protected static $_config = array();
52:
53: /**
54: * Whether to reset the settings with the next call to Cache::set();
55: *
56: * @var array
57: */
58: protected static $_reset = false;
59:
60: /**
61: * Engine instances keyed by configuration name.
62: *
63: * @var array
64: */
65: protected static $_engines = array();
66:
67: /**
68: * Set the cache configuration to use. config() can
69: * both create new configurations, return the settings for already configured
70: * configurations.
71: *
72: * To create a new configuration, or to modify an existing configuration permanently:
73: *
74: * `Cache::config('my_config', array('engine' => 'File', 'path' => TMP));`
75: *
76: * If you need to modify a configuration temporarily, use Cache::set().
77: * To get the settings for a configuration:
78: *
79: * `Cache::config('default');`
80: *
81: * There are 5 built-in caching engines:
82: *
83: * - `FileEngine` - Uses simple files to store content. Poor performance, but good for
84: * storing large objects, or things that are not IO sensitive.
85: * - `ApcEngine` - Uses the APC object cache, one of the fastest caching engines.
86: * - `MemcacheEngine` - Uses the PECL::Memcache extension and Memcached for storage.
87: * Fast reads/writes, and benefits from memcache being distributed.
88: * - `XcacheEngine` - Uses the Xcache extension, an alternative to APC.
89: * - `WincacheEngine` - Uses Windows Cache Extension for PHP. Supports wincache 1.1.0 and higher.
90: *
91: * The following keys are used in core cache engines:
92: *
93: * - `duration` Specify how long items in this cache configuration last.
94: * - `prefix` Prefix appended to all entries. Good for when you need to share a keyspace
95: * with either another cache config or another application.
96: * - `probability` Probability of hitting a cache gc cleanup. Setting to 0 will disable
97: * cache::gc from ever being called automatically.
98: * - `servers' Used by memcache. Give the address of the memcached servers to use.
99: * - `compress` Used by memcache. Enables memcache's compressed format.
100: * - `serialize` Used by FileCache. Should cache objects be serialized first.
101: * - `path` Used by FileCache. Path to where cachefiles should be saved.
102: * - `lock` Used by FileCache. Should files be locked before writing to them?
103: * - `user` Used by Xcache. Username for XCache
104: * - `password` Used by Xcache. Password for XCache
105: *
106: * @see app/Config/core.php for configuration settings
107: * @param string $name Name of the configuration
108: * @param array $settings Optional associative array of settings passed to the engine
109: * @return array(engine, settings) on success, false on failure
110: * @throws CacheException
111: */
112: public static function config($name = null, $settings = array()) {
113: if (is_array($name)) {
114: $settings = $name;
115: }
116:
117: $current = array();
118: if (isset(self::$_config[$name])) {
119: $current = self::$_config[$name];
120: }
121:
122: if (!empty($settings)) {
123: self::$_config[$name] = array_merge($current, $settings);
124: }
125:
126: if (empty(self::$_config[$name]['engine'])) {
127: return false;
128: }
129:
130: $engine = self::$_config[$name]['engine'];
131:
132: if (!isset(self::$_engines[$name])) {
133: self::_buildEngine($name);
134: $settings = self::$_config[$name] = self::settings($name);
135: } elseif ($settings = self::set(self::$_config[$name], null, $name)) {
136: self::$_config[$name] = $settings;
137: }
138: return compact('engine', 'settings');
139: }
140:
141: /**
142: * Finds and builds the instance of the required engine class.
143: *
144: * @param string $name Name of the config array that needs an engine instance built
145: * @return boolean
146: * @throws CacheException
147: */
148: protected static function _buildEngine($name) {
149: $config = self::$_config[$name];
150:
151: list($plugin, $class) = pluginSplit($config['engine'], true);
152: $cacheClass = $class . 'Engine';
153: App::uses($cacheClass, $plugin . 'Cache/Engine');
154: if (!class_exists($cacheClass)) {
155: return false;
156: }
157: $cacheClass = $class . 'Engine';
158: if (!is_subclass_of($cacheClass, 'CacheEngine')) {
159: throw new CacheException(__d('cake_dev', 'Cache engines must use CacheEngine as a base class.'));
160: }
161: self::$_engines[$name] = new $cacheClass();
162: if (self::$_engines[$name]->init($config)) {
163: if (self::$_engines[$name]->settings['probability'] && time() % self::$_engines[$name]->settings['probability'] === 0) {
164: self::$_engines[$name]->gc();
165: }
166: return true;
167: }
168: return false;
169: }
170:
171: /**
172: * Returns an array containing the currently configured Cache settings.
173: *
174: * @return array Array of configured Cache config names.
175: */
176: public static function configured() {
177: return array_keys(self::$_config);
178: }
179:
180: /**
181: * Drops a cache engine. Deletes the cache configuration information
182: * If the deleted configuration is the last configuration using an certain engine,
183: * the Engine instance is also unset.
184: *
185: * @param string $name A currently configured cache config you wish to remove.
186: * @return boolean success of the removal, returns false when the config does not exist.
187: */
188: public static function drop($name) {
189: if (!isset(self::$_config[$name])) {
190: return false;
191: }
192: unset(self::$_config[$name], self::$_engines[$name]);
193: return true;
194: }
195:
196: /**
197: * Temporarily change the settings on a cache config. The settings will persist for the next write
198: * operation (write, decrement, increment, clear). Any reads that are done before the write, will
199: * use the modified settings. If `$settings` is empty, the settings will be reset to the
200: * original configuration.
201: *
202: * Can be called with 2 or 3 parameters. To set multiple values at once.
203: *
204: * `Cache::set(array('duration' => '+30 minutes'), 'my_config');`
205: *
206: * Or to set one value.
207: *
208: * `Cache::set('duration', '+30 minutes', 'my_config');`
209: *
210: * To reset a config back to the originally configured values.
211: *
212: * `Cache::set(null, 'my_config');`
213: *
214: * @param mixed $settings Optional string for simple name-value pair or array
215: * @param string $value Optional for a simple name-value pair
216: * @param string $config The configuration name you are changing. Defaults to 'default'
217: * @return array Array of settings.
218: */
219: public static function set($settings = array(), $value = null, $config = 'default') {
220: if (is_array($settings) && $value !== null) {
221: $config = $value;
222: }
223: if (!isset(self::$_config[$config]) || !isset(self::$_engines[$config])) {
224: return false;
225: }
226: if (!empty($settings)) {
227: self::$_reset = true;
228: }
229:
230: if (self::$_reset === true) {
231: if (empty($settings)) {
232: self::$_reset = false;
233: $settings = self::$_config[$config];
234: } else {
235: if (is_string($settings) && $value !== null) {
236: $settings = array($settings => $value);
237: }
238: $settings = array_merge(self::$_config[$config], $settings);
239: if (isset($settings['duration']) && !is_numeric($settings['duration'])) {
240: $settings['duration'] = strtotime($settings['duration']) - time();
241: }
242: }
243: self::$_engines[$config]->settings = $settings;
244: }
245: return self::settings($config);
246: }
247:
248: /**
249: * Garbage collection
250: *
251: * Permanently remove all expired and deleted data
252: *
253: * @param string $config The config name you wish to have garbage collected. Defaults to 'default'
254: * @return void
255: */
256: public static function gc($config = 'default') {
257: self::$_engines[$config]->gc();
258: }
259:
260: /**
261: * Write data for key into cache. Will automatically use the currently
262: * active cache configuration. To set the currently active configuration use
263: * Cache::config()
264: *
265: * ### Usage:
266: *
267: * Writing to the active cache config:
268: *
269: * `Cache::write('cached_data', $data);`
270: *
271: * Writing to a specific cache config:
272: *
273: * `Cache::write('cached_data', $data, 'long_term');`
274: *
275: * @param string $key Identifier for the data
276: * @param mixed $value Data to be cached - anything except a resource
277: * @param string $config Optional string configuration name to write to. Defaults to 'default'
278: * @return boolean True if the data was successfully cached, false on failure
279: */
280: public static function write($key, $value, $config = 'default') {
281: $settings = self::settings($config);
282:
283: if (empty($settings)) {
284: return false;
285: }
286: if (!self::isInitialized($config)) {
287: return false;
288: }
289: $key = self::$_engines[$config]->key($key);
290:
291: if (!$key || is_resource($value)) {
292: return false;
293: }
294:
295: $success = self::$_engines[$config]->write($settings['prefix'] . $key, $value, $settings['duration']);
296: self::set(null, $config);
297: if ($success === false && $value !== '') {
298: trigger_error(
299: __d('cake_dev',
300: "%s cache was unable to write '%s' to %s cache",
301: $config,
302: $key,
303: self::$_engines[$config]->settings['engine']
304: ),
305: E_USER_WARNING
306: );
307: }
308: return $success;
309: }
310:
311: /**
312: * Read a key from the cache. Will automatically use the currently
313: * active cache configuration. To set the currently active configuration use
314: * Cache::config()
315: *
316: * ### Usage:
317: *
318: * Reading from the active cache configuration.
319: *
320: * `Cache::read('my_data');`
321: *
322: * Reading from a specific cache configuration.
323: *
324: * `Cache::read('my_data', 'long_term');`
325: *
326: * @param string $key Identifier for the data
327: * @param string $config optional name of the configuration to use. Defaults to 'default'
328: * @return mixed The cached data, or false if the data doesn't exist, has expired, or if there was an error fetching it
329: */
330: public static function read($key, $config = 'default') {
331: $settings = self::settings($config);
332:
333: if (empty($settings)) {
334: return false;
335: }
336: if (!self::isInitialized($config)) {
337: return false;
338: }
339: $key = self::$_engines[$config]->key($key);
340: if (!$key) {
341: return false;
342: }
343: return self::$_engines[$config]->read($settings['prefix'] . $key);
344: }
345:
346: /**
347: * Increment a number under the key and return incremented value.
348: *
349: * @param string $key Identifier for the data
350: * @param integer $offset How much to add
351: * @param string $config Optional string configuration name. Defaults to 'default'
352: * @return mixed new value, or false if the data doesn't exist, is not integer,
353: * or if there was an error fetching it.
354: */
355: public static function increment($key, $offset = 1, $config = 'default') {
356: $settings = self::settings($config);
357:
358: if (empty($settings)) {
359: return false;
360: }
361: if (!self::isInitialized($config)) {
362: return false;
363: }
364: $key = self::$_engines[$config]->key($key);
365:
366: if (!$key || !is_integer($offset) || $offset < 0) {
367: return false;
368: }
369: $success = self::$_engines[$config]->increment($settings['prefix'] . $key, $offset);
370: self::set(null, $config);
371: return $success;
372: }
373:
374: /**
375: * Decrement a number under the key and return decremented value.
376: *
377: * @param string $key Identifier for the data
378: * @param integer $offset How much to subtract
379: * @param string $config Optional string configuration name. Defaults to 'default'
380: * @return mixed new value, or false if the data doesn't exist, is not integer,
381: * or if there was an error fetching it
382: */
383: public static function decrement($key, $offset = 1, $config = 'default') {
384: $settings = self::settings($config);
385:
386: if (empty($settings)) {
387: return false;
388: }
389: if (!self::isInitialized($config)) {
390: return false;
391: }
392: $key = self::$_engines[$config]->key($key);
393:
394: if (!$key || !is_integer($offset) || $offset < 0) {
395: return false;
396: }
397: $success = self::$_engines[$config]->decrement($settings['prefix'] . $key, $offset);
398: self::set(null, $config);
399: return $success;
400: }
401:
402: /**
403: * Delete a key from the cache.
404: *
405: * ### Usage:
406: *
407: * Deleting from the active cache configuration.
408: *
409: * `Cache::delete('my_data');`
410: *
411: * Deleting from a specific cache configuration.
412: *
413: * `Cache::delete('my_data', 'long_term');`
414: *
415: * @param string $key Identifier for the data
416: * @param string $config name of the configuration to use. Defaults to 'default'
417: * @return boolean True if the value was successfully deleted, false if it didn't exist or couldn't be removed
418: */
419: public static function delete($key, $config = 'default') {
420: $settings = self::settings($config);
421:
422: if (empty($settings)) {
423: return false;
424: }
425: if (!self::isInitialized($config)) {
426: return false;
427: }
428: $key = self::$_engines[$config]->key($key);
429: if (!$key) {
430: return false;
431: }
432:
433: $success = self::$_engines[$config]->delete($settings['prefix'] . $key);
434: self::set(null, $config);
435: return $success;
436: }
437:
438: /**
439: * Delete all keys from the cache.
440: *
441: * @param boolean $check if true will check expiration, otherwise delete all
442: * @param string $config name of the configuration to use. Defaults to 'default'
443: * @return boolean True if the cache was successfully cleared, false otherwise
444: */
445: public static function clear($check = false, $config = 'default') {
446: if (!self::isInitialized($config)) {
447: return false;
448: }
449: $success = self::$_engines[$config]->clear($check);
450: self::set(null, $config);
451: return $success;
452: }
453:
454: /**
455: * Check if Cache has initialized a working config for the given name.
456: *
457: * @param string $config name of the configuration to use. Defaults to 'default'
458: * @return boolean Whether or not the config name has been initialized.
459: */
460: public static function isInitialized($config = 'default') {
461: if (Configure::read('Cache.disable')) {
462: return false;
463: }
464: return isset(self::$_engines[$config]);
465: }
466:
467: /**
468: * Return the settings for the named cache engine.
469: *
470: * @param string $name Name of the configuration to get settings for. Defaults to 'default'
471: * @return array list of settings for this engine
472: * @see Cache::config()
473: */
474: public static function settings($name = 'default') {
475: if (!empty(self::$_engines[$name])) {
476: return self::$_engines[$name]->settings();
477: }
478: return array();
479: }
480:
481: }
482:
483: