1: <?php
2: /**
3: * Caching for CakePHP.
4: *
5: *
6: * PHP versions 4 and 5
7: *
8: * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
9: * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
10: *
11: * Licensed under The MIT License
12: * Redistributions of files must retain the above copyright notice.
13: *
14: * @copyright Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
15: * @link http://cakephp.org CakePHP(tm) Project
16: * @package cake
17: * @subpackage cake.cake.libs
18: * @since CakePHP(tm) v 1.2.0.4933
19: * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
20: */
21:
22: /**
23: * Caching for CakePHP.
24: *
25: * @package cake
26: * @subpackage cake.cake.libs
27: */
28: class Cache {
29:
30: /**
31: * Cache configuration stack
32: * Keeps the permanent/default settings for each cache engine.
33: * These settings are used to reset the engines after temporary modification.
34: *
35: * @var array
36: * @access private
37: */
38: var $__config = array();
39:
40: /**
41: * Holds name of the current configuration name being used.
42: *
43: * @var array
44: * @access private
45: */
46: var $__name = 'default';
47:
48: /**
49: * Whether to reset the settings with the next call to Cache::set();
50: *
51: * @var array
52: * @access private
53: */
54: var $__reset = false;
55:
56: /**
57: * Engine instances keyed by configuration name.
58: *
59: * @var array
60: */
61: var $_engines = array();
62:
63: /**
64: * Returns a singleton instance
65: *
66: * @return object
67: * @access public
68: * @static
69: */
70: function &getInstance() {
71: static $instance = array();
72: if (!$instance) {
73: $instance[0] =& new Cache();
74: }
75: return $instance[0];
76: }
77:
78: /**
79: * Set the cache configuration to use. config() can
80: * both create new configurations, return the settings for already configured
81: * configurations. It also sets the 'default' configuration to use for subsequent
82: * operations.
83: *
84: * To create a new configuration:
85: *
86: * `Cache::config('my_config', array('engine' => 'File', 'path' => TMP));`
87: *
88: * To get the settings for a configuration, and set it as the currently selected configuration
89: *
90: * `Cache::config('default');`
91: *
92: * The following keys are used in core cache engines:
93: *
94: * - `duration` Specify how long items in this cache configuration last.
95: * - `prefix` Prefix appended to all entries. Good for when you need to share a keyspace
96: * with either another cache config or annother application.
97: * - `probability` Probability of hitting a cache gc cleanup. Setting to 0 will disable
98: * cache::gc from ever being called automatically.
99: * - `servers' Used by memcache. Give the address of the memcached servers to use.
100: * - `compress` Used by memcache. Enables memcache's compressed format.
101: * - `serialize` Used by FileCache. Should cache objects be serialized first.
102: * - `path` Used by FileCache. Path to where cachefiles should be saved.
103: * - `lock` Used by FileCache. Should files be locked before writing to them?
104: * - `user` Used by Xcache. Username for XCache
105: * - `password` Used by Xcache. Password for XCache
106: *
107: * @see app/config/core.php for configuration settings
108: * @param string $name Name of the configuration
109: * @param array $settings Optional associative array of settings passed to the engine
110: * @return array(engine, settings) on success, false on failure
111: * @access public
112: * @static
113: */
114: function config($name = null, $settings = array()) {
115: $self =& Cache::getInstance();
116: if (is_array($name)) {
117: $settings = $name;
118: }
119:
120: if ($name === null || !is_string($name)) {
121: $name = $self->__name;
122: }
123:
124: $current = array();
125: if (isset($self->__config[$name])) {
126: $current = $self->__config[$name];
127: }
128:
129: if (!empty($settings)) {
130: $self->__config[$name] = array_merge($current, $settings);
131: }
132:
133: if (empty($self->__config[$name]['engine'])) {
134: return false;
135: }
136:
137: $engine = $self->__config[$name]['engine'];
138: $self->__name = $name;
139:
140: if (!isset($self->_engines[$name])) {
141: $self->_buildEngine($name);
142: $settings = $self->__config[$name] = $self->settings($name);
143: } elseif ($settings = $self->set($self->__config[$name])) {
144: $self->__config[$name] = $settings;
145: }
146: return compact('engine', 'settings');
147: }
148:
149: /**
150: * Finds and builds the instance of the required engine class.
151: *
152: * @param string $name Name of the config array that needs an engine instance built
153: * @return void
154: * @access protected
155: */
156: function _buildEngine($name) {
157: $config = $this->__config[$name];
158:
159: list($plugin, $class) = pluginSplit($config['engine']);
160: $cacheClass = $class . 'Engine';
161: if (!class_exists($cacheClass) && $this->__loadEngine($class, $plugin) === false) {
162: return false;
163: }
164: $cacheClass = $class . 'Engine';
165: $this->_engines[$name] =& new $cacheClass();
166: if ($this->_engines[$name]->init($config)) {
167: if ($this->_engines[$name]->settings['probability'] && time() % $this->_engines[$name]->settings['probability'] === 0) {
168: $this->_engines[$name]->gc();
169: }
170: return true;
171: }
172: return false;
173: }
174:
175: /**
176: * Returns an array containing the currently configured Cache settings.
177: *
178: * @return array Array of configured Cache config names.
179: */
180: function configured() {
181: $self =& Cache::getInstance();
182: return array_keys($self->__config);
183: }
184:
185: /**
186: * Drops a cache engine. Deletes the cache configuration information
187: * If the deleted configuration is the last configuration using an certain engine,
188: * the Engine instance is also unset.
189: *
190: * @param string $name A currently configured cache config you wish to remove.
191: * @return boolen success of the removal, returns false when the config does not exist.
192: */
193: function drop($name) {
194: $self =& Cache::getInstance();
195: if (!isset($self->__config[$name])) {
196: return false;
197: }
198: unset($self->__config[$name]);
199: unset($self->_engines[$name]);
200: return true;
201: }
202:
203: /**
204: * Tries to find and include a file for a cache engine and returns object instance
205: *
206: * @param $name Name of the engine (without 'Engine')
207: * @return mixed $engine object or null
208: * @access private
209: */
210: function __loadEngine($name, $plugin = null) {
211: if ($plugin) {
212: return App::import('Lib', $plugin . '.cache' . DS . $name, false);
213: } else {
214: $core = App::core();
215: $path = $core['libs'][0] . 'cache' . DS . strtolower($name) . '.php';
216: if (file_exists($path)) {
217: require $path;
218: return true;
219: }
220: return App::import('Lib', 'cache' . DS . $name, false);
221: }
222: }
223:
224: /**
225: * Temporarily change settings to current config options. if no params are passed, resets settings if needed
226: * Cache::write() will reset the configuration changes made
227: *
228: * @param mixed $settings Optional string for simple name-value pair or array
229: * @param string $value Optional for a simple name-value pair
230: * @return array Array of settings.
231: * @access public
232: * @static
233: */
234: function set($settings = array(), $value = null) {
235: $self =& Cache::getInstance();
236: if (!isset($self->__config[$self->__name]) || !isset($self->_engines[$self->__name])) {
237: return false;
238: }
239: $name = $self->__name;
240: if (!empty($settings)) {
241: $self->__reset = true;
242: }
243:
244: if ($self->__reset === true) {
245: if (empty($settings)) {
246: $self->__reset = false;
247: $settings = $self->__config[$name];
248: } else {
249: if (is_string($settings) && $value !== null) {
250: $settings = array($settings => $value);
251: }
252: $settings = array_merge($self->__config[$name], $settings);
253: if (isset($settings['duration']) && !is_numeric($settings['duration'])) {
254: $settings['duration'] = strtotime($settings['duration']) - time();
255: }
256: }
257: $self->_engines[$name]->settings = $settings;
258: }
259: return $self->settings($name);
260: }
261:
262: /**
263: * Garbage collection
264: *
265: * Permanently remove all expired and deleted data
266: *
267: * @return void
268: * @access public
269: * @static
270: */
271: function gc() {
272: $self =& Cache::getInstance();
273: $self->_engines[$self->__name]->gc();
274: }
275:
276: /**
277: * Write data for key into cache. Will automatically use the currently
278: * active cache configuration. To set the currently active configuration use
279: * Cache::config()
280: *
281: * ### Usage:
282: *
283: * Writing to the active cache config:
284: *
285: * `Cache::write('cached_data', $data);`
286: *
287: * Writing to a specific cache config:
288: *
289: * `Cache::write('cached_data', $data, 'long_term');`
290: *
291: * @param string $key Identifier for the data
292: * @param mixed $value Data to be cached - anything except a resource
293: * @param string $config Optional string configuration name to write to.
294: * @return boolean True if the data was successfully cached, false on failure
295: * @access public
296: * @static
297: */
298: function write($key, $value, $config = null) {
299: $self =& Cache::getInstance();
300:
301: if (!$config) {
302: $config = $self->__name;
303: }
304: $settings = $self->settings($config);
305:
306: if (empty($settings)) {
307: return null;
308: }
309: if (!$self->isInitialized($config)) {
310: return false;
311: }
312: $key = $self->_engines[$config]->key($key);
313:
314: if (!$key || is_resource($value)) {
315: return false;
316: }
317:
318: $success = $self->_engines[$config]->write($settings['prefix'] . $key, $value, $settings['duration']);
319: $self->set();
320: return $success;
321: }
322:
323: /**
324: * Read a key from the cache. Will automatically use the currently
325: * active cache configuration. To set the currently active configuration use
326: * Cache::config()
327: *
328: * ### Usage:
329: *
330: * Reading from the active cache configuration.
331: *
332: * `Cache::read('my_data');`
333: *
334: * Reading from a specific cache configuration.
335: *
336: * `Cache::read('my_data', 'long_term');`
337: *
338: * @param string $key Identifier for the data
339: * @param string $config optional name of the configuration to use.
340: * @return mixed The cached data, or false if the data doesn't exist, has expired, or if there was an error fetching it
341: * @access public
342: * @static
343: */
344: function read($key, $config = null) {
345: $self =& Cache::getInstance();
346:
347: if (!$config) {
348: $config = $self->__name;
349: }
350: $settings = $self->settings($config);
351:
352: if (empty($settings)) {
353: return null;
354: }
355: if (!$self->isInitialized($config)) {
356: return false;
357: }
358: $key = $self->_engines[$config]->key($key);
359: if (!$key) {
360: return false;
361: }
362: $success = $self->_engines[$config]->read($settings['prefix'] . $key);
363:
364: if ($config !== null && $config !== $self->__name) {
365: $self->set();
366: }
367: return $success;
368: }
369:
370: /**
371: * Increment a number under the key and return incremented value.
372: *
373: * @param string $key Identifier for the data
374: * @param integer $offset How much to add
375: * @param string $config Optional string configuration name. If not specified the current
376: * default config will be used.
377: * @return mixed new value, or false if the data doesn't exist, is not integer,
378: * or if there was an error fetching it.
379: * @access public
380: */
381: function increment($key, $offset = 1, $config = null) {
382: $self =& Cache::getInstance();
383:
384: if (!$config) {
385: $config = $self->__name;
386: }
387: $settings = $self->settings($config);
388:
389: if (empty($settings)) {
390: return null;
391: }
392: if (!$self->isInitialized($config)) {
393: return false;
394: }
395: $key = $self->_engines[$config]->key($key);
396:
397: if (!$key || !is_integer($offset) || $offset < 0) {
398: return false;
399: }
400: $success = $self->_engines[$config]->increment($settings['prefix'] . $key, $offset);
401: $self->set();
402: return $success;
403: }
404: /**
405: * Decrement a number under the key and return decremented value.
406: *
407: * @param string $key Identifier for the data
408: * @param integer $offset How much to substract
409: * @param string $config Optional string configuration name, if not specified the current
410: * default config will be used.
411: * @return mixed new value, or false if the data doesn't exist, is not integer,
412: * or if there was an error fetching it
413: * @access public
414: */
415: function decrement($key, $offset = 1, $config = null) {
416: $self =& Cache::getInstance();
417:
418: if (!$config) {
419: $config = $self->__name;
420: }
421: $settings = $self->settings($config);
422:
423: if (empty($settings)) {
424: return null;
425: }
426: if (!$self->isInitialized($config)) {
427: return false;
428: }
429: $key = $self->_engines[$config]->key($key);
430:
431: if (!$key || !is_integer($offset) || $offset < 0) {
432: return false;
433: }
434: $success = $self->_engines[$config]->decrement($settings['prefix'] . $key, $offset);
435: $self->set();
436: return $success;
437: }
438: /**
439: * Delete a key from the cache. Will automatically use the currently
440: * active cache configuration. To set the currently active configuration use
441: * Cache::config()
442: *
443: * ### Usage:
444: *
445: * Deleting from the active cache configuration.
446: *
447: * `Cache::delete('my_data');`
448: *
449: * Deleting from a specific cache configuration.
450: *
451: * `Cache::delete('my_data', 'long_term');`
452: *
453: * @param string $key Identifier for the data
454: * @param string $config name of the configuration to use
455: * @return boolean True if the value was succesfully deleted, false if it didn't exist or couldn't be removed
456: * @access public
457: * @static
458: */
459: function delete($key, $config = null) {
460: $self =& Cache::getInstance();
461: if (!$config) {
462: $config = $self->__name;
463: }
464: $settings = $self->settings($config);
465:
466: if (empty($settings)) {
467: return null;
468: }
469: if (!$self->isInitialized($config)) {
470: return false;
471: }
472: $key = $self->_engines[$config]->key($key);
473: if (!$key) {
474: return false;
475: }
476:
477: $success = $self->_engines[$config]->delete($settings['prefix'] . $key);
478: $self->set();
479: return $success;
480: }
481:
482: /**
483: * Delete all keys from the cache.
484: *
485: * @param boolean $check if true will check expiration, otherwise delete all
486: * @param string $config name of the configuration to use
487: * @return boolean True if the cache was succesfully cleared, false otherwise
488: * @access public
489: * @static
490: */
491: function clear($check = false, $config = null) {
492: $self =& Cache::getInstance();
493: if (!$config) {
494: $config = $self->__name;
495: }
496: $settings = $self->settings($config);
497:
498: if (empty($settings)) {
499: return null;
500: }
501:
502: if (!$self->isInitialized($config)) {
503: return false;
504: }
505: $success = $self->_engines[$config]->clear($check);
506: $self->set();
507: return $success;
508: }
509:
510: /**
511: * Check if Cache has initialized a working config for the given name.
512: *
513: * @param string $engine Name of the engine
514: * @param string $config Name of the configuration setting
515: * @return bool Whether or not the config name has been initialized.
516: * @access public
517: * @static
518: */
519: function isInitialized($name = null) {
520: if (Configure::read('Cache.disable')) {
521: return false;
522: }
523: $self =& Cache::getInstance();
524: if (!$name && isset($self->__config[$self->__name])) {
525: $name = $self->__name;
526: }
527: return isset($self->_engines[$name]);
528: }
529:
530: /**
531: * Return the settings for current cache engine. If no name is supplied the settings
532: * for the 'active default' configuration will be returned. To set the 'active default'
533: * configuration use `Cache::config()`
534: *
535: * @param string $engine Name of the configuration to get settings for.
536: * @return array list of settings for this engine
537: * @see Cache::config()
538: * @access public
539: * @static
540: */
541: function settings($name = null) {
542: $self =& Cache::getInstance();
543: if (!$name && isset($self->__config[$self->__name])) {
544: $name = $self->__name;
545: }
546: if (!empty($self->_engines[$name])) {
547: return $self->_engines[$name]->settings();
548: }
549: return array();
550: }
551:
552: /**
553: * Write the session when session data is persisted with cache.
554: *
555: * @return void
556: * @access public
557: */
558: function __destruct() {
559: if (Configure::read('Session.save') == 'cache' && function_exists('session_write_close')) {
560: session_write_close();
561: }
562: }
563: }
564:
565: /**
566: * Storage engine for CakePHP caching
567: *
568: * @package cake
569: * @subpackage cake.cake.libs
570: */
571: class CacheEngine {
572:
573: /**
574: * Settings of current engine instance
575: *
576: * @var int
577: * @access public
578: */
579: var $settings = array();
580:
581: /**
582: * Initialize the cache engine
583: *
584: * Called automatically by the cache frontend
585: *
586: * @param array $params Associative array of parameters for the engine
587: * @return boolean True if the engine has been succesfully initialized, false if not
588: * @access public
589: */
590: function init($settings = array()) {
591: $this->settings = array_merge(
592: array('prefix' => 'cake_', 'duration'=> 3600, 'probability'=> 100),
593: $this->settings,
594: $settings
595: );
596: if (!is_numeric($this->settings['duration'])) {
597: $this->settings['duration'] = strtotime($this->settings['duration']) - time();
598: }
599: return true;
600: }
601:
602: /**
603: * Garbage collection
604: *
605: * Permanently remove all expired and deleted data
606: *
607: * @access public
608: */
609: function gc() {
610: }
611:
612: /**
613: * Write value for a key into cache
614: *
615: * @param string $key Identifier for the data
616: * @param mixed $value Data to be cached
617: * @param mixed $duration How long to cache the data, in seconds
618: * @return boolean True if the data was succesfully cached, false on failure
619: * @access public
620: */
621: function write($key, &$value, $duration) {
622: trigger_error(sprintf(__('Method write() not implemented in %s', true), get_class($this)), E_USER_ERROR);
623: }
624:
625: /**
626: * Read a key from the cache
627: *
628: * @param string $key Identifier for the data
629: * @return mixed The cached data, or false if the data doesn't exist, has expired, or if there was an error fetching it
630: * @access public
631: */
632: function read($key) {
633: trigger_error(sprintf(__('Method read() not implemented in %s', true), get_class($this)), E_USER_ERROR);
634: }
635:
636: /**
637: * Increment a number under the key and return incremented value
638: *
639: * @param string $key Identifier for the data
640: * @param integer $offset How much to add
641: * @return New incremented value, false otherwise
642: * @access public
643: */
644: function increment($key, $offset = 1) {
645: trigger_error(sprintf(__('Method increment() not implemented in %s', true), get_class($this)), E_USER_ERROR);
646: }
647: /**
648: * Decrement a number under the key and return decremented value
649: *
650: * @param string $key Identifier for the data
651: * @param integer $value How much to substract
652: * @return New incremented value, false otherwise
653: * @access public
654: */
655: function decrement($key, $offset = 1) {
656: trigger_error(sprintf(__('Method decrement() not implemented in %s', true), get_class($this)), E_USER_ERROR);
657: }
658: /**
659: * Delete a key from the cache
660: *
661: * @param string $key Identifier for the data
662: * @return boolean True if the value was succesfully deleted, false if it didn't exist or couldn't be removed
663: * @access public
664: */
665: function delete($key) {
666: }
667:
668: /**
669: * Delete all keys from the cache
670: *
671: * @param boolean $check if true will check expiration, otherwise delete all
672: * @return boolean True if the cache was succesfully cleared, false otherwise
673: * @access public
674: */
675: function clear($check) {
676: }
677:
678: /**
679: * Cache Engine settings
680: *
681: * @return array settings
682: * @access public
683: */
684: function settings() {
685: return $this->settings;
686: }
687:
688: /**
689: * Generates a safe key for use with cache engine storage engines.
690: *
691: * @param string $key the key passed over
692: * @return mixed string $key or false
693: * @access public
694: */
695: function key($key) {
696: if (empty($key)) {
697: return false;
698: }
699: $key = Inflector::underscore(str_replace(array(DS, '/', '.'), '_', strval($key)));
700: return $key;
701: }
702: }
703: