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* @author Fabien MARTY <fab@php.net> 23* @author Markus Tacker <tacker@php.net> 24*/ 25 26define('CACHE_LITE_ERROR_RETURN', 1); 27define('CACHE_LITE_ERROR_DIE', 8); 28 29class 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 * If null, the cache is valid forever. 55 * 56 * @var int $_lifeTime 57 */ 58 var $_lifeTime = 3600; 59 60 /** 61 * Enable / disable fileLocking 62 * 63 * (can avoid cache corruption under bad circumstances) 64 * 65 * @var boolean $_fileLocking 66 */ 67 var $_fileLocking = true; 68 69 /** 70 * Timestamp of the last valid cache 71 * 72 * @var int $_refreshTime 73 */ 74 var $_refreshTime; 75 76 /** 77 * File name (with path) 78 * 79 * @var string $_file 80 */ 81 var $_file; 82 83 /** 84 * File name (without path) 85 * 86 * @var string $_fileName 87 */ 88 var $_fileName; 89 90 /** 91 * Enable / disable write control (the cache is read just after writing to detect corrupt entries) 92 * 93 * Enable write control will lightly slow the cache writing but not the cache reading 94 * Write control can detect some corrupt cache files but maybe it's not a perfect control 95 * 96 * @var boolean $_writeControl 97 */ 98 var $_writeControl = true; 99 100 /** 101 * Enable / disable read control 102 * 103 * If enabled, a control key is embeded in cache file and this key is compared with the one 104 * calculated after the reading. 105 * 106 * @var boolean $_writeControl 107 */ 108 var $_readControl = true; 109 110 /** 111 * Type of read control (only if read control is enabled) 112 * 113 * Available values are : 114 * 'md5' for a md5 hash control (best but slowest) 115 * 'crc32' for a crc32 hash control (lightly less safe but faster, better choice) 116 * 'strlen' for a length only test (fastest) 117 * 118 * @var boolean $_readControlType 119 */ 120 var $_readControlType = 'crc32'; 121 122 /** 123 * Pear error mode (when raiseError is called) 124 * 125 * (see PEAR doc) 126 * 127 * @see setToDebug() 128 * @var int $_pearErrorMode 129 */ 130 var $_pearErrorMode = CACHE_LITE_ERROR_RETURN; 131 132 /** 133 * Current cache id 134 * 135 * @var string $_id 136 */ 137 var $_id; 138 139 /** 140 * Current cache group 141 * 142 * @var string $_group 143 */ 144 var $_group; 145 146 /** 147 * Enable / Disable "Memory Caching" 148 * 149 * NB : There is no lifetime for memory caching ! 150 * 151 * @var boolean $_memoryCaching 152 */ 153 var $_memoryCaching = false; 154 155 /** 156 * Enable / Disable "Only Memory Caching" 157 * (be carefull, memory caching is "beta quality") 158 * 159 * @var boolean $_onlyMemoryCaching 160 */ 161 var $_onlyMemoryCaching = false; 162 163 /** 164 * Memory caching array 165 * 166 * @var array $_memoryCachingArray 167 */ 168 var $_memoryCachingArray = array(); 169 170 /** 171 * Memory caching counter 172 * 173 * @var int $memoryCachingCounter 174 */ 175 var $_memoryCachingCounter = 0; 176 177 /** 178 * Memory caching limit 179 * 180 * @var int $memoryCachingLimit 181 */ 182 var $_memoryCachingLimit = 1000; 183 184 /** 185 * File Name protection 186 * 187 * if set to true, you can use any cache id or group name 188 * if set to false, it can be faster but cache ids and group names 189 * will be used directly in cache file names so be carefull with 190 * special characters... 191 * 192 * @var boolean $fileNameProtection 193 */ 194 var $_fileNameProtection = true; 195 196 /** 197 * Enable / disable automatic serialization 198 * 199 * it can be used to save directly datas which aren't strings 200 * (but it's slower) 201 * 202 * @var boolean $_serialize 203 */ 204 var $_automaticSerialization = false; 205 206 /** 207 * Disable / Tune the automatic cleaning process 208 * 209 * The automatic cleaning process destroy too old (for the given life time) 210 * cache files when a new cache file is written. 211 * 0 => no automatic cache cleaning 212 * 1 => systematic cache cleaning 213 * x (integer) > 1 => automatic cleaning randomly 1 times on x cache write 214 * 215 * @var int $_automaticCleaning 216 */ 217 var $_automaticCleaningFactor = 0; 218 219 /** 220 * Nested directory level 221 * 222 * Set the hashed directory structure level. 0 means "no hashed directory 223 * structure", 1 means "one level of directory", 2 means "two levels"... 224 * This option can speed up Cache_Lite only when you have many thousands of 225 * cache file. Only specific benchs can help you to choose the perfect value 226 * for you. Maybe, 1 or 2 is a good start. 227 * 228 * @var int $_hashedDirectoryLevel 229 */ 230 var $_hashedDirectoryLevel = 0; 231 232 /** 233 * Umask for hashed directory structure 234 * 235 * @var int $_hashedDirectoryUmask 236 */ 237 var $_hashedDirectoryUmask = 0700; 238 239 /** 240 * API break for error handling in CACHE_LITE_ERROR_RETURN mode 241 * 242 * In CACHE_LITE_ERROR_RETURN mode, error handling was not good because 243 * for example save() method always returned a boolean (a PEAR_Error object 244 * would be better in CACHE_LITE_ERROR_RETURN mode). To correct this without 245 * breaking the API, this option (false by default) can change this handling. 246 * 247 * @var boolean 248 */ 249 var $_errorHandlingAPIBreak = false; 250 251 var $_hashedDirectoryGroup = NULL; 252 253 var $_cacheFileMode = NULL; 254 255 var $_cacheFileGroup = NULL; 256 257 // --- Public methods --- 258 259 /** 260 * Constructor 261 * 262 * $options is an assoc. Available options are : 263 * $options = array( 264 * 'cacheDir' => directory where to put the cache files (string), 265 * 'caching' => enable / disable caching (boolean), 266 * 'lifeTime' => cache lifetime in seconds (int), 267 * 'fileLocking' => enable / disable fileLocking (boolean), 268 * 'writeControl' => enable / disable write control (boolean), 269 * 'readControl' => enable / disable read control (boolean), 270 * 'readControlType' => type of read control 'crc32', 'md5', 'strlen' (string), 271 * 'pearErrorMode' => pear error mode (when raiseError is called) (cf PEAR doc) (int), 272 * 'memoryCaching' => enable / disable memory caching (boolean), 273 * 'onlyMemoryCaching' => enable / disable only memory caching (boolean), 274 * 'memoryCachingLimit' => max nbr of records to store into memory caching (int), 275 * 'fileNameProtection' => enable / disable automatic file name protection (boolean), 276 * 'automaticSerialization' => enable / disable automatic serialization (boolean), 277 * 'automaticCleaningFactor' => distable / tune automatic cleaning process (int), 278 * 'hashedDirectoryLevel' => level of the hashed directory system (int), 279 * 'hashedDirectoryUmask' => umask for hashed directory structure (int), 280 * 'errorHandlingAPIBreak' => API break for better error handling ? (boolean) 281 * 'hashedDirectoryGroup' => group of hashed directory structure (int | string) (see function chgrp) 282 * 'cacheFileMode' => filesystem mode of newly created cache files (int) 283 * 'cacheFileGroup' => group of newly created cache files (int | string) (see function chgrp) 284 * ); 285 * 286 * If sys_get_temp_dir() is available and the 287 * 'cacheDir' option is not provided in the 288 * constructor options array its output is used 289 * to determine the suitable temporary directory. 290 * 291 * @see http://de.php.net/sys_get_temp_dir 292 * @see http://pear.php.net/bugs/bug.php?id=18328 293 * 294 * @param array $options options 295 * @access public 296 */ 297 function __construct($options = array(NULL)) 298 { 299 foreach($options as $key => $value) { 300 $this->setOption($key, $value); 301 } 302 if (!isset($options['cacheDir']) && function_exists('sys_get_temp_dir')) { 303 $this->setOption('cacheDir', sys_get_temp_dir() . DIRECTORY_SEPARATOR); 304 } 305 } 306 307 /** 308 * PHP4 constructor for backwards compatibility with older code 309 * 310 * @param array $options Options 311 */ 312 function Cache_Lite($options = array(NULL)) 313 { 314 self::__construct($options); 315 } 316 317 /** 318 * Generic way to set a Cache_Lite option 319 * 320 * see Cache_Lite constructor for available options 321 * 322 * @var string $name name of the option 323 * @var mixed $value value of the option 324 * @access public 325 */ 326 function setOption($name, $value) 327 { 328 $availableOptions = array('errorHandlingAPIBreak', 'hashedDirectoryUmask', 'hashedDirectoryLevel', 'automaticCleaningFactor', 'automaticSerialization', 'fileNameProtection', 'memoryCaching', 'onlyMemoryCaching', 'memoryCachingLimit', 'cacheDir', 'caching', 'lifeTime', 'fileLocking', 'writeControl', 'readControl', 'readControlType', 'pearErrorMode', 'hashedDirectoryGroup', 'cacheFileMode', 'cacheFileGroup'); 329 if (in_array($name, $availableOptions)) { 330 $property = '_'.$name; 331 $this->$property = $value; 332 } 333 } 334 335 /** 336 * Test if a cache is available and (if yes) return it 337 * 338 * @param string $id cache id 339 * @param string $group name of the cache group 340 * @param boolean $doNotTestCacheValidity if set to true, the cache validity won't be tested 341 * @return string data of the cache (else : false) 342 * @access public 343 */ 344 function get($id, $group = 'default', $doNotTestCacheValidity = false) 345 { 346 $this->_id = $id; 347 $this->_group = $group; 348 $data = false; 349 if ($this->_caching) { 350 $this->_setRefreshTime(); 351 $this->_setFileName($id, $group); 352 clearstatcache(); 353 if ($this->_memoryCaching) { 354 if (isset($this->_memoryCachingArray[$this->_file])) { 355 if ($this->_automaticSerialization) { 356 return unserialize($this->_memoryCachingArray[$this->_file]); 357 } 358 return $this->_memoryCachingArray[$this->_file]; 359 } 360 if ($this->_onlyMemoryCaching) { 361 return false; 362 } 363 } 364 if (($doNotTestCacheValidity) || (is_null($this->_refreshTime))) { 365 if (file_exists($this->_file)) { 366 $data = $this->_read(); 367 } 368 } else { 369 if ((file_exists($this->_file)) && (@filemtime($this->_file) > $this->_refreshTime)) { 370 $data = $this->_read(); 371 } 372 } 373 if (($data) and ($this->_memoryCaching)) { 374 $this->_memoryCacheAdd($data); 375 } 376 if (($this->_automaticSerialization) and (is_string($data))) { 377 $data = unserialize($data); 378 } 379 return $data; 380 } 381 return false; 382 } 383 384 /** 385 * Save some data in a cache file 386 * 387 * @param string $data data to put in cache (can be another type than strings if automaticSerialization is on) 388 * @param string $id cache id 389 * @param string $group name of the cache group 390 * @return boolean true if no problem (else : false or a PEAR_Error object) 391 * @access public 392 */ 393 function save($data, $id = NULL, $group = 'default') 394 { 395 if ($this->_caching) { 396 if ($this->_automaticSerialization) { 397 $data = serialize($data); 398 } 399 if (isset($id)) { 400 $this->_setFileName($id, $group); 401 } 402 if ($this->_memoryCaching) { 403 $this->_memoryCacheAdd($data); 404 if ($this->_onlyMemoryCaching) { 405 return true; 406 } 407 } 408 if ($this->_automaticCleaningFactor>0 && ($this->_automaticCleaningFactor==1 || mt_rand(1, $this->_automaticCleaningFactor)==1)) { 409 $this->clean(false, 'old'); 410 } 411 if ($this->_writeControl) { 412 $res = $this->_writeAndControl($data); 413 if (is_bool($res)) { 414 if ($res) { 415 return true; 416 } 417 // if $res if false, we need to invalidate the cache 418 @touch($this->_file, time() - 2*abs($this->_lifeTime)); 419 return false; 420 } 421 } else { 422 $res = $this->_write($data); 423 } 424 if (is_object($res)) { 425 // $res is a PEAR_Error object 426 if (!($this->_errorHandlingAPIBreak)) { 427 return false; // we return false (old API) 428 } 429 } 430 return $res; 431 } 432 return false; 433 } 434 435 /** 436 * Remove a cache file 437 * 438 * @param string $id cache id 439 * @param string $group name of the cache group 440 * @param boolean $checkbeforeunlink check if file exists before removing it 441 * @return boolean true if no problem 442 * @access public 443 */ 444 function remove($id, $group = 'default', $checkbeforeunlink = false) 445 { 446 $this->_setFileName($id, $group); 447 if ($this->_memoryCaching) { 448 if (isset($this->_memoryCachingArray[$this->_file])) { 449 unset($this->_memoryCachingArray[$this->_file]); 450 $this->_memoryCachingCounter = $this->_memoryCachingCounter - 1; 451 } 452 if ($this->_onlyMemoryCaching) { 453 return true; 454 } 455 } 456 if ( $checkbeforeunlink ) { 457 if (!file_exists($this->_file)) return true; 458 } 459 return $this->_unlink($this->_file); 460 } 461 462 /** 463 * Clean the cache 464 * 465 * if no group is specified all cache files will be destroyed 466 * else only cache files of the specified group will be destroyed 467 * 468 * @param string $group name of the cache group 469 * @param string $mode flush cache mode : 'old', 'ingroup', 'notingroup', 470 * 'callback_myFunction' 471 * @return boolean true if no problem 472 * @access public 473 */ 474 function clean($group = false, $mode = 'ingroup') 475 { 476 return $this->_cleanDir($this->_cacheDir, $group, $mode); 477 } 478 479 /** 480 * Set to debug mode 481 * 482 * When an error is found, the script will stop and the message will be displayed 483 * (in debug mode only). 484 * 485 * @access public 486 */ 487 function setToDebug() 488 { 489 $this->setOption('pearErrorMode', CACHE_LITE_ERROR_DIE); 490 } 491 492 /** 493 * Set a new life time 494 * 495 * @param int $newLifeTime new life time (in seconds) 496 * @access public 497 */ 498 function setLifeTime($newLifeTime) 499 { 500 $this->_lifeTime = $newLifeTime; 501 $this->_setRefreshTime(); 502 } 503 504 /** 505 * Save the state of the caching memory array into a cache file cache 506 * 507 * @param string $id cache id 508 * @param string $group name of the cache group 509 * @access public 510 */ 511 function saveMemoryCachingState($id, $group = 'default') 512 { 513 if ($this->_caching) { 514 $array = array( 515 'counter' => $this->_memoryCachingCounter, 516 'array' => $this->_memoryCachingArray 517 ); 518 $data = serialize($array); 519 $this->save($data, $id, $group); 520 } 521 } 522 523 /** 524 * Load the state of the caching memory array from a given cache file cache 525 * 526 * @param string $id cache id 527 * @param string $group name of the cache group 528 * @param boolean $doNotTestCacheValidity if set to true, the cache validity won't be tested 529 * @access public 530 */ 531 function getMemoryCachingState($id, $group = 'default', $doNotTestCacheValidity = false) 532 { 533 if ($this->_caching) { 534 if ($data = $this->get($id, $group, $doNotTestCacheValidity)) { 535 $array = unserialize($data); 536 $this->_memoryCachingCounter = $array['counter']; 537 $this->_memoryCachingArray = $array['array']; 538 } 539 } 540 } 541 542 /** 543 * Return the cache last modification time 544 * 545 * BE CAREFUL : THIS METHOD IS FOR HACKING ONLY ! 546 * 547 * @return int last modification time 548 */ 549 function lastModified() 550 { 551 return @filemtime($this->_file); 552 } 553 554 /** 555 * Trigger a PEAR error 556 * 557 * To improve performances, the PEAR.php file is included dynamically. 558 * The file is so included only when an error is triggered. So, in most 559 * cases, the file isn't included and perfs are much better. 560 * 561 * @param string $msg error message 562 * @param int $code error code 563 * @access public 564 */ 565 function raiseError($msg, $code) 566 { 567 include_once('PEAR.php'); 568 return PEAR::raiseError($msg, $code, $this->_pearErrorMode); 569 } 570 571 /** 572 * Extend the life of a valid cache file 573 * 574 * see http://pear.php.net/bugs/bug.php?id=6681 575 * 576 * @access public 577 */ 578 function extendLife() 579 { 580 @touch($this->_file); 581 } 582 583 // --- Private methods --- 584 585 /** 586 * Compute & set the refresh time 587 * 588 * @access private 589 */ 590 function _setRefreshTime() 591 { 592 if (is_null($this->_lifeTime)) { 593 $this->_refreshTime = null; 594 } else { 595 $this->_refreshTime = time() - $this->_lifeTime; 596 } 597 } 598 599 /** 600 * Remove a file 601 * 602 * @param string $file complete file path and name 603 * @return boolean true if no problem 604 * @access private 605 */ 606 function _unlink($file) 607 { 608 if (!@unlink($file)) { 609 return $this->raiseError('Cache_Lite : Unable to remove cache !', -3); 610 } 611 return true; 612 } 613 614 /** 615 * Recursive function for cleaning cache file in the given directory 616 * 617 * @param string $dir directory complete path (with a trailing slash) 618 * @param string $group name of the cache group 619 * @param string $mode flush cache mode : 'old', 'ingroup', 'notingroup', 620 'callback_myFunction' 621 * @return boolean true if no problem 622 * @access private 623 */ 624 function _cleanDir($dir, $group = false, $mode = 'ingroup') 625 { 626 if ($this->_fileNameProtection) { 627 $motif = ($group) ? 'cache_'.md5($group).'_' : 'cache_'; 628 } else { 629 $motif = ($group) ? 'cache_'.$group.'_' : 'cache_'; 630 } 631 if ($this->_memoryCaching) { 632 foreach($this->_memoryCachingArray as $key => $v) { 633 if (strpos($key, $motif) !== false) { 634 unset($this->_memoryCachingArray[$key]); 635 $this->_memoryCachingCounter = $this->_memoryCachingCounter - 1; 636 } 637 } 638 if ($this->_onlyMemoryCaching) { 639 return true; 640 } 641 } 642 if (!($dh = opendir($dir))) { 643 return $this->raiseError('Cache_Lite : Unable to open cache directory !', -4); 644 } 645 $result = true; 646 while (($file = readdir($dh)) !== false) { 647 if (($file != '.') && ($file != '..')) { 648 if (substr($file, 0, 6)=='cache_') { 649 $file2 = $dir . $file; 650 if (is_file($file2)) { 651 switch (substr($mode, 0, 9)) { 652 case 'old': 653 // files older than lifeTime get deleted from cache 654 if (!is_null($this->_lifeTime)) { 655 if ((time() - @filemtime($file2)) > $this->_lifeTime) { 656 $result = ($result and ($this->_unlink($file2))); 657 } 658 } 659 break; 660 case 'notingrou': 661 if (strpos($file2, $motif) === false) { 662 $result = ($result and ($this->_unlink($file2))); 663 } 664 break; 665 case 'callback_': 666 $func = substr($mode, 9, strlen($mode) - 9); 667 if ($func($file2, $group)) { 668 $result = ($result and ($this->_unlink($file2))); 669 } 670 break; 671 case 'ingroup': 672 default: 673 if (strpos($file2, $motif) !== false) { 674 $result = ($result and ($this->_unlink($file2))); 675 } 676 break; 677 } 678 } 679 if ((is_dir($file2)) and ($this->_hashedDirectoryLevel>0)) { 680 $result = ($result and ($this->_cleanDir($file2 . '/', $group, $mode))); 681 } 682 } 683 } 684 } 685 return $result; 686 } 687 688 /** 689 * Touch the cache file while are recreating it to avoid 690 * launch this task more then once when necessary 691 * When the cache recreated and Added in Cache Memory 692 * @return void 693 * @access private 694 */ 695 function _touchCacheFile(){ 696 if (file_exists($this->_file)) { 697 @touch($this->_file); 698 } 699 } 700 /** 701 * Add some date in the memory caching array 702 * 703 * @param string $data data to cache 704 * @access private 705 */ 706 function _memoryCacheAdd($data) 707 { 708 $this->_touchCacheFile(); 709 $this->_memoryCachingArray[$this->_file] = $data; 710 if ($this->_memoryCachingCounter >= $this->_memoryCachingLimit) { 711 $key = key($this->_memoryCachingArray); 712 next($this->_memoryCachingArray); 713 unset($this->_memoryCachingArray[$key]); 714 715 } else { 716 $this->_memoryCachingCounter = $this->_memoryCachingCounter + 1; 717 } 718 } 719 720 /** 721 * Make a file name (with path) 722 * 723 * @param string $id cache id 724 * @param string $group name of the group 725 * @access private 726 */ 727 function _setFileName($id, $group) 728 { 729 730 if ($this->_fileNameProtection) { 731 $suffix = 'cache_'.md5($group).'_'.md5($id); 732 } else { 733 $suffix = 'cache_'.$group.'_'.$id; 734 } 735 $root = $this->_cacheDir; 736 if ($this->_hashedDirectoryLevel>0) { 737 $hash = md5($suffix); 738 for ($i=0 ; $i<$this->_hashedDirectoryLevel ; $i++) { 739 $root = $root . 'cache_' . substr($hash, 0, $i + 1) . '/'; 740 } 741 } 742 $this->_fileName = $suffix; 743 $this->_file = $root.$suffix; 744 } 745 746 /** 747 * Read the cache file and return the content 748 * 749 * @return string content of the cache file (else : false or a PEAR_Error object) 750 * @access private 751 */ 752 function _read() 753 { 754 $fp = @fopen($this->_file, "rb"); 755 if ($fp) { 756 if ($this->_fileLocking) @flock($fp, LOCK_SH); 757 clearstatcache(); 758 $length = @filesize($this->_file); 759 $mqr = (function_exists('get_magic_quotes_runtime') ? @get_magic_quotes_runtime() : 0); 760 if ($mqr) { 761 set_magic_quotes_runtime(0); 762 } 763 if ($this->_readControl) { 764 $hashControl = @fread($fp, 32); 765 $length = $length - 32; 766 } 767 768 if ($length) { 769 $data = ''; 770 // See https://bugs.php.net/bug.php?id=30936 771 // The 8192 magic number is the chunk size used internally by PHP. 772 while(!feof($fp)) $data .= fread($fp, 8192); 773 } else { 774 $data = ''; 775 } 776 if ($mqr) { 777 set_magic_quotes_runtime($mqr); 778 } 779 if ($this->_fileLocking) @flock($fp, LOCK_UN); 780 @fclose($fp); 781 if ($this->_readControl) { 782 $hashData = $this->_hash($data, $this->_readControlType); 783 if ($hashData != $hashControl) { 784 if (!(is_null($this->_lifeTime))) { 785 @touch($this->_file, time() - 2*abs($this->_lifeTime)); 786 } else { 787 @unlink($this->_file); 788 } 789 return false; 790 } 791 } 792 return $data; 793 } 794 return $this->raiseError('Cache_Lite : Unable to read cache !', -2); 795 } 796 797 /** 798 * Write the given data in the cache file 799 * 800 * @param string $data data to put in cache 801 * @return boolean true if ok (a PEAR_Error object else) 802 * @access private 803 */ 804 function _write($data) 805 { 806 if ($this->_hashedDirectoryLevel > 0) { 807 $hash = md5($this->_fileName); 808 $root = $this->_cacheDir; 809 for ($i=0 ; $i<$this->_hashedDirectoryLevel ; $i++) { 810 $root = $root . 'cache_' . substr($hash, 0, $i + 1) . '/'; 811 if (!(@is_dir($root))) { 812 if (@mkdir($root)) 813 { 814 @chmod($root, $this->_hashedDirectoryUmask); 815 if (! is_null($this->_hashedDirectoryGroup)) 816 @chgrp($root, $this->_hashedDirectoryGroup); 817 } 818 } 819 } 820 } 821 // if both _cacheFileMode and _cacheFileGroup is null, then we don't need to call 822 // file_exists (see below: if ($is_newfile) ...) 823 $is_newfile = (! is_null($this->_cacheFileMode) || !is_null($this->_cacheFileGroup)) 824 && ! @file_exists($this->_file); 825 $fp = @fopen($this->_file, "wb"); 826 if ($fp) { 827 if ($this->_fileLocking) @flock($fp, LOCK_EX); 828 if ($is_newfile) 829 { 830 if (! is_null($this->_cacheFileMode)) 831 @chmod($this->_file, $this->_cacheFileMode); 832 if (! is_null($this->_cacheFileGroup)) 833 @chgrp($this->_file, $this->_cacheFileGroup); 834 } 835 if ($this->_readControl) { 836 @fwrite($fp, $this->_hash($data, $this->_readControlType), 32); 837 } 838 $mqr = (function_exists('get_magic_quotes_runtime') ? @get_magic_quotes_runtime() : 0); 839 if ($mqr) { 840 set_magic_quotes_runtime(0); 841 } 842 @fwrite($fp, $data); 843 if ($mqr) { 844 set_magic_quotes_runtime($mqr); 845 } 846 if ($this->_fileLocking) @flock($fp, LOCK_UN); 847 @fclose($fp); 848 return true; 849 } 850 return $this->raiseError('Cache_Lite : Unable to write cache file : '.$this->_file, -1); 851 } 852 853 /** 854 * Write the given data in the cache file and control it just after to avoir corrupted cache entries 855 * 856 * @param string $data data to put in cache 857 * @return boolean true if the test is ok (else : false or a PEAR_Error object) 858 * @access private 859 */ 860 function _writeAndControl($data) 861 { 862 $result = $this->_write($data); 863 if (is_object($result)) { 864 return $result; # We return the PEAR_Error object 865 } 866 $dataRead = $this->_read(); 867 if (is_object($dataRead)) { 868 return $dataRead; # We return the PEAR_Error object 869 } 870 if ((is_bool($dataRead)) && (!$dataRead)) { 871 return false; 872 } 873 return ($dataRead==$data); 874 } 875 876 /** 877 * Make a control key with the string containing datas 878 * 879 * @param string $data data 880 * @param string $controlType type of control 'md5', 'crc32' or 'strlen' 881 * @return string control key 882 * @access private 883 */ 884 function _hash($data, $controlType) 885 { 886 switch ($controlType) { 887 case 'md5': 888 return md5($data); 889 case 'crc32': 890 return sprintf('% 32d', crc32($data)); 891 case 'strlen': 892 return sprintf('% 32d', strlen($data)); 893 default: 894 return $this->raiseError('Unknown controlType ! (available values are only \'md5\', \'crc32\', \'strlen\')', -5); 895 } 896 } 897 898} 899