sbergmann               Thu Mar  1 07:35:18 2001 EDT

  Added files:                 
    /php4/pear/Cache    Cache.php 
    /php4/pear/Cache/Container  cache_container.php 
                                cache_container_file.php 

  Removed files:               
    /php4/pear/Cache    Function_Cache.php 
  Log:
  Removed my old Function_Cache class implementation. Initial commit of the 
multi-purpose Cache System Ulf Wendel and I worked on the last couple of days. In this 
first phase I commit the Cache base class, the Cache Container Base class and the 
File-based Cache Container. During the next couple of hours I hope to finish the Cache 
Container classes for caching with PEAR/DB, PHPLIB/DB_Sql and Native MySQL Heap Table 
support. I also have working versions of classes extending Cache, namely 
Function_Cache, Graphics_Cache and Output_Cache. Bare with me, tomorrow at the latest 
PEAR/Cache will be a extensive and efficient means for general caching purposes, 
besides specialized caching for graphics.
  

Index: php4/pear/Cache/Cache.php
+++ php4/pear/Cache/Cache.php
<?php
// +----------------------------------------------------------------------+
// | PHP version 4.0                                                      |
// +----------------------------------------------------------------------+
// | Copyright (c) 1997, 1998, 1999, 2000, 2001 The PHP Group             |
// +----------------------------------------------------------------------+
// | This source file is subject to version 2.0 of the PHP license,       |
// | that is bundled with this package in the file LICENSE, and is        |
// | available at through the world-wide-web at                           |
// | http://www.php.net/license/2_02.txt.                                 |
// | If you did not receive a copy of the PHP license and are unable to   |
// | obtain it through the world-wide-web, please send a note to          |
// | [EMAIL PROTECTED] so we can mail you a copy immediately.               |
// +----------------------------------------------------------------------+
// | Authors: Ulf Wendel <[EMAIL PROTECTED]>                           |
// |          Sebastian Bergmann <[EMAIL PROTECTED]>               |
// +----------------------------------------------------------------------+
//
// $Id: Cache.php,v 1.1 2001/03/01 15:35:18 sbergmann Exp $

/**
* Cache is a base class for cache implementations.
*
* TODO: Simple usage example goes here.
*
* @author   Ulf Wendel <[EMAIL PROTECTED]>
* @version  $Id: Cache.php,v 1.1 2001/03/01 15:35:18 sbergmann Exp $
* @package  Cache
* @access   public 
*/
class Cache {

    /**
    * Disables the caching.
    *
    * TODO: Add explanation what this is good for.
    *
    * @var      boolean
    * @access   public
    */
    var $no_cache = false;

    /**
    * Garbage collection: probability in seconds
    *
    * If set to a value above 0 a garbage collection will 
    * flush all cache entries older than the specified number 
    * of seconds.
    *
    * @var      integer
    * @see      $gc_probability
    * @access   public
    */
    var $gc_time  = 1;

    /**
    * Garbage collection: probability in percent
    *
    * TODO: Add an explanation.
    *
    * @var      integer     0 => never
    * @see      $gc_time
    * @access   public
    */
    var $gc_probability = 1;
    
    /**
    * Storage container object.
    * 
    * @var  object cache_container
    */    
    var $container;

    //
    // public methods
    //

    /**    
    *
    * @param    string  Name of storage container class
    * @param    array   Array with storage class dependend config options
    * @see      setOptions()
    */
    function cache($storage_class, $storage_options = "") {
    
        $storage_class = "cache_container_" . $storage_class;
        include_once("Cache/Container/{$storage_class}.php");
        $this->container = new $storage_class($storage_options);

        $this->garbageCollection();
    } // end constructor
    
    /**
    * Returns the requested dataset it if exists and is not expired
    *  
    * @param    string  dataset ID
    * @return   mixed   cached data or NULL on failure
    * @access   public
    */
    function get($id) {
        if ($this->no_cache)
            return "";
            
        if ($this->isCached($id) && !$this->isExpired($id))
            return $this->load($id);
        
        return NULL;            
    } // end func get

    /**
    * Stores the given data in the cache.
    * 
    * @param    string  dataset ID used as cache identifier
    * @param    mixed   data to cache
    * @param    integer lifetime of the cached data in seconds - 0 for endless
    * @return   boolean
    * @access   public
    */
    function save($id, $data, $expires = 0) {
        if ($this->no_cache)
            return true;
            
        return $this->container->save($id, $data, $expires);
    } // end func save
    
