home *** CD-ROM | disk | FTP | other *** search
/ Enter 2004 June / ENTER.ISO / files / xampp-win32-1.4.5-installer.exe / xampp / Lite.php < prev    next >
Encoding:
PHP Script  |  2004-03-24  |  17.3 KB  |  607 lines

  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. * A tutorial is available in english at this url :
  14. * http://www.pearfr.org/index.php/en/article/cache_lite
  15. * (big thanks to Pierre-Alain Joye for the translation)
  16. *
  17. * The same tutorial is also available in french at this url :
  18. * http://www.pearfr.org/index.php/fr/article/cache_lite
  19. *
  20. * Memory Caching is from an original idea of
  21. * Mike BENOIT <ipso@snappymail.ca>
  22. *
  23. * @package Cache_Lite
  24. * @category Caching
  25. * @version $Id: Lite.php,v 1.18 2004/01/08 18:00:28 fab Exp $
  26. * @author Fabien MARTY <fab@php.net>
  27. */
  28.  
  29. define('CACHE_LITE_ERROR_RETURN', 1);
  30. define('CACHE_LITE_ERROR_DIE', 8);
  31.  
  32. class Cache_Lite
  33. {
  34.  
  35.     // --- Private properties ---
  36.  
  37.     /**
  38.     * Directory where to put the cache files
  39.     * (make sure to add a trailing slash)
  40.     *
  41.     * @var string $_cacheDir
  42.     */
  43.     var $_cacheDir = '/tmp/';
  44.  
  45.     /**
  46.     * Enable / disable caching
  47.     *
  48.     * (can be very usefull for the debug of cached scripts)
  49.     *
  50.     * @var boolean $_caching
  51.     */
  52.     var $_caching = true;
  53.  
  54.     /**
  55.     * Cache lifetime (in seconds)
  56.     *
  57.     * @var int $_lifeTime
  58.     */
  59.     var $_lifeTime = 3600;
  60.  
  61.     /**
  62.     * Enable / disable fileLocking
  63.     *
  64.     * (can avoid cache corruption under bad circumstances)
  65.     *
  66.     * @var boolean $_fileLocking
  67.     */
  68.     var $_fileLocking = true;
  69.  
  70.     /**
  71.     * Timestamp of the last valid cache
  72.     *
  73.     * @var int $_refreshTime
  74.     */
  75.     var $_refreshTime;
  76.  
  77.     /**
  78.     * File name (with path)
  79.     *
  80.     * @var string $_file
  81.     */
  82.     var $_file;
  83.  
  84.     /**
  85.     * Enable / disable write control (the cache is read just after writing to detect corrupt entries)
  86.     *
  87.     * Enable write control will lightly slow the cache writing but not the cache reading
  88.     * Write control can detect some corrupt cache files but maybe it's not a perfect control
  89.     *
  90.     * @var boolean $_writeControl
  91.     */
  92.     var $_writeControl = true;
  93.  
  94.     /**
  95.     * Enable / disable read control
  96.     *
  97.     * If enabled, a control key is embeded in cache file and this key is compared with the one
  98.     * calculated after the reading.
  99.     *
  100.     * @var boolean $_writeControl
  101.     */
  102.     var $_readControl = true;
  103.  
  104.     /**
  105.     * Type of read control (only if read control is enabled)
  106.     *
  107.     * Available values are :
  108.     * 'md5' for a md5 hash control (best but slowest)
  109.     * 'crc32' for a crc32 hash control (lightly less safe but faster, better choice)
  110.     * 'strlen' for a length only test (fastest)
  111.     *
  112.     * @var boolean $_readControlType
  113.     */
  114.     var $_readControlType = 'crc32';
  115.  
  116.     /**
  117.     * Pear error mode (when raiseError is called)
  118.     *
  119.     * (see PEAR doc)
  120.     *
  121.     * @see setToDebug()
  122.     * @var int $_pearErrorMode
  123.     */
  124.     var $_pearErrorMode = CACHE_LITE_ERROR_RETURN;
  125.     
  126.     /**
  127.     * Current cache id
  128.     *
  129.     * @var string $_id
  130.     */
  131.     var $_id;
  132.  
  133.     /**
  134.     * Current cache group
  135.     *
  136.     * @var string $_group
  137.     */
  138.     var $_group;
  139.  
  140.     /**
  141.     * Enable / Disable "Memory Caching"
  142.     *
  143.     * NB : There is no lifetime for memory caching ! 
  144.     *
  145.     * @var boolean $_memoryCaching
  146.     */
  147.     var $_memoryCaching = false;
  148.  
  149.     /**
  150.     * Enable / Disable "Only Memory Caching"
  151.     * (be carefull, memory caching is "beta quality")
  152.     *
  153.     * @var boolean $_onlyMemoryCaching
  154.     */
  155.     var $_onlyMemoryCaching = false;
  156.  
  157.     /**
  158.     * Memory caching array
  159.     *
  160.     * @var array $_memoryCachingArray
  161.     */
  162.     var $_memoryCachingArray = array();
  163.  
  164.     /**
  165.     * Memory caching counter
  166.     *
  167.     * @var int $memoryCachingCounter
  168.     */
  169.     var $_memoryCachingCounter = 0;
  170.  
  171.     /**
  172.     * Memory caching limit
  173.     *
  174.     * @var int $memoryCachingLimit
  175.     */
  176.     var $_memoryCachingLimit = 1000;
  177.     
  178.     /**
  179.     * File Name protection
  180.     *
  181.     * if set to true, you can use any cache id or group name
  182.     * if set to false, it can be faster but cache ids and group names
  183.     * will be used directly in cache file names so be carefull with
  184.     * special characters...
  185.     *
  186.     * @var boolean $fileNameProtection
  187.     */
  188.     var $_fileNameProtection = true;
  189.     
  190.     /**
  191.     * Enable / disable automatic serialization
  192.     *
  193.     * it can be used to save directly datas which aren't strings
  194.     * (but it's slower)    
  195.     *
  196.     * @var boolean $_serialize
  197.     */
  198.     var $_automaticSerialization = false;
  199.     
  200.     // --- Public methods ---
  201.  
  202.     /**
  203.     * Constructor
  204.     *
  205.     * $options is an assoc. Available options are :
  206.     * $options = array(
  207.     *     'cacheDir' => directory where to put the cache files (string),
  208.     *     'caching' => enable / disable caching (boolean),
  209.     *     'lifeTime' => cache lifetime in seconds (int),
  210.     *     'fileLocking' => enable / disable fileLocking (boolean),
  211.     *     'writeControl' => enable / disable write control (boolean),
  212.     *     'readControl' => enable / disable read control (boolean),
  213.     *     'readControlType' => type of read control 'crc32', 'md5', 'strlen' (string),
  214.     *     'pearErrorMode' => pear error mode (when raiseError is called) (cf PEAR doc) (int),
  215.     *     'memoryCaching' => enable / disable memory caching (boolean),
  216.     *     'onlyMemoryCaching' => enable / disable only memory caching (boolean),
  217.     *     'memoryCachingLimit' => max nbr of records to store into memory caching (int),
  218.     *     'fileNameProtection' => enable / disable automatic file name protection (boolean),
  219.     *     'automaticSerialization' => enable / disable automatic serialization (boolean)
  220.     * );
  221.     *
  222.     * @param array $options options
  223.     * @access public
  224.     */
  225.     function Cache_Lite($options = array(NULL))
  226.     {
  227.         $availableOptions = array('automaticSerialization', 'fileNameProtection', 'memoryCaching', 'onlyMemoryCaching', 'memoryCachingLimit', 'cacheDir', 'caching', 'lifeTime', 'fileLocking', 'writeControl', 'readControl', 'readControlType', 'pearErrorMode');
  228.         foreach($options as $key => $value) {
  229.             if(in_array($key, $availableOptions)) {
  230.                 $property = '_'.$key;
  231.                 $this->$property = $value;
  232.             }
  233.         }
  234.         $this->_refreshTime = time() - $this->_lifeTime;
  235.     }
  236.     
  237.     /**
  238.     * Test if a cache is available and (if yes) return it
  239.     *
  240.     * @param string $id cache id
  241.     * @param string $group name of the cache group
  242.     * @param boolean $doNotTestCacheValidity if set to true, the cache validity won't be tested
  243.     * @return string data of the cache (or false if no cache available)
  244.     * @access public
  245.     */
  246.     function get($id, $group = 'default', $doNotTestCacheValidity = false)
  247.     {
  248.         $this->_id = $id;
  249.         $this->_group = $group;
  250.         $data = false;
  251.         if ($this->_caching) {
  252.             $this->_setFileName($id, $group);
  253.             if ($this->_memoryCaching) {
  254.                 if (isset($this->_memoryCachingArray[$this->_file])) {
  255.                     if ($this->_automaticSerialization) {
  256.                         return unserialize($this->_memoryCachingArray[$this->_file]);
  257.                     } else {
  258.                         return $this->_memoryCachingArray[$this->_file];
  259.                     }
  260.                 } else {
  261.                     if ($this->_onlyMemoryCaching) {
  262.                         return false;
  263.                     }
  264.                 }
  265.             }
  266.             if ($doNotTestCacheValidity) {
  267.                 if (file_exists($this->_file)) {
  268.                     $data = $this->_read();
  269.                 }
  270.             } else {
  271.                 if (@filemtime($this->_file) > $this->_refreshTime) {
  272.                     $data = $this->_read();
  273.                 }
  274.             }
  275.             if (($data) and ($this->_memoryCaching)) {
  276.                 $this->_memoryCacheAdd($this->_file, $data);
  277.             }
  278.             if (($this->_automaticSerialization) and (is_string($data))) {
  279.                 $data = unserialize($data);
  280.             }
  281.             return $data;
  282.         }
  283.         return false;
  284.     }
  285.     
  286.     /**
  287.     * Save some data in a cache file
  288.     *
  289.     * @param string $data data to put in cache (can be another type than strings if automaticSerialization is on)
  290.     * @param string $id cache id
  291.     * @param string $group name of the cache group
  292.     * @return boolean true if no problem
  293.     * @access public
  294.     */
  295.     function save($data, $id = NULL, $group = 'default')
  296.     {
  297.         if ($this->_caching) {
  298.             if ($this->_automaticSerialization) {
  299.                 $data = serialize($data);
  300.             }
  301.             if (isset($id)) {
  302.                 $this->_setFileName($id, $group);
  303.             }
  304.             if ($this->_memoryCaching) {
  305.                 $this->_memoryCacheAdd($this->_file, $data);
  306.                 if ($this->_onlyMemoryCaching) {
  307.                     return true;
  308.                 }
  309.             }
  310.             if ($this->_writeControl) {
  311.                 if (!$this->_writeAndControl($data)) {
  312.                     @touch($this->_file, time() - 2*abs($this->_lifeTime));
  313.                     return false;
  314.                 } else {
  315.                     return true;
  316.                 }
  317.             } else {
  318.                 return $this->_write($data);
  319.             }
  320.         }
  321.         return false;
  322.     }
  323.  
  324.     /**
  325.     * Remove a cache file
  326.     *
  327.     * @param string $id cache id
  328.     * @param string $group name of the cache group
  329.     * @return boolean true if no problem
  330.     * @access public
  331.     */
  332.     function remove($id, $group = 'default')
  333.     {
  334.         $this->_setFileName($id, $group);
  335.         if (!@unlink($this->_file)) {
  336.             $this->raiseError('Cache_Lite : Unable to remove cache !', -3);   
  337.             return false;
  338.         }
  339.         return true;
  340.     }
  341.  
  342.     /**
  343.     * Clean the cache
  344.     *
  345.     * if no group is specified all cache files will be destroyed
  346.     * else only cache files of the specified group will be destroyed
  347.     *
  348.     * @param string $group name of the cache group
  349.     * @return boolean true if no problem
  350.     * @access public
  351.     */
  352.     function clean($group = false)     
  353.     {
  354.         if ($this->_fileNameProtection) {
  355.             $motif = ($group) ? 'cache_'.md5($group).'_' : 'cache_';
  356.         } else {
  357.             $motif = ($group) ? 'cache_'.$group.'_' : 'cache_';
  358.         }
  359.         if ($this->_memoryCaching) {
  360.             while (list($key, $value) = each($this->_memoryCaching)) {
  361.                 if (strpos($key, $motif, 0)) {
  362.                     unset($this->_memoryCaching[$key]);
  363.                     $this->_memoryCachingCounter = $this->_memoryCachingCounter - 1;
  364.                 }
  365.             }
  366.             if ($this->_onlyMemoryCaching) {
  367.                 return true;
  368.             }
  369.         }
  370.         if (!($dh = opendir($this->_cacheDir))) {
  371.             $this->raiseError('Cache_Lite : Unable to open cache directory !', -4);
  372.             return false;
  373.         }
  374.         while ($file = readdir($dh)) {
  375.             if (($file != '.') && ($file != '..')) {
  376.                 $file = $this->_cacheDir . $file;
  377.                 if (is_file($file)) {
  378.                     if (strpos($file, $motif, 0)) {
  379.                         if (!@unlink($file)) {
  380.                             $this->raiseError('Cache_Lite : Unable to remove cache !', -3);
  381.                             return false;
  382.                         }
  383.                     }
  384.                 }
  385.             }
  386.         }
  387.         return true;
  388.     }
  389.     
  390.     /**
  391.     * Set to debug mode
  392.     *
  393.     * When an error is found, the script will stop and the message will be displayed
  394.     * (in debug mode only).
  395.     *
  396.     * @access public
  397.     */
  398.     function setToDebug()
  399.     {
  400.         $this->_pearErrorMode = CACHE_LITE_ERROR_DIE;
  401.     }
  402.  
  403.     /**
  404.     * Set a new life time
  405.     *
  406.     * @param int $newLifeTime new life time (in seconds)
  407.     * @access public
  408.     */
  409.     function setLifeTime($newLifeTime)
  410.     {
  411.         $this->_lifeTime = $newLifeTime;
  412.         $this->_refreshTime = time() - $newLifeTime;
  413.     }
  414.  
  415.     /**
  416.     *
  417.     * @access public
  418.     */
  419.     function saveMemoryCachingState($id, $group = 'default')
  420.     {
  421.         if ($this->_caching) {
  422.             $array = array(
  423.                 'counter' => $this->_memoryCachingCounter,
  424.                 'array' => $this->_memoryCachingState
  425.             );
  426.             $data = serialize($array);
  427.             $this->save($data, $id, $group);
  428.         }
  429.     }
  430.  
  431.     /**
  432.     *
  433.     * @access public
  434.     */
  435.     function getMemoryCachingState($id, $group = 'default', $doNotTestCacheValidity = false)
  436.     {
  437.         if ($this->_caching) {
  438.             if ($data = $this->get($id, $group, $doNotTestCacheValidity)) {
  439.                 $array = unserialize($data);
  440.                 $this->_memoryCachingCounter = $array['counter'];
  441.                 $this->_memoryCachingArray = $array['array'];
  442.             }
  443.         }
  444.     }
  445.     
  446.     /**
  447.     * Return the cache last modification time
  448.     *
  449.     * BE CAREFUL : THIS METHOD IS FOR HACKING ONLY !
  450.     *
  451.     * @return int last modification time
  452.     */
  453.     function lastModified() {
  454.         return filemtime($this->_file);
  455.     }
  456.     
  457.     /**
  458.     * Trigger a PEAR error
  459.     *
  460.     * To improve performances, the PEAR.php file is included dynamically.
  461.     * The file is so included only when an error is triggered. So, in most
  462.     * cases, the file isn't included and perfs are much better.
  463.     *
  464.     * @param string $msg error message
  465.     * @param int $code error code
  466.     * @access public
  467.     */
  468.     function raiseError($msg, $code)
  469.     {
  470.         include_once('PEAR.php');
  471.         PEAR::raiseError($msg, $code, $this->_pearErrorMode);
  472.     }
  473.  
  474.     // --- Private methods ---
  475.  
  476.     /**
  477.     *
  478.     * @access private
  479.     */
  480.     function _memoryCacheAdd($id, $data)
  481.     {
  482.         $this->_memoryCachingArray[$this->_file] = $data;
  483.         if ($this->_memoryCachingCounter >= $this->_memoryCachingLimit) {
  484.             list($key, $value) = each($this->_memoryCachingArray);
  485.             unset($this->_memoryCachingArray[$key]);
  486.         } else {
  487.             $this->_memoryCachingCounter = $this->_memoryCachingCounter + 1;
  488.         }
  489.     }
  490.  
  491.     /**
  492.     * Make a file name (with path)
  493.     *
  494.     * @param string $id cache id
  495.     * @param string $group name of the group
  496.     * @access private
  497.     */
  498.     function _setFileName($id, $group)
  499.     {
  500.         if ($this->_fileNameProtection) {
  501.             $this->_file = ($this->_cacheDir.'cache_'.md5($group).'_'.md5($id));
  502.         } else {
  503.             $this->_file = $this->_cacheDir.'cache_'.$group.'_'.$id;
  504.         }
  505.     }
  506.     
  507.     /**
  508.     * Read the cache file and return the content
  509.     *
  510.     * @return string content of the cache file
  511.     * @access private
  512.     */
  513.     function _read()
  514.     {
  515.         $fp = @fopen($this->_file, "rb");
  516.         if ($this->_fileLocking) @flock($fp, LOCK_SH);
  517.         if ($fp) {
  518.             clearstatcache(); // because the filesize can be cached by PHP itself...
  519.             $length = @filesize($this->_file);
  520.             $mqr = get_magic_quotes_runtime();
  521.             set_magic_quotes_runtime(0);
  522.             if ($this->_readControl) {
  523.                 $hashControl = @fread($fp, 32);
  524.                 $length = $length - 32;
  525.             } 
  526.             $data = @fread($fp, $length);
  527.             set_magic_quotes_runtime($mqr);
  528.             if ($this->_fileLocking) @flock($fp, LOCK_UN);
  529.             @fclose($fp);
  530.             if ($this->_readControl) {
  531.                 $hashData = $this->_hash($data, $this->_readControlType);
  532.                 if ($hashData != $hashControl) {
  533.                     @touch($this->_file, time() - 2*abs($this->_lifeTime)); 
  534.                     return false;
  535.                 }
  536.             }
  537.             return $data;
  538.         }
  539.         $this->raiseError('Cache_Lite : Unable to read cache !', -2);   
  540.         return false;
  541.     }
  542.     
  543.     /**
  544.     * Write the given data in the cache file
  545.     *
  546.     * @param string $data data to put in cache
  547.     * @return boolean true if ok
  548.     * @access private
  549.     */
  550.     function _write($data)
  551.     {
  552.         $fp = @fopen($this->_file, "wb");
  553.         if ($fp) {
  554.             if ($this->_fileLocking) @flock($fp, LOCK_EX);
  555.             if ($this->_readControl) {
  556.                 @fwrite($fp, $this->_hash($data, $this->_readControlType), 32);
  557.             }
  558.             $len = strlen($data);
  559.             @fwrite($fp, $data, $len);
  560.             if ($this->_fileLocking) @flock($fp, LOCK_UN);
  561.             @fclose($fp);
  562.             return true;
  563.         }
  564.         $this->raiseError('Cache_Lite : Unable to write cache !', -1);
  565.         return false;
  566.     }
  567.     
  568.     /**
  569.     * Write the given data in the cache file and control it just after to avoir corrupted cache entries
  570.     *
  571.     * @param string $data data to put in cache
  572.     * @return boolean true if the test is ok
  573.     * @access private
  574.     */
  575.     function _writeAndControl($data)
  576.     {
  577.         $this->_write($data);
  578.         $dataRead = $this->_read($data);
  579.         return ($dataRead==$data);
  580.     }
  581.     
  582.     /**
  583.     * Make a control key with the string containing datas
  584.     *
  585.     * @param string $data data
  586.     * @param string $controlType type of control 'md5', 'crc32' or 'strlen'
  587.     * @return string control key
  588.     * @access private
  589.     */
  590.     function _hash($data, $controlType)
  591.     {
  592.         switch ($controlType) {
  593.         case 'md5':
  594.             return md5($data);
  595.         case 'crc32':
  596.             return sprintf('% 32d', crc32($data));
  597.         case 'strlen':
  598.             return sprintf('% 32d', strlen($data));
  599.         default:
  600.             $this->raiseError('Unknown controlType ! (available values are only \'md5\', \'crc32\', \'strlen\')', -5);
  601.         }
  602.     }
  603.     
  604.  
  605. ?>
  606.