| [ Index ] |
|
Code source de PHP PEAR 1.4.5 |
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.45 2006/06/03 08:10:33 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 * 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 // --- Public methods --- 252 253 /** 254 * Constructor 255 * 256 * $options is an assoc. Available options are : 257 * $options = array( 258 * 'cacheDir' => directory where to put the cache files (string), 259 * 'caching' => enable / disable caching (boolean), 260 * 'lifeTime' => cache lifetime in seconds (int), 261 * 'fileLocking' => enable / disable fileLocking (boolean), 262 * 'writeControl' => enable / disable write control (boolean), 263 * 'readControl' => enable / disable read control (boolean), 264 * 'readControlType' => type of read control 'crc32', 'md5', 'strlen' (string), 265 * 'pearErrorMode' => pear error mode (when raiseError is called) (cf PEAR doc) (int), 266 * 'memoryCaching' => enable / disable memory caching (boolean), 267 * 'onlyMemoryCaching' => enable / disable only memory caching (boolean), 268 * 'memoryCachingLimit' => max nbr of records to store into memory caching (int), 269 * 'fileNameProtection' => enable / disable automatic file name protection (boolean), 270 * 'automaticSerialization' => enable / disable automatic serialization (boolean), 271 * 'automaticCleaningFactor' => distable / tune automatic cleaning process (int), 272 * 'hashedDirectoryLevel' => level of the hashed directory system (int), 273 * 'hashedDirectoryUmask' => umask for hashed directory structure (int), 274 * 'errorHandlingAPIBreak' => API break for better error handling ? (boolean) 275 * ); 276 * 277 * @param array $options options 278 * @access public 279 */ 280 function Cache_Lite($options = array(NULL)) 281 { 282 foreach($options as $key => $value) { 283 $this->setOption($key, $value); 284 } 285 } 286 287 /** 288 * Generic way to set a Cache_Lite option 289 * 290 * see Cache_Lite constructor for available options 291 * 292 * @var string $name name of the option 293 * @var mixed $value value of the option 294 * @access public 295 */ 296 function setOption($name, $value) 297 { 298 $availableOptions = array('errorHandlingAPIBreak', 'hashedDirectoryUmask', 'hashedDirectoryLevel', 'automaticCleaningFactor', 'automaticSerialization', 'fileNameProtection', 'memoryCaching', 'onlyMemoryCaching', 'memoryCachingLimit', 'cacheDir', 'caching', 'lifeTime', 'fileLocking', 'writeControl', 'readControl', 'readControlType', 'pearErrorMode'); 299 if (in_array($name, $availableOptions)) { 300 $property = '_'.$name; 301 $this->$property = $value; 302 } 303 } 304 305 /** 306 * Test if a cache is available and (if yes) return it 307 * 308 * @param string $id cache id 309 * @param string $group name of the cache group 310 * @param boolean $doNotTestCacheValidity if set to true, the cache validity won't be tested 311 * @return string data of the cache (else : false) 312 * @access public 313 */ 314 function get($id, $group = 'default', $doNotTestCacheValidity = false) 315 { 316 $this->_id = $id; 317 $this->_group = $group; 318 $data = false; 319 if ($this->_caching) { 320 $this->_setRefreshTime(); 321 $this->_setFileName($id, $group); 322 clearstatcache(); 323 if ($this->_memoryCaching) { 324 if (isset($this->_memoryCachingArray[$this->_file])) { 325 if ($this->_automaticSerialization) { 326 return unserialize($this->_memoryCachingArray[$this->_file]); 327 } 328 return $this->_memoryCachingArray[$this->_file]; 329 } 330 if ($this->_onlyMemoryCaching) { 331 return false; 332 } 333 } 334 if (($doNotTestCacheValidity) || (is_null($this->_refreshTime))) { 335 if (file_exists($this->_file)) { 336 $data = $this->_read(); 337 } 338 } else { 339 if ((file_exists($this->_file)) && (@filemtime($this->_file) > $this->_refreshTime)) { 340 $data = $this->_read(); 341 } 342 } 343 if (($data) and ($this->_memoryCaching)) { 344 $this->_memoryCacheAdd($data); 345 } 346 if (($this->_automaticSerialization) and (is_string($data))) { 347 $data = unserialize($data); 348 } 349 return $data; 350 } 351 return false; 352 } 353 354 /** 355 * Save some data in a cache file 356 * 357 * @param string $data data to put in cache (can be another type than strings if automaticSerialization is on) 358 * @param string $id cache id 359 * @param string $group name of the cache group 360 * @return boolean true if no problem (else : false or a PEAR_Error object) 361 * @access public 362 */ 363 function save($data, $id = NULL, $group = 'default') 364 { 365 if ($this->_caching) { 366 if ($this->_automaticSerialization) { 367 $data = serialize($data); 368 } 369 if (isset($id)) { 370 $this->_setFileName($id, $group); 371 } 372 if ($this->_memoryCaching) { 373 $this->_memoryCacheAdd($data); 374 if ($this->_onlyMemoryCaching) { 375 return true; 376 } 377 } 378 if ($this->_automaticCleaningFactor>0) { 379 $rand = rand(1, $this->_automaticCleaningFactor); 380 if ($rand==1) { 381 $this->clean(false, 'old'); 382 } 383 } 384 if ($this->_writeControl) { 385 $res = $this->_writeAndControl($data); 386 if (is_bool($res)) { 387 if ($res) { 388 return true; 389 } 390 // if $res if false, we need to invalidate the cache 391 @touch($this->_file, time() - 2*abs($this->_lifeTime)); 392 return false; 393 } 394 } else { 395 $res = $this->_write($data); 396 } 397 if (is_object($res)) { 398 // $res is a PEAR_Error object 399 if (!($this->_errorHandlingAPIBreak)) { 400 return false; // we return false (old API) 401 } 402 } 403 return $res; 404 } 405 return false; 406 } 407 408 /** 409 * Remove a cache file 410 * 411 * @param string $id cache id 412 * @param string $group name of the cache group 413 * @return boolean true if no problem 414 * @access public 415 */ 416 function remove($id, $group = 'default') 417 { 418 $this->_setFileName($id, $group); 419 if ($this->_memoryCaching) { 420 if (isset($this->_memoryCachingArray[$this->_file])) { 421 unset($this->_memoryCachingArray[$this->_file]); 422 $this->_memoryCachingCounter = $this->_memoryCachingCounter - 1; 423 } 424 if ($this->_onlyMemoryCaching) { 425 return true; 426 } 427 } 428 return $this->_unlink($this->_file); 429 } 430 431 /** 432 * Clean the cache 433 * 434 * if no group is specified all cache files will be destroyed 435 * else only cache files of the specified group will be destroyed 436 * 437 * @param string $group name of the cache group 438 * @param string $mode flush cache mode : 'old', 'ingroup', 'notingroup', 439 * 'callback_myFunction' 440 * @return boolean true if no problem 441 * @access public 442 */ 443 function clean($group = false, $mode = 'ingroup') 444 { 445 return $this->_cleanDir($this->_cacheDir, $group, $mode); 446 } 447 448 /** 449 * Set to debug mode 450 * 451 * When an error is found, the script will stop and the message will be displayed 452 * (in debug mode only). 453 * 454 * @access public 455 */ 456 function setToDebug() 457 { 458 $this->setOption('pearErrorMode', CACHE_LITE_ERROR_DIE); 459 } 460 461 /** 462 * Set a new life time 463 * 464 * @param int $newLifeTime new life time (in seconds) 465 * @access public 466 */ 467 function setLifeTime($newLifeTime) 468 { 469 $this->_lifeTime = $newLifeTime; 470 $this->_setRefreshTime(); 471 } 472 473 /** 474 * Save the state of the caching memory array into a cache file cache 475 * 476 * @param string $id cache id 477 * @param string $group name of the cache group 478 * @access public 479 */ 480 function saveMemoryCachingState($id, $group = 'default') 481 { 482 if ($this->_caching) { 483 $array = array( 484 'counter' => $this->_memoryCachingCounter, 485 'array' => $this->_memoryCachingState 486 ); 487 $data = serialize($array); 488 $this->save($data, $id, $group); 489 } 490 } 491 492 /** 493 * Load the state of the caching memory array from a given cache file cache 494 * 495 * @param string $id cache id 496 * @param string $group name of the cache group 497 * @param boolean $doNotTestCacheValidity if set to true, the cache validity won't be tested 498 * @access public 499 */ 500 function getMemoryCachingState($id, $group = 'default', $doNotTestCacheValidity = false) 501 { 502 if ($this->_caching) { 503 if ($data = $this->get($id, $group, $doNotTestCacheValidity)) { 504 $array = unserialize($data); 505 $this->_memoryCachingCounter = $array['counter']; 506 $this->_memoryCachingArray = $array['array']; 507 } 508 } 509 } 510 511 /** 512 * Return the cache last modification time 513 * 514 * BE CAREFUL : THIS METHOD IS FOR HACKING ONLY ! 515 * 516 * @return int last modification time 517 */ 518 function lastModified() 519 { 520 return @filemtime($this->_file); 521 } 522 523 /** 524 * Trigger a PEAR error 525 * 526 * To improve performances, the PEAR.php file is included dynamically. 527 * The file is so included only when an error is triggered. So, in most 528 * cases, the file isn't included and perfs are much better. 529 * 530 * @param string $msg error message 531 * @param int $code error code 532 * @access public 533 */ 534 function raiseError($msg, $code) 535 { 536 include_once ('PEAR.php'); 537 return PEAR::raiseError($msg, $code, $this->_pearErrorMode); 538 } 539 540 /** 541 * Extend the life of a valid cache file 542 * 543 * see http://pear.php.net/bugs/bug.php?id=6681 544 * 545 * @access public 546 */ 547 function extendLife() 548 { 549 @touch($this->_file); 550 } 551 552 // --- Private methods --- 553 554 /** 555 * Compute & set the refresh time 556 * 557 * @access private 558 */ 559 function _setRefreshTime() 560 { 561 if (is_null($this->_lifeTime)) { 562 $this->_refreshTime = null; 563 } else { 564 $this->_refreshTime = time() - $this->_lifeTime; 565 } 566 } 567 568 /** 569 * Remove a file 570 * 571 * @param string $file complete file path and name 572 * @return boolean true if no problem 573 * @access private 574 */ 575 function _unlink($file) 576 { 577 if (!@unlink($file)) { 578 return $this->raiseError('Cache_Lite : Unable to remove cache !', -3); 579 } 580 return true; 581 } 582 583 /** 584 * Recursive function for cleaning cache file in the given directory 585 * 586 * @param string $dir directory complete path (with a trailing slash) 587 * @param string $group name of the cache group 588 * @param string $mode flush cache mode : 'old', 'ingroup', 'notingroup', 589 'callback_myFunction' 590 * @return boolean true if no problem 591 * @access private 592 */ 593 function _cleanDir($dir, $group = false, $mode = 'ingroup') 594 { 595 if ($this->_fileNameProtection) { 596 $motif = ($group) ? 'cache_'.md5($group).'_' : 'cache_'; 597 } else { 598 $motif = ($group) ? 'cache_'.$group.'_' : 'cache_'; 599 } 600 if ($this->_memoryCaching) { 601 while (list($key, ) = each($this->_memoryCachingArray)) { 602 if (strpos($key, $motif, 0)) { 603 unset($this->_memoryCachingArray[$key]); 604 $this->_memoryCachingCounter = $this->_memoryCachingCounter - 1; 605 } 606 } 607 if ($this->_onlyMemoryCaching) { 608 return true; 609 } 610 } 611 if (!($dh = opendir($dir))) { 612 return $this->raiseError('Cache_Lite : Unable to open cache directory !', -4); 613 } 614 $result = true; 615 while ($file = readdir($dh)) { 616 if (($file != '.') && ($file != '..')) { 617 if (substr($file, 0, 6)=='cache_') { 618 $file2 = $dir . $file; 619 if (is_file($file2)) { 620 switch (substr($mode, 0, 9)) { 621 case 'old': 622 // files older than lifeTime get deleted from cache 623 if (!is_null($this->_lifeTime)) { 624 if ((mktime() - @filemtime($file2)) > $this->_lifeTime) { 625 $result = ($result and ($this->_unlink($file2))); 626 } 627 } 628 break; 629 case 'notingrou': 630 if (!strpos($file2, $motif, 0)) { 631 $result = ($result and ($this->_unlink($file2))); 632 } 633 break; 634 case 'callback_': 635 $func = substr($mode, 9, strlen($mode) - 9); 636 if ($func($file2, $group)) { 637 $result = ($result and ($this->_unlink($file2))); 638 } 639 break; 640 case 'ingroup': 641 default: 642 if (strpos($file2, $motif, 0)) { 643 $result = ($result and ($this->_unlink($file2))); 644 } 645 break; 646 } 647 } 648 if ((is_dir($file2)) and ($this->_hashedDirectoryLevel>0)) { 649 $result = ($result and ($this->_cleanDir($file2 . '/', $group, $mode))); 650 } 651 } 652 } 653 } 654 return $result; 655 } 656 657 /** 658 * Add some date in the memory caching array 659 * 660 * @param string $data data to cache 661 * @access private 662 */ 663 function _memoryCacheAdd($data) 664 { 665 $this->_memoryCachingArray[$this->_file] = $data; 666 if ($this->_memoryCachingCounter >= $this->_memoryCachingLimit) { 667 list($key, ) = each($this->_memoryCachingArray); 668 unset($this->_memoryCachingArray[$key]); 669 } else { 670 $this->_memoryCachingCounter = $this->_memoryCachingCounter + 1; 671 } 672 } 673 674 /** 675 * Make a file name (with path) 676 * 677 * @param string $id cache id 678 * @param string $group name of the group 679 * @access private 680 */ 681 function _setFileName($id, $group) 682 { 683 684 if ($this->_fileNameProtection) { 685 $suffix = 'cache_'.md5($group).'_'.md5($id); 686 } else { 687 $suffix = 'cache_'.$group.'_'.$id; 688 } 689 $root = $this->_cacheDir; 690 if ($this->_hashedDirectoryLevel>0) { 691 $hash = md5($suffix); 692 for ($i=0 ; $i<$this->_hashedDirectoryLevel ; $i++) { 693 $root = $root . 'cache_' . substr($hash, 0, $i + 1) . '/'; 694 } 695 } 696 $this->_fileName = $suffix; 697 $this->_file = $root.$suffix; 698 } 699 700 /** 701 * Read the cache file and return the content 702 * 703 * @return string content of the cache file (else : false or a PEAR_Error object) 704 * @access private 705 */ 706 function _read() 707 { 708 $fp = @fopen($this->_file, "rb"); 709 if ($this->_fileLocking) @flock($fp, LOCK_SH); 710 if ($fp) { 711 clearstatcache(); 712 $length = @filesize($this->_file); 713 $mqr = get_magic_quotes_runtime(); 714 set_magic_quotes_runtime(0); 715 if ($this->_readControl) { 716 $hashControl = @fread($fp, 32); 717 $length = $length - 32; 718 } 719 if ($length) { 720 $data = @fread($fp, $length); 721 } else { 722 $data = ''; 723 } 724 set_magic_quotes_runtime($mqr); 725 if ($this->_fileLocking) @flock($fp, LOCK_UN); 726 @fclose($fp); 727 if ($this->_readControl) { 728 $hashData = $this->_hash($data, $this->_readControlType); 729 if ($hashData != $hashControl) { 730 if (!(is_null($this->_lifeTime))) { 731 @touch($this->_file, time() - 2*abs($this->_lifeTime)); 732 } else { 733 @unlink($this->_file); 734 } 735 return false; 736 } 737 } 738 return $data; 739 } 740 return $this->raiseError('Cache_Lite : Unable to read cache !', -2); 741 } 742 743 /** 744 * Write the given data in the cache file 745 * 746 * @param string $data data to put in cache 747 * @return boolean true if ok (a PEAR_Error object else) 748 * @access private 749 */ 750 function _write($data) 751 { 752 if ($this->_hashedDirectoryLevel > 0) { 753 $hash = md5($this->_fileName); 754 $root = $this->_cacheDir; 755 for ($i=0 ; $i<$this->_hashedDirectoryLevel ; $i++) { 756 $root = $root . 'cache_' . substr($hash, 0, $i + 1) . '/'; 757 if (!(@is_dir($root))) { 758 @mkdir($root, $this->_hashedDirectoryUmask); 759 } 760 } 761 } 762 $fp = @fopen($this->_file, "wb"); 763 if ($fp) { 764 if ($this->_fileLocking) @flock($fp, LOCK_EX); 765 if ($this->_readControl) { 766 @fwrite($fp, $this->_hash($data, $this->_readControlType), 32); 767 } 768 $len = strlen($data); 769 @fwrite($fp, $data, $len); 770 if ($this->_fileLocking) @flock($fp, LOCK_UN); 771 @fclose($fp); 772 return true; 773 } 774 return $this->raiseError('Cache_Lite : Unable to write cache file : '.$this->_file, -1); 775 } 776 777 /** 778 * Write the given data in the cache file and control it just after to avoir corrupted cache entries 779 * 780 * @param string $data data to put in cache 781 * @return boolean true if the test is ok (else : false or a PEAR_Error object) 782 * @access private 783 */ 784 function _writeAndControl($data) 785 { 786 $result = $this->_write($data); 787 if (is_object($result)) { 788 return $result; #Â We return the PEAR_Error object 789 } 790 $dataRead = $this->_read(); 791 if (is_object($dataRead)) { 792 return $result; #Â We return the PEAR_Error object 793 } 794 if ((is_bool($dataRead)) && (!$dataRead)) { 795 return false; 796 } 797 return ($dataRead==$data); 798 } 799 800 /** 801 * Make a control key with the string containing datas 802 * 803 * @param string $data data 804 * @param string $controlType type of control 'md5', 'crc32' or 'strlen' 805 * @return string control key 806 * @access private 807 */ 808 function _hash($data, $controlType) 809 { 810 switch ($controlType) { 811 case 'md5': 812 return md5($data); 813 case 'crc32': 814 return sprintf('% 32d', crc32($data)); 815 case 'strlen': 816 return sprintf('% 32d', strlen($data)); 817 default: 818 return $this->raiseError('Unknown controlType ! (available values are only \'md5\', \'crc32\', \'strlen\')', -5); 819 } 820 } 821 822 } 823 824 ?>
titre
Description
Corps
titre
Description
Corps
titre
Description
Corps
titre
Corps
| Généré le : Sun Feb 25 14:08:00 2007 | par Balluche grâce à PHPXref 0.7 |