    /**
    * Loads the given ID from the cache.
    * 
    * @param    string  dataset ID
    * @return   mixed   cached data or NULL on failure 
    * @access   public
    */
    function load($id) {
        if ($this->no_cache)
            return "";
            
        return $this->container->load($id);
    } // end func load
    
    /**
    * Removes the specified dataset from the cache.
    * 
    * @param    string  dataset ID
    * @return   boolean
    * @access   public
    */
    function delete($id) {
        if ($this->no_cache)
            return true;
            
        return $this->container->delete($id);
    } // end func delete
    
    /**
    * Flushes the cache - removes all data from it
    * 
    * @return   integer number of removed datasets
    */
    function flush() {
        if ($this->no_cache)
            return true;
            
        return $this->container->flush();
    } // end func flush
    
    /**
    * Checks if a dataset exists.
    * 
    * Note: this does not say that the cached data is not expired!
    * 
    * @param    string  dataset ID
    * @return   boolean
    * @access   public
    */
    function isCached($id) {
        if ($this->no_cache)
            return false;
            
        return $this->container->isCached($id);
    } // end func isCached
    
    /**
    * Checks if a dataset is expired
    * 
    * @param    string  dataset ID
    * @param    integer maximum age for the cached data in seconds - 0 for endless
    *                   If the cached data is older but the given lifetime it will
    *                   be removed from the cache. You don't have to provide this 
    *                   argument if you call isExpired(). Every dataset knows 
    *                   it's expire date and will be removed automatically. Use 
    *                   this only if you know what you're doing...
    * @return   boolean
    * @access   public
    */
    function isExpired($id, $max_age = 0) {
        if ($this->no_cache)
            return true;
            
        return $this->container->isExpired($id, $max_age);
    } // end func isExpired
    
    /**
    * Generates a "unique" ID for the given value
    * 
    * This is a quick but dirty hack to get a "unique" ID for a any kind of variable.
    * ID clashes might occur from time to time although they are extreme unlikely!
    *
    * @param    mixed   variable to generate a ID for
    * @return   string  "unique" ID
    * @access   public
    */
    function generateID($variable) {
        // WARNING: ID clashes are possible although unlikely
        return md5(serialize($variable));
    }

    /**
    * Calls the garbage collector of the storage object with a certain probability
    * 
    * @param    boolean Force a garbage collection run?
    * @see  $gc_probability, $gc_time, setOptions()
    */
    function garbageCollection($force = false) {
        static $last_run = 0;
        
        if ($this->no_cache)
            return;

        srand((double) microtime() * 1000000);
        
        // time and probability based
        if (($force) || ($last_run && $last_run < time() + $this->gc_time) || (rand(1, 
100) < $this->gc_probability)) {
            $this->container->garbageCollection();
            $last_run = time();
        }
        
    } // end func garbageCollection

    
} // end class cache 
?>
Index: php4/pear/Cache/Container/cache_container.php
+++ php4/pear/Cache/Container/cache_container.php
<?php
// +----------------------------------------------------------------------+
// | PHP version 4.0                                                      |
// +----------------------------------------------------------------------+
// | Copyright (c) 1997, 1998, 1999, 2000, 2001 The PHP Group             |
// +----------------------------------------------------------------------+
// | This source file is subject to version 2.0 of the PHP license,       |
// | that is bundled with this package in the file LICENSE, and is        |
// | available at through the world-wide-web at                           |
// | http://www.php.net/license/2_02.txt.                                 |
// | If you did not receive a copy of the PHP license and are unable to   |
// | obtain it through the world-wide-web, please send a note to          |
// | [EMAIL PROTECTED] so we can mail you a copy immediately.               |
// +----------------------------------------------------------------------+
// | Authors: Ulf Wendel <[EMAIL PROTECTED]>                           |
// |          Sebastian Bergmann <[EMAIL PROTECTED]>               |
// +----------------------------------------------------------------------+
//
// $Id: cache_container.php,v 1.1 2001/03/01 15:35:18 sbergmann Exp $

