source: Dev/branches/cakephp/cake/libs/cache.php @ 126

Last change on this file since 126 was 126, checked in by fpvanagthoven, 14 years ago

Cakephp branch.

File size: 18.4 KB
Line 
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-2011, 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-2011, 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 */
28class 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 */
571class 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}
Note: See TracBrowser for help on using the repository browser.