[ Index ] |
|
Code source de Serendipity 1.2 |
1 <?php 2 3 /** 4 * Fast, light and safe Cache Class 5 * 6 * Cache_Lite is a fast, light and safe cache system. It's optimized 7 * for file containers. It is fast and safe (because it uses file 8 * locking and/or anti-corruption tests). 9 * 10 * There are some examples in the 'docs/examples' file 11 * Technical choices are described in the 'docs/technical' file 12 * 13 * Memory Caching is from an original idea of 14 * Mike BENOIT <ipso@snappymail.ca> 15 * 16 * Nota : A chinese documentation (thanks to RainX <china_1982@163.com>) is 17 * available at : 18 * http://rainx.phpmore.com/manual/cache_lite.html 19 * 20 * @package Cache_Lite 21 * @category Caching 22 * @version $Id: Lite.php,v 1.30 2005/06/13 20:50:48 fab Exp $ 23 * @author Fabien MARTY <fab@php.net> 24 */ 25 26 define('CACHE_LITE_ERROR_RETURN', 1); 27 define('CACHE_LITE_ERROR_DIE', 8); 28 29 class Cache_Lite 30 { 31 32 // --- Private properties --- 33 34 /** 35 * Directory where to put the cache files 36 * (make sure to add a trailing slash) 37 * 38 * @var string $_cacheDir 39 */ 40 var $_cacheDir = '/tmp/'; 41 42 /** 43 * Enable / disable caching 44 * 45 * (can be very usefull for the debug of cached scripts) 46 * 47 * @var boolean $_caching 48 */ 49 var $_caching = true; 50 51 /** 52 * Cache lifetime (in seconds) 53 * 54 * @var int $_lifeTime 55 */ 56 var $_lifeTime = 3600; 57 58 /** 59 * Enable / disable fileLocking 60 * 61 * (can avoid cache corruption under bad circumstances) 62 * 63 * @var boolean $_fileLocking 64 */ 65 var $_fileLocking = true; 66 67 /** 68 * Timestamp of the last valid cache 69 * 70 * @var int $_refreshTime 71 */ 72 var $_refreshTime; 73 74 /** 75 * File name (with path) 76 * 77 * @var string $_file 78 */ 79 var $_file; 80 81 /** 82 * File name (without path) 83 * 84 * @var string $_fileName 85 */ 86 var $_fileName; 87 88 /** 89 * Enable / disable write control (the cache is read just after writing to detect corrupt entries) 90 * 91 * Enable write control will lightly slow the cache writing but not the cache reading 92 * Write control can detect some corrupt cache files but maybe it's not a perfect control 93 * 94 * @var boolean $_writeControl 95 */ 96 var $_writeControl = true; 97 98 /** 99 * Enable / disable read control 100 * 101 * If enabled, a control key is embeded in cache file and this key is compared with the one 102 * calculated after the reading. 103 * 104 * @var boolean $_writeControl 105 */ 106 var $_readControl = true; 107 108 /** 109 * Type of read control (only if read control is enabled) 110 * 111 * Available values are : 112 * 'md5' for a md5 hash control (best but slowest) 113 * 'crc32' for a crc32 hash control (lightly less safe but faster, better choice) 114 * 'strlen' for a length only test (fastest) 115 * 116 * @var boolean $_readControlType 117 */ 118 var $_readControlType = 'crc32'; 119 120 /** 121 * Pear error mode (when raiseError is called) 122 * 123 * (see PEAR doc) 124 * 125 * @see setToDebug() 126 * @var int $_pearErrorMode 127 */ 128 var $_pearErrorMode = CACHE_LITE_ERROR_RETURN; 129 130 /** 131 * Current cache id 132 * 133 * @var string $_id 134 */ 135 var $_id; 136 137 /** 138 * Current cache group 139 * 140 * @var string $_group 141 */ 142 var $_group; 143 144 /** 145 * Enable / Disable "Memory Caching" 146 * 147 * NB : There is no lifetime for memory caching ! 148 * 149 * @var boolean $_memoryCaching 150 */ 151 var $_memoryCaching = false; 152 153 /** 154 * Enable / Disable "Only Memory Caching" 155 * (be carefull, memory caching is "beta quality") 156 * 157 * @var boolean $_onlyMemoryCaching 158 */ 159 var $_onlyMemoryCaching = false; 160 161 /** 162 * Memory caching array 163 * 164 * @var array $_memoryCachingArray 165 */ 166 var $_memoryCachingArray = array(); 167 168 /** 169 * Memory caching counter 170 * 171 * @var int $memoryCachingCounter 172 */ 173 var $_memoryCachingCounter = 0; 174 175 /** 176 * Memory caching limit 177 * 178 * @var int $memoryCachingLimit 179 */ 180 var $_memoryCachingLimit = 1000; 181 182 /** 183 * File Name protection 184 * 185 * if set to true, you can use any cache id or group name 186 * if set to false, it can be faster but cache ids and group names 187 * will be used directly in cache file names so be carefull with 188 * special characters... 189 * 190 * @var boolean $fileNameProtection 191 */ 192 var $_fileNameProtection = true; 193 194 /** 195 * Enable / disable automatic serialization 196 * 197 * it can be used to save directly datas which aren't strings 198 * (but it's slower) 199 * 200 * @var boolean $_serialize 201 */ 202 var $_automaticSerialization = false; 203 204 /** 205 * Disable / Tune the automatic cleaning process 206 * 207 * The automatic cleaning process destroy too old (for the given life time) 208 * cache files when a new cache file is written. 209 * 0 => no automatic cache cleaning 210 * 1 => systematic cache cleaning 211 * x (integer) > 1 => automatic cleaning randomly 1 times on x cache write 212 * 213 * @var int $_automaticCleaning 214 */ 215 var $_automaticCleaningFactor = 0; 216 217 /** 218 * Nested directory level 219 * 220 * Set the hashed directory structure level. 0 means "no hashed directory 221 * structure", 1 means "one level of directory", 2 means "two levels"... 222 * This option can speed up Cache_Lite only when you have many thousands of 223 * cache file. Only specific benchs can help you to choose the perfect value 224 * for you. Maybe, 1 or 2 is a good start. 225 * 226 * @var int $_hashedDirectoryLevel 227 */ 228 var $_hashedDirectoryLevel = 0; 229 230 /** 231 * Umask for hashed directory structure 232 * 233 * @var int $_hashedDirectoryUmask 234 */ 235 var $_hashedDirectoryUmask = 0700; 236 237 // --- Public methods --- 238 239 /** 240 * Constructor 241 * 242 * $options is an assoc. Available options are : 243 * $options = array( 244 * 'cacheDir' => directory where to put the cache files (string), 245 * 'caching' => enable / disable caching (boolean), 246 * 'lifeTime' => cache lifetime in seconds (int), 247 * 'fileLocking' => enable / disable fileLocking (boolean), 248 * 'writeControl' => enable / disable write control (boolean), 249 * 'readControl' => enable / disable read control (boolean), 250 * 'readControlType' => type of read control 'crc32', 'md5', 'strlen' (string), 251 * 'pearErrorMode' => pear error mode (when raiseError is called) (cf PEAR doc) (int), 252 * 'memoryCaching' => enable / disable memory caching (boolean), 253 * 'onlyMemoryCaching' => enable / disable only memory caching (boolean), 254 * 'memoryCachingLimit' => max nbr of records to store into memory caching (int), 255 * 'fileNameProtection' => enable / disable automatic file name protection (boolean), 256 * 'automaticSerialization' => enable / disable automatic serialization (boolean) 257 * 'automaticCleaningFactor' => distable / tune automatic cleaning process (int) 258 * 'hashedDirectoryLevel' => level of the hashed directory system (int) 259 * 'hashedDirectoryUmask' => umask for hashed directory structure (int) 260 * ); 261 * 262 * @param array $options options 263 * @access public 264 */ 265 function Cache_Lite($options = array(NULL)) 266 { 267 $availableOptions = array('hashedDirectoryUmask', 'hashedDirectoryLevel', 'automaticCleaningFactor', 'automaticSerialization', 'fileNameProtection', 'memoryCaching', 'onlyMemoryCaching', 'memoryCachingLimit', 'cacheDir', 'caching', 'lifeTime', 'fileLocking', 'writeControl', 'readControl', 'readControlType', 'pearErrorMode'); 268 foreach($options as $key => $value) { 269 if(in_array($key, $availableOptions)) { 270 $property = '_'.$key; 271 $this->$property = $value; 272 } 273 } 274 $this->_refreshTime = time() - $this->_lifeTime; 275 } 276 277 /** 278 * Test if a cache is available and (if yes) return it 279 * 280 * @param string $id cache id 281 * @param string $group name of the cache group 282 * @param boolean $doNotTestCacheValidity if set to true, the cache validity won't be tested 283 * @return string data of the cache (or false if no cache available) 284 * @access public 285 */ 286 function get($id, $group = 'default', $doNotTestCacheValidity = false) 287 { 288 $this->_id = $id; 289 $this->_group = $group; 290 $data = false; 291 if ($this->_caching) { 292 $this->_setFileName($id, $group); 293 if ($this->_memoryCaching) { 294 if (isset($this->_memoryCachingArray[$this->_file])) { 295 if ($this->_automaticSerialization) { 296 return unserialize($this->_memoryCachingArray[$this->_file]); 297 } else { 298 return $this->_memoryCachingArray[$this->_file]; 299 } 300 } else { 301 if ($this->_onlyMemoryCaching) { 302 return false; 303 } 304 } 305 } 306 if ($doNotTestCacheValidity) { 307 if (file_exists($this->_file)) { 308 $data = $this->_read(); 309 } 310 } else { 311 if ((file_exists($this->_file)) && (@filemtime($this->_file) > $this->_refreshTime)) { 312 $data = $this->_read(); 313 } 314 } 315 if (($data) and ($this->_memoryCaching)) { 316 $this->_memoryCacheAdd($this->_file, $data); 317 } 318 if (($this->_automaticSerialization) and (is_string($data))) { 319 $data = unserialize($data); 320 } 321 return $data; 322 } 323 return false; 324 } 325 326 /** 327 * Save some data in a cache file 328 * 329 * @param string $data data to put in cache (can be another type than strings if automaticSerialization is on) 330 * @param string $id cache id 331 * @param string $group name of the cache group 332 * @return boolean true if no problem 333 * @access public 334 */ 335 function save($data, $id = NULL, $group = 'default') 336 { 337 if ($this->_caching) { 338 if ($this->_automaticSerialization) { 339 $data = serialize($data); 340 } 341 if (isset($id)) { 342 $this->_setFileName($id, $group); 343 } 344 if ($this->_memoryCaching) { 345 $this->_memoryCacheAdd($this->_file, $data); 346 if ($this->_onlyMemoryCaching) { 347 return true; 348 } 349 } 350 if ($this->_automaticCleaningFactor>0) { 351 $rand = rand(1, $this->_automaticCleaningFactor); 352 if ($rand==1) { 353 $this->clean(false, 'old'); 354 } 355 } 356 if ($this->_writeControl) { 357 if (!$this->_writeAndControl($data)) { 358 @touch($this->_file, time() - 2*abs($this->_lifeTime)); 359 return false; 360 } else { 361 return true; 362 } 363 } else { 364 return $this->_write($data); 365 } 366 } 367 return false; 368 } 369 370 /** 371 * Remove a cache file 372 * 373 * @param string $id cache id 374 * @param string $group name of the cache group 375 * @return boolean true if no problem 376 * @access public 377 */ 378 function remove($id, $group = 'default') 379 { 380 $this->_setFileName($id, $group); 381 if ($this->_memoryCaching) { 382 if (isset($this->_memoryCachingArray[$this->_file])) { 383 unset($this->_memoryCachingArray[$this->_file]); 384 $this->_memoryCachingCounter = $this->_memoryCachingCounter - 1; 385 } 386 if ($this->_onlyMemoryCaching) { 387 return true; 388 } 389 } 390 return $this->_unlink($this->_file); 391 } 392 393 /** 394 * Clean the cache 395 * 396 * if no group is specified all cache files will be destroyed 397 * else only cache files of the specified group will be destroyed 398 * 399 * @param string $group name of the cache group 400 * @param string $mode flush cache mode : 'old', 'ingroup', 'notingroup', 401 * 'callback_myFunction' 402 * @return boolean true if no problem 403 * @access public 404 */ 405 function clean($group = false, $mode = 'ingroup') 406 { 407 return $this->_cleanDir($this->_cacheDir, $group, $mode); 408 } 409 410 /** 411 * Set to debug mode 412 * 413 * When an error is found, the script will stop and the message will be displayed 414 * (in debug mode only). 415 * 416 * @access public 417 */ 418 function setToDebug() 419 { 420 $this->_pearErrorMode = CACHE_LITE_ERROR_DIE; 421 } 422 423 /** 424 * Set a new life time 425 * 426 * @param int $newLifeTime new life time (in seconds) 427 * @access public 428 */ 429 function setLifeTime($newLifeTime) 430 { 431 $this->_lifeTime = $newLifeTime; 432 $this->_refreshTime = time() - $newLifeTime; 433 } 434 435 /** 436 * Save the state of the caching memory array into a cache file cache 437 * 438 * @param string $id cache id 439 * @param string $group name of the cache group 440 * @access public 441 */ 442 function saveMemoryCachingState($id, $group = 'default') 443 { 444 if ($this->_caching) { 445 $array = array( 446 'counter' => $this->_memoryCachingCounter, 447 'array' => $this->_memoryCachingState 448 ); 449 $data = serialize($array); 450 $this->save($data, $id, $group); 451 } 452 } 453 454 /** 455 * Load the state of the caching memory array from a given cache file cache 456 * 457 * @param string $id cache id 458 * @param string $group name of the cache group 459 * @param boolean $doNotTestCacheValidity if set to true, the cache validity won't be tested 460 * @access public 461 */ 462 function getMemoryCachingState($id, $group = 'default', $doNotTestCacheValidity = false) 463 { 464 if ($this->_caching) { 465 if ($data = $this->get($id, $group, $doNotTestCacheValidity)) { 466 $array = unserialize($data); 467 $this->_memoryCachingCounter = $array['counter']; 468 $this->_memoryCachingArray = $array['array']; 469 } 470 } 471 } 472 473 /** 474 * Return the cache last modification time 475 * 476 * BE CAREFUL : THIS METHOD IS FOR HACKING ONLY ! 477 * 478 * @return int last modification time 479 */ 480 function lastModified() { 481 return @filemtime($this->_file); 482 } 483 484 /** 485 * Trigger a PEAR error 486 * 487 * To improve performances, the PEAR.php file is included dynamically. 488 * The file is so included only when an error is triggered. So, in most 489 * cases, the file isn't included and perfs are much better. 490 * 491 * @param string $msg error message 492 * @param int $code error code 493 * @access public 494 */ 495 function raiseError($msg, $code) 496 { 497 include_once(dirname(__FILE__) . '/../PEAR.php'); 498 PEAR::raiseError($msg, $code, $this->_pearErrorMode); 499 } 500 501 // --- Private methods --- 502 503 /** 504 * Remove a file 505 * 506 * @param string $file complete file path and name 507 * @return boolean true if no problem 508 * @access private 509 */ 510 function _unlink($file) 511 { 512 if (!@unlink($file)) { 513 $this->raiseError('Cache_Lite : Unable to remove cache !', -3); 514 return false; 515 } else { 516 return true; 517 } 518 } 519 520 /** 521 * Recursive function for cleaning cache file in the given directory 522 * 523 * @param string $dir directory complete path (with a trailing slash) 524 * @param string $group name of the cache group 525 * @param string $mode flush cache mode : 'old', 'ingroup', 'notingroup', 526 'callback_myFunction' 527 * @return boolean true if no problem 528 * @access private 529 */ 530 function _cleanDir($dir, $group = false, $mode = 'ingroup') 531 { 532 if ($this->_fileNameProtection) { 533 $motif = ($group) ? 'cache_'.md5($group).'_' : 'cache_'; 534 } else { 535 $motif = ($group) ? 'cache_'.$group.'_' : 'cache_'; 536 } 537 if ($this->_memoryCaching) { 538 while (list($key, $value) = each($this->_memoryCachingArray)) { 539 if (strpos($key, $motif, 0)) { 540 unset($this->_memoryCachingArray[$key]); 541 $this->_memoryCachingCounter = $this->_memoryCachingCounter - 1; 542 } 543 } 544 if ($this->_onlyMemoryCaching) { 545 return true; 546 } 547 } 548 if (!($dh = opendir($dir))) { 549 $this->raiseError('Cache_Lite : Unable to open cache directory !', -4); 550 return false; 551 } 552 $result = true; 553 while ($file = readdir($dh)) { 554 if (($file != '.') && ($file != '..')) { 555 if (substr($file, 0, 6)=='cache_') { 556 $file2 = $dir . $file; 557 if (is_file($file2)) { 558 switch (substr($mode, 0, 9)) { 559 case 'old': 560 // files older than lifeTime get deleted from cache 561 if ((mktime() - filemtime($file2)) > $this->_lifeTime) { 562 $result = ($result and ($this->_unlink($file2))); 563 } 564 break; 565 case 'notingrou': 566 if (!strpos($file2, $motif, 0)) { 567 $result = ($result and ($this->_unlink($file2))); 568 } 569 break; 570 case 'callback_': 571 $func = substr($mode, 9, strlen($mode) - 9); 572 if ($func($file2, $group)) { 573 $result = ($result and ($this->_unlink($file2))); 574 } 575 break; 576 case 'ingroup': 577 default: 578 if (strpos($file2, $motif, 0)) { 579 $result = ($result and ($this->_unlink($file2))); 580 } 581 break; 582 } 583 } 584 if ((is_dir($file2)) and ($this->_hashedDirectoryLevel>0)) { 585 $result = ($result and ($this->_cleanDir($file2 . '/', $group, $mode))); 586 } 587 } 588 } 589 } 590 return $result; 591 } 592 593 /** 594 * Add some date in the memory caching array 595 * 596 * @param string $id cache id 597 * @param string $data data to cache 598 * @access private 599 */ 600 function _memoryCacheAdd($id, $data) 601 { 602 $this->_memoryCachingArray[$this->_file] = $data; 603 if ($this->_memoryCachingCounter >= $this->_memoryCachingLimit) { 604 list($key, $value) = each($this->_memoryCachingArray); 605 unset($this->_memoryCachingArray[$key]); 606 } else { 607 $this->_memoryCachingCounter = $this->_memoryCachingCounter + 1; 608 } 609 } 610 611 /** 612 * Make a file name (with path) 613 * 614 * @param string $id cache id 615 * @param string $group name of the group 616 * @access private 617 */ 618 function _setFileName($id, $group) 619 { 620 621 if ($this->_fileNameProtection) { 622 $suffix = 'cache_'.md5($group).'_'.md5($id); 623 } else { 624 $suffix = 'cache_'.$group.'_'.$id; 625 } 626 $root = $this->_cacheDir; 627 if ($this->_hashedDirectoryLevel>0) { 628 $hash = md5($suffix); 629 for ($i=0 ; $i<$this->_hashedDirectoryLevel ; $i++) { 630 $root = $root . 'cache_' . substr($hash, 0, $i + 1) . '/'; 631 } 632 } 633 $this->_fileName = $suffix; 634 $this->_file = $root.$suffix; 635 } 636 637 /** 638 * Read the cache file and return the content 639 * 640 * @return string content of the cache file 641 * @access private 642 */ 643 function _read() 644 { 645 $fp = @fopen($this->_file, "rb"); 646 if ($this->_fileLocking) @flock($fp, LOCK_SH); 647 if ($fp) { 648 clearstatcache(); // because the filesize can be cached by PHP itself... 649 $length = @filesize($this->_file); 650 $mqr = get_magic_quotes_runtime(); 651 set_magic_quotes_runtime(0); 652 if ($this->_readControl) { 653 $hashControl = @fread($fp, 32); 654 $length = $length - 32; 655 } 656 if ($length) { 657 $data = @fread($fp, $length); 658 } else { 659 $data = ''; 660 } 661 set_magic_quotes_runtime($mqr); 662 if ($this->_fileLocking) @flock($fp, LOCK_UN); 663 @fclose($fp); 664 if ($this->_readControl) { 665 $hashData = $this->_hash($data, $this->_readControlType); 666 if ($hashData != $hashControl) { 667 @touch($this->_file, time() - 2*abs($this->_lifeTime)); 668 return false; 669 } 670 } 671 return $data; 672 } 673 $this->raiseError('Cache_Lite : Unable to read cache !', -2); 674 return false; 675 } 676 677 /** 678 * Write the given data in the cache file 679 * 680 * @param string $data data to put in cache 681 * @return boolean true if ok 682 * @access private 683 */ 684 function _write($data) 685 { 686 $try = 1; 687 while ($try<=2) { 688 $fp = @fopen($this->_file, "wb"); 689 if ($fp) { 690 if ($this->_fileLocking) @flock($fp, LOCK_EX); 691 if ($this->_readControl) { 692 @fwrite($fp, $this->_hash($data, $this->_readControlType), 32); 693 } 694 $len = strlen($data); 695 @fwrite($fp, $data, $len); 696 if ($this->_fileLocking) @flock($fp, LOCK_UN); 697 @fclose($fp); 698 return true; 699 } else { 700 if (($try==1) and ($this->_hashedDirectoryLevel>0)) { 701 $hash = md5($this->_fileName); 702 $root = $this->_cacheDir; 703 for ($i=0 ; $i<$this->_hashedDirectoryLevel ; $i++) { 704 $root = $root . 'cache_' . substr($hash, 0, $i + 1) . '/'; 705 @mkdir($root, $this->_hashedDirectoryUmask); 706 } 707 $try = 2; 708 } else { 709 $try = 999; 710 } 711 } 712 } 713 $this->raiseError('Cache_Lite : Unable to write cache file : '.$this->_file, -1); 714 return false; 715 } 716 717 /** 718 * Write the given data in the cache file and control it just after to avoir corrupted cache entries 719 * 720 * @param string $data data to put in cache 721 * @return boolean true if the test is ok 722 * @access private 723 */ 724 function _writeAndControl($data) 725 { 726 $this->_write($data); 727 $dataRead = $this->_read($data); 728 return ($dataRead==$data); 729 } 730 731 /** 732 * Make a control key with the string containing datas 733 * 734 * @param string $data data 735 * @param string $controlType type of control 'md5', 'crc32' or 'strlen' 736 * @return string control key 737 * @access private 738 */ 739 function _hash($data, $controlType) 740 { 741 switch ($controlType) { 742 case 'md5': 743 return md5($data); 744 case 'crc32': 745 return sprintf('% 32d', crc32($data)); 746 case 'strlen': 747 return sprintf('% 32d', strlen($data)); 748 default: 749 $this->raiseError('Unknown controlType ! (available values are only \'md5\', \'crc32\', \'strlen\')', -5); 750 } 751 } 752 753 } 754 755 ?>
titre
Description
Corps
titre
Description
Corps
titre
Description
Corps
titre
Corps
Généré le : Sat Nov 24 09:00:37 2007 | par Balluche grâce à PHPXref 0.7 |
![]() |