/**
* Common base class of all cache storage container.
* 
* To speed up things we do a preload you should know about, otherwise it might 
* play you a trick. The Cache controller classes (Cache/Cache, Cache/Output, ...)
* usually do something like is (isCached($id) && !isExpired($id)) return 
$container->load($id).
* if you implement isCached(), isExpired() and load() straight ahead, each of this 
* functions will result in a storage medium (db, file,...) access. This generates too 
much load. 
* Now, a simple speculative preload should saves time in most cases. Whenever 
* one of the mentioned methods is invoked we preload the cached dataset into class 
variables.
* That means that we have only one storage medium access for the sequence
*  (isCached($id) && !isExpired($id)) return $container->load($id).
* The bad thing is that the preloaded data might be outdated meanwhile, which is 
* unlikely but for you power users, be warned. If you do not want the preload 
* you should switch it off by setting the class variable $preload to false. Anyway, 
this is 
* not recommended!
* 
* @author   Ulf Wendel <[EMAIL PROTECTED]>
* @version  $Id: cache_container.php,v 1.1 2001/03/01 15:35:18 sbergmann Exp $
* @package  Cache
* @access   public
* @abstract
*/
class cache_container {

    /**
    * Flag indicating wheter to preload datasets.
    *
    * See the class description for more details.
    *
    * @var  boolean
    */
    var $preload = true;
    
    /**
    * ID of a preloaded dataset
    *
    * @var  string
    */
    var $id = "";
    
    /**
    * Expiration timestamp of a preloaded dataset.
    * 
    * @var  integer 0 means never, endless
    */
    var $expires = 0;
    
    /**
    * Value of a preloaded dataset.
    * 
    * @var  string
    */
    var $data = "";
    
    /**
    * Flag indicating that the dataset requested for preloading is unknown.
    *  
    * @var  boolean
    */
    var $unknown = true;
    
    /**
    * Encoding mode for cache data: base64 or addslashes() (slash).
    *
    * @var  string  base64 or slash
    */
    var $encoding_mode = "base64";
        
    /**
    * Loads a dataset from the cache.
    * 
    * @param    string  dataset ID
    * @return   mixed   dataset value or NULL on failure
    * @access   public
    */
    function load($id) {

        if ($this->preload) {
        
            if ($this->id != $id)
                $this->preload($id);
            return $this->data;
                            
        } else {
        
            list( , $data) = $this->fetch($id);
            return $data;
            
        }

    } // end func load
    
    /**
    * Checks if a dataset is expired.
    * 
    * @param    string  dataset ID
    * @param    integer maximum age in seconds of the data set
    * @return   boolean 
    * @access   public
    */
    function isExpired($id, $max_age = 0) {

        if ($this->preload) {
            
          if ($this->id != $id)
            $this->preload($id);
          
          if ($this->unknown)
            return false;
                        
        } else {
        
            // check if at all it is cached
            if (!$this->isCached($id))
                return false;
                
            // I'm lazy...
            list($this->expires, ) = $this->fetch($id);
        }
    
        // endless
        if (0 == $this->expires)
            return false;
        
        // you feel fine, Ulf?
        if ($expired  = ($this->expires <= time() || ($max_age && $this->expires <= 
time() + $max_age)) ) {        
           
           $this->delete($id);
           
           // remove preloaded values
           $this->id = "";
           $this->data = "";
           $this->expires = 0;
           $this->unknown = true;
           
        }
        
        return $expired;
    } // end func isExpired
    
    /**
    * Checks if a dataset is cached.
    *
    * @param    string  dataset ID
    * @return   boolean
    */
    function isCached($id) {
    
        if ($this->preload) {
            
            if ($this->id != $id)
                $this->preload($id);

            return !($this->unknown);                            
            
        } else {
            
            return $this->idExists($id);
            
        }
        
    } // end func isCached
    
    //
    // abstract methods
    //
    
    /**
    * Fetches a dataset from the storage medium.
    *
    * @param    string  dataset ID
    * @return   array   format: [expire date, cached data]
    * @throws   CacheError
    * @abstract
    */
    function fetch($id) {
        return array(NULL, NULL);
    } // end func fetch
    
    /**
    * Stores a dataset.
    * 
    * @param    string  dataset ID
    * @param    mixed   data to store
    * @param    mixed   userdefined expire date
    * @return   boolean
    * @throws   CacheError
    * @access   public
    * @abstract
    */
    function save($id, $data, $expire = 0) {
        return NULL;
    } // end func save
    
    
    /**
    * Deletes a dataset.
    * 
    * @param    string  dataset ID
    * @return   boolean  
    * @access   public
    * @abstract
    */     
    function delete($id) {
        return NULL;
    } // end func delete
    
