Cake/Cache/Cache.php

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  
484