1<?php 2// +----------------------------------------------------------------------+ 3// | PEAR :: Cache | 4// +----------------------------------------------------------------------+ 5// | Copyright (c) 1997-2003 The PHP Group | 6// +----------------------------------------------------------------------+ 7// | This source file is subject to version 2.0 of the PHP license, | 8// | that is bundled with this package in the file LICENSE, and is | 9// | available at through the world-wide-web at | 10// | http://www.php.net/license/2_02.txt. | 11// | If you did not receive a copy of the PHP license and are unable to | 12// | obtain it through the world-wide-web, please send a note to | 13// | license@php.net so we can mail you a copy immediately. | 14// +----------------------------------------------------------------------+ 15// | Authors: Ulf Wendel <ulf.wendel@phpdoc.de> | 16// | Sebastian Bergmann <sb@sebastian-bergmann.de> | 17// +----------------------------------------------------------------------+ 18// 19// $Id: Cache.php 267047 2008-10-07 08:58:46Z dufuz $ 20 21require_once 'PEAR.php'; 22require_once 'Cache/Error.php'; 23 24/** 25* Cache is a base class for cache implementations. 26* 27* The pear cache module is a generic data cache which can be used to 28* cache script runs. The idea behind the cache is quite simple. If you have 29* the same input parameters for whatever tasks/algorithm you use you'll 30* usually get the same output. So why not caching templates, functions calls, 31* graphic generation etc. Caching certain actions e.g. XSLT tranformations 32* saves you lots of time. 33* 34* The design of the cache reminds of PHPLibs session implementation. A 35* (PHPLib: session) controller uses storage container (PHPLib: ct_*.inc) to save 36* certain data (PHPLib: session data). In contrast to the session stuff it's up to 37* you to generate an ID for the data to cache. If you're using the output cache 38* you might use the script name as a seed for cache::generateID(), if your using the 39* function cache you'd use an array with all function parameters. 40* 41* Usage example of the generic data cache: 42* 43* require_once('Cache.php'); 44* 45* $cache = new Cache('file', array('cache_dir' => 'cache/') ); 46* $id = $cache->generateID('testentry'); 47* 48* if ($data = $cache->get($id)) { 49* print "Cache hit.<br>Data: $data"; 50* 51* } else { 52* $data = 'data of any kind'; 53* $cache->save($id, $data); 54* print 'Cache miss.<br>'; 55* } 56* 57* WARNING: No File/DB-Table-Row locking is implemented yet, 58* it's possible, that you get corrupted data-entries under 59* bad circumstances (especially with the file container) 60* 61* @author Ulf Wendel <ulf.wendel@phpdoc.de> 62* @version $Id: Cache.php 267047 2008-10-07 08:58:46Z dufuz $ 63* @package Cache 64* @access public 65*/ 66class Cache extends PEAR 67{ 68 69 /** 70 * Enables / disables caching. 71 * 72 * TODO: Add explanation what this is good for. 73 * 74 * @var boolean 75 * @access private 76 */ 77 var $caching = true; 78 79 /** 80 * Garbage collection: probability in seconds 81 * 82 * If set to a value above 0 a garbage collection will 83 * flush all cache entries older than the specified number 84 * of seconds. 85 * 86 * @var integer 87 * @see $gc_probability, $gc_maxlifetime 88 * @access public 89 */ 90 var $gc_time = 1; 91 92 /** 93 * Garbage collection: probability in percent 94 * 95 * TODO: Add an explanation. 96 * 97 * @var integer 0 => never 98 * @see $gc_time, $gc_maxlifetime 99 * @access public 100 */ 101 var $gc_probability = 1; 102 103 /** 104 * Garbage collection: delete all entries not use for n seconds. 105 * 106 * Default is one day, 60 * 60 * 24 = 86400 seconds. 107 * 108 * @var integer 109 * @see $gc_probability, $gc_time 110 */ 111 var $gc_maxlifetime = 86400; 112 113 /** 114 * Storage container object. 115 * 116 * @var object Cache_Container 117 */ 118 var $container; 119 120 // 121 // public methods 122 // 123 124 /** 125 * 126 * @param string Name of container class 127 * @param array Array with container class options 128 */ 129 function Cache($container, $container_options = '') 130 { 131 $this->PEAR(); 132 $container = strtolower($container); 133 $container_class = 'Cache_Container_' . $container; 134 $container_classfile = 'Cache/Container/' . $container . '.php'; 135 136 include_once $container_classfile; 137 $this->container = new $container_class($container_options); 138 } 139 140 //deconstructor 141 function _Cache() 142 { 143 $this->garbageCollection(); 144 } 145 146 /** 147 * Returns the current caching state. 148 * 149 * @return boolean The current caching state. 150 * @access public 151 */ 152 function getCaching() 153 { 154 return $this->caching; 155 } 156 157 /** 158 * Enables or disables caching. 159 * 160 * @param boolean The new caching state. 161 * @access public 162 */ 163 function setCaching($state) 164 { 165 $this->caching = $state; 166 } 167 168 /** 169 * Returns the requested dataset it if exists and is not expired 170 * 171 * @param string dataset ID 172 * @param string cache group 173 * @return mixed cached data or null on failure 174 * @access public 175 */ 176 function get($id, $group = 'default') 177 { 178 if (!$this->caching) { 179 return ''; 180 } 181 182 if ($this->isCached($id, $group) && !$this->isExpired($id, $group)) { 183 return $this->load($id, $group); 184 } 185 return null; 186 } // end func get 187 188 /** 189 * Stores the given data in the cache. 190 * 191 * @param string dataset ID used as cache identifier 192 * @param mixed data to cache 193 * @param integer lifetime of the cached data in seconds - 0 for endless 194 * @param string cache group 195 * @return boolean 196 * @access public 197 */ 198 function save($id, $data, $expires = 0, $group = 'default') 199 { 200 if (!$this->caching) { 201 return true; 202 } 203 return $this->extSave($id, $data, '',$expires, $group); 204 } // end func save 205 206 /** 207 * Stores a dataset with additional userdefined data. 208 * 209 * @param string dataset ID 210 * @param mixed data to store 211 * @param string additional userdefined data 212 * @param mixed userdefined expire date 213 * @param string cache group 214 * @return boolean 215 * @throws Cache_Error 216 * @access public 217 * @see getUserdata() 218 */ 219 function extSave($id, $cachedata, $userdata, $expires = 0, $group = 'default') 220 { 221 if (!$this->caching) { 222 return true; 223 } 224 return $this->container->save($id, $cachedata, $expires, $group, $userdata); 225 } // end func extSave 226 227 /** 228 * Loads the given ID from the cache. 229 * 230 * @param string dataset ID 231 * @param string cache group 232 * @return mixed cached data or null on failure 233 * @access public 234 */ 235 function load($id, $group = 'default') 236 { 237 if (!$this->caching) { 238 return ''; 239 } 240 return $this->container->load($id, $group); 241 } // end func load 242 243 /** 244 * Returns the userdata field of a cached data set. 245 * 246 * @param string dataset ID 247 * @param string cache group 248 * @return string userdata 249 * @access public 250 * @see extSave() 251 */ 252 function getUserdata($id, $group = 'default') 253 { 254 if (!$this->caching) { 255 return ''; 256 } 257 return $this->container->getUserdata($id, $group); 258 } // end func getUserdata 259 260 /** 261 * Removes the specified dataset from the cache. 262 * 263 * @param string dataset ID 264 * @param string cache group 265 * @return boolean 266 * @access public 267 */ 268 function remove($id, $group = 'default') 269 { 270 if (!$this->caching) { 271 return true; 272 } 273 return $this->container->remove($id, $group); 274 } // end func remove 275 276 /** 277 * Flushes the cache - removes all data from it 278 * 279 * @param string cache group, if empty all groups will be flashed 280 * @return integer number of removed datasets 281 */ 282 function flush($group = 'default') 283 { 284 if (!$this->caching) { 285 return true; 286 } 287 return $this->container->flush($group); 288 } // end func flush 289 290 /** 291 * Checks if a dataset exists. 292 * 293 * Note: this does not say that the cached data is not expired! 294 * 295 * @param string dataset ID 296 * @param string cache group 297 * @return boolean 298 * @access public 299 */ 300 function isCached($id, $group = 'default') 301 { 302 if (!$this->caching) { 303 return false; 304 } 305 return $this->container->isCached($id, $group); 306 } // end func isCached 307 308 /** 309 * Checks if a dataset is expired 310 * 311 * @param string dataset ID 312 * @param string cache group 313 * @param integer maximum age for the cached data in seconds - 0 for endless 314 * If the cached data is older but the given lifetime it will 315 * be removed from the cache. You don't have to provide this 316 * argument if you call isExpired(). Every dataset knows 317 * it's expire date and will be removed automatically. Use 318 * this only if you know what you're doing... 319 * @return boolean 320 * @access public 321 */ 322 function isExpired($id, $group = 'default', $max_age = 0) 323 { 324 if (!$this->caching) { 325 return true; 326 } 327 return $this->container->isExpired($id, $group, $max_age); 328 } // end func isExpired 329 330 /** 331 * Generates a "unique" ID for the given value 332 * 333 * This is a quick but dirty hack to get a "unique" ID for a any kind of variable. 334 * ID clashes might occur from time to time although they are extreme unlikely! 335 * 336 * @param mixed variable to generate a ID for 337 * @return string "unique" ID 338 * @access public 339 */ 340 function generateID($variable) 341 { 342 // WARNING: ID clashes are possible although unlikely 343 return md5(serialize($variable)); 344 } 345 346 /** 347 * Calls the garbage collector of the storage object with a certain probability 348 * 349 * @param boolean Force a garbage collection run? 350 * @see $gc_probability, $gc_time 351 */ 352 function garbageCollection($force = false) 353 { 354 static $last_run = 0; 355 356 if (!$this->caching) { 357 return; 358 } 359 360 // time and probability based 361 if (($force) || ($last_run && $last_run < time() + $this->gc_time) || (rand(1, 100) < $this->gc_probability)) { 362 $this->container->garbageCollection($this->gc_maxlifetime); 363 $last_run = time(); 364 } 365 } // end func garbageCollection 366 367} // end class cache 368?> 369