    /**
    * Flushes the cache - removes all caches datasets from the cache
    * 
    * @return   integer Number of removed datasets, -1 on failure
    * @throws   CacheError
    * @access   public
    * @abstract
    */
    function flush() {
        return NULL;
    } // end func flush
    
    /**
    * Checks if a dataset exists
    * 
    * @param    string  dataset ID
    * @return   boolean 
    * @access   public
    * @abstract
    */
    function idExists($id) {
        return NULL;
    } // end func idExists
    
    /**
    * Starts the garbage collection.
    * 
    * @access   public
    * @abstract
    */
    function garbageCollection() {
    } // end func garbageCollection

    /**
    * Does a speculative preload of a dataset
    *
    * @param    string  dataset ID
    * @return   boolean
    */ 
    function preload($id) {
        
        // whatever happens, remember the preloaded ID
        $this->id = $id;        
        
        list($this->expires, $this->data) = $this->fetch($id);
        if (NULL === $this->data) {
            
            // Uuups, unknown ID
            
            // clear the internal preload values
            $this->data = "";
            $this->expires = -1;
            $this->unknown = true;
            
            return false;
        }
        
        $this->unknown = false;
        
        return true;
    } // end func preload
    
    /**
    * Imports the requested datafields as object variables if allowed
    * 
    * @param    array   List of fields to be imported as object variables
    * @param    array   List of allowed datafields
    */
    function setOptions($requested, $allowed) {

        foreach ($allowed as $k => $field)
            if (isset($requested[$field]))
                $this->$field = $requested[$field];
                
    } // end func setOptions
    
    /**
    * Encodes the data for the storage container
    * 
    * @var  mixed data to encode
    */
    function encode($data) {
    
        if ("base64" == $this->encoding_mode) 
            return base64_encode(serialize($data));
        else 
            return addslashes(serialize($data));
    } // end func encode
    
    /**
    * Decodes the data from the storage container.
    * 
    * @var  mixed
    */
    function decode($data) {
        
        if ("base64" == $this->encoding_mode)
            return unserialize(base64_decode($data));
        else
            return unserialize(stripslashes($data));
                        
    } // end func decode
    
} // end class cache_container
?>
Index: php4/pear/Cache/Container/cache_container_file.php
+++ php4/pear/Cache/Container/cache_container_file.php
<?php
// +----------------------------------------------------------------------+
// | PHP version 4.0                                                      |
// +----------------------------------------------------------------------+
// | Copyright (c) 1997, 1998, 1999, 2000, 2001 The PHP Group             |
// +----------------------------------------------------------------------+
// | This source file is subject to version 2.0 of the PHP license,       |
// | that is bundled with this package in the file LICENSE, and is        |
// | available at through the world-wide-web at                           |
// | http://www.php.net/license/2_02.txt.                                 |
// | If you did not receive a copy of the PHP license and are unable to   |
// | obtain it through the world-wide-web, please send a note to          |
// | [EMAIL PROTECTED] so we can mail you a copy immediately.               |
// +----------------------------------------------------------------------+
// | Authors: Ulf Wendel <[EMAIL PROTECTED]>                           |
// |          Sebastian Bergmann <[EMAIL PROTECTED]>               |
// +----------------------------------------------------------------------+
//
// $Id: cache_container_file.php,v 1.1 2001/03/01 15:35:18 sbergmann Exp $

require_once("Cache/Container/cache_container.php");

/**
* Stores cache contents in a file.
*
* @author   Ulf Wendel  <[EMAIL PROTECTED]>
* @version  $Id: cache_container_file.php,v 1.1 2001/03/01 15:35:18 sbergmann Exp $
*/
class cache_container_file extends cache_container {

    /**
    * Directory where to put the cache files.
    * 
    * @var  string  Make sure to add a trailing slash
    */
    var $cache_dir = "";
    
    /**
    * Filename prefix for cache files.
    * 
    * You can use the filename prefix to implement a "domain" based cache or just
    * to give the files a more descriptive name. The word "domain" is borroed from 
    * a user authentification system. One user id (cached dataset with the ID x) 
    * may exists in different domains (different filename prefix). You might want
    * to use this to have different cache values for a production, development and
    * quality assurance system. If you want the production cache not to be influenced
    * by the quality assurance activities, use different filename prefixes for them.
    * 
    * I personally don't think that you'll never need this, but 640kb happend to be 
    * not enough, so... you know what I mean. If you find a useful application of the
    * feature please update this inline doc.
    *
    * @var  string
    */
    var $filename_prefix = "";

    /**
    * Creates the cache directory if neccessary
    * 
    * @param    array   Config options: ["cache_dir" => ..., "filename_prefix" => ...]
    */
    function cache_container_file($options = "") {

        if (is_array($options))
            $this->setOptions($options, array("cache_dir", "filename_prefix"));

        if (!file_exists($this->cache_dir) || !is_dir($this->cache_dir))
            mkdir($this->cache_dir, 0755);
        
    } // end func contructor
    
    
    function fetch($id) {
        
        $file = $this->getFilename($id);
        if (!file_exists($file)) 
            return array(NULL, NULL);
       
        // retrive the content
        if (!($fh = @fopen($file, "rb")))
            return new CacheError("Can't access cache file '$file'. Check access 
rights and path.", __FILE__, __LINE__);
        
        // file format:
        // 1st line: expiration date
        // 2nd+ lines: cache data
        $expire = trim(fgets($fh, 11));
        $data = $this->decode(fread($fh, filesize($file)));
        
        fclose($fh);
        
        return array($expire, $data);
    } // end func fetch
    
    
    function save($id, $data, $expire = 0) {
        
        $file = $this->getFilename($id);
        if (!($fh = @fopen($file, "wb")))
            return new CacheError("Can't access '$file' to store cache data. Check 
access rights and path.", __FILE__, __LINE__);

        // file format:
        // 1st line: expiration date
        // 2nd+ lines: cache data
        fwrite($fh, $expire . "\r\n");
        fwrite($fh, $this->encode($data));
        
        fclose($fh);
        
        // I'm not sure if we need this
        touch($file);
        
        return true;        
    } // end func save
    
    
    function delete($id) {
        
        $file = $this->getFilename($id);
        if (file_exists($file)) {
            
            unlink($file);
            clearstatcache();
            
            return true;
        }
                    
        return false;            
    } // end func delete
    
    
    function flush() {
        
        if (!($dh = opendir($this->cache_dir)))
            return new CacheError("Can't access the cache directory 
'$this->cache_dir'. Check access rights and path", __FILE__, __LINE__);

        $num_removed = 0;                
        while ($file = readdir($dh)) {
            if ("." == $file || ".." == $file)
                continue;
                
            unlink($this->cache_dir . $file);
            $num_removed++;
        }
        
        clearstatcache();

        return $num_removed;
    } // end func flush

    
    function idExists($id) {
    
        return file_exists($this->cache_dir . $this->filename_prefix . $id);
        
    } // end func idExists
    
    
    /**
    * Deletes all expired files.
    * 
    * Garbage collection for files is a rather "expensive", "long time"
    * operation. All files in the cache directory have to be examined which 
    * means that they must be opened for reading, the expiration date has to be 
    * read from them and if neccessary they have to be unlinked (removed).
    * If you have a user comment for a good default gc probability please add it to
    * to the inline docs.
    */
    function garbageCollection() {
        
        if (!($dh = opendir($this->cache_dir)))
            return new CacheError("Can't access cache directory.", __FILE__, __LINE__);

        while ($file = readdir($dh)) {
            if ("." == $file || ".." == $file)
                continue;

            $file = $this->cache_dir . $file;
                    
            // skip trouble makers but inform the user                
            if (!($fh = @fopen($file, "rb"))) {
                new CacheError("Can't access cache file '$file' skipping for garbage 
collection. Check access rights and path.", __FILE__, __LINE__);
                continue;
            }
            
            $expire = time(fgets($fh, 11));
            fclose($fh);
            
            // remove if expired
            if ($expire && $expire <= time())
                unlink($file);                
        }            
    
        closedir($dh);
        
        // flush the disk state cache
        clearstatcache();
        
    } // end func garbageCollection
    
    
    /**
    * Returns the filename for the specified id.
    *
    * @param    string  dataset ID
    * @return   string  full filename with the path
    * @access   public
    */
    function getFilename($id) {
        return $this->cache_dir . $this->filename_prefix . $id;
    } // end func getFilename

} // end class cache_container_file
?>
-- 
PHP CVS Mailing List (http://www.php.net/)
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]
To contact the list administrators, e-mail: [EMAIL PROTECTED]

Reply via email to