#native_company# #native_desc#
#native_cta#

PHP formcache

By Markus Fischer
on April 26, 2001

Version: 0.2

Type: Class

Category: HTML

License: GNU General Public License

Description: Persistently cache whole pages identified by their md5 hash.

<?
    /*
     * $Header: /var/cvs/formcache/formcache.php,v 1.6 2001/04/26 16:44:24 mfischer Exp $
     *
     *     Class        :    formcache
     *     Version        :    0.2
     *     Author        :    [email protected]
     *                     http://josefine.ben.tuwien.ac.at/~mfischer/
     *
     *     Description    :    Class which handles cached form submission
     *                    
     *    Exaomple    :    
     *                    If someone just submitted the form and its in the cache,
     *                    serve the user the cached result :
     *
     *                    $fc = new formcache( 'myform', '/tmp/php/');
     *    
     *                    if( $fc->exists( array_merge( $HTTP_POST_VARS, $HTTP_GET_VARS)))
     *                        echo join( '', $fc->serve());
     *                    else
     *                        doGetResult(); // that's up to you
     *
     *
     *                    Somewhere in 'doGetResult()' you then write the output to the cache :
     *    
     *                    $fc->cache( $output);
     */

     // That's up to the end-developer, really
     // error_reporting( E_ALL ^ E_NOTICE);


    /*
        Change this to a default setting which makes sense to your enviroment

        FORMCACHE_NAMESPACE    -    Is part of the name of the cachefile to easier distingiush cached files from different forms

        FORMCACHE_CACHDIR    -    The directory where everything gets writte to and served from
                                Make sure Apache/PHP has the proper rights to write and read files from there
                                If the directory does not exist the class tries to create it automatically
    */
    define( 'FORMCACHE_NAMESPACE', 'formcache');
    define( 'FORMCACHE_CACHEDIR', '/tmp/');


    /**
    * Provides detection if a FORM was submitted a second time a feed
    * feed brwoser with resulting page from cache
    * @access        public
    * @author        Markus Fischer <[email protected]>
    * @link            http://josefine.ben.tuwien.ac.at Homepage of the author
    * @copyright    Netway AG
    */
    class formcache {

        /**
        * Namespace declaration
        * @var string
        * @access private
        */
        var $namespace;

        /**
        * Directory name where to store/retrieve cached files
        * @var string
        * @access private
        */
        var $cachedir;

        /**
        * Cache filename
        * @var string
        * @access private
        */
        var $cachefile;

        /**
        * Wheter use compression or not when writing to the cache. Defaults to false.
        * @var boolean
        * @access public
        */
        var $compress;

        /**
        * Contains last error message
        * @var string
        * @access public
        */
        var $err;
        
        /**
        * Constructor
        *
        * Instances a new formcheck object.
        *
        * @param string The Namespace the cache lives under
        * @param string Optional directory for reading/writing cached files. Defaults to FORMCACHE_CACHDIR
        * @param boolean Whether use gzip compression (TRUE) or not (FALSE)
        */
        function formcache( $namespace = FORMCACHE_NAMESPACE, $cachedir = FORMCACHE_CACHDIR, $compress = false) {
            $this->namespace    = $namespace;
            if( empty( $this->namespace))
                $this->namespace = FORMCACHE_NAMESPACE;

            $this->cachedir        = $cachedir;
            if( empty( $this->cachedir))
                $this->cachedir = FORMCACHE_CACHEDIR;
            else
                if( $this->cachedir[ strlen( $this->cachedir) - 1] != '/')
                    $this->cachedir .= '/';
                    
            if( ! @is_dir( $this->cachedir)) {
                $dirs = split( '/', $this->cachedir);
                $createdir = '';
                foreach( $dirs as $dir) { $createdir .= '/' . $dir; @mkdir( $createdir, 0775);}
            }

            $this->cachefile    = '';
            if( $compress)
                $this->compress = true;
            else
                $this->compress    = false;
        }

        /**
        * Elicit the disambiguatity for cache-hits by means of the form elements
        *
        * @param array Form elements
        * @return boolean True if uniqie identifier (cachefile) has been generated; False if not (ie. $form was empty or not an array)
        * @access public
        */
        function formdata( $form) {
            $param = array();

            if( empty( $form) || ( ! gettype( $form) == 'array'))
                return $this->error( 'Form elements array is empty or not of type 'array'');

            // Different browsers return form elements in different orders => fix this!
            ksort( $form, SORT_STRING);

            foreach( $form as $key => $value)
                $param[] = urlencode( $key) . '=' . urlencode( $value);

            $this->cachefile = $this->cachedir . $this->namespace . '-' . md5( join( '&', $param)) . '.cache';

            return true;
        }

        /**
        * Check wether a cached file exists or not
        *
        * @param array A optional hash containing all form elements for determining the disambiguatity.
        *              Use this if you haven't called $this-set() before
        *
        * @return boolean True if a cached file exists; False if not (or there was no cachefile previously generated)
        *
        * @acces public
        */
        function exists( $form = '') {

            $this->formdata( $form);

            if( empty( $this->cachefile))
                return $this->error( 'Cachefile is empty; initialize first with method formadata()');

            if( file_exists( $this->cachefile))
                return true;
            else
                return $this->error( 'Cachefile does not exist');
        }

        /**
        * Serve file from cache
        *
        * @param array  A optional hash containing all form elements for determining the disambiguatity.
        *                Use this if you haven't called $this-set() before
        *
        * @return mixed    Array containing the cached file, line by line. False on failure
        *
        * @access public
        */
        function serve( $form = '') {

            $this->formdata( $form);

            if( empty( $this->cachefile))
                return $this->error( 'Cachefile is empty; initialize first with method formadata()');

            if( $this->exists())
                return @gzfile( $this->cachefile);
            else
                return false;
        }

        /**
        * Caches the data
        *
        * Note that this function overwrites without warning; use exists() before inserting
        * 
        * @param array    Data to cache
        *
        * @return boolean True on successful cache insertion; False if something has gone wrong
        *
        * @access public
        */
        function cache( $data, $form = '') {

            if( empty( $data) || ( gettype( $data) != 'array'))
                return $this->error( 'Data is empty or not an array');
        
            if( ! empty( $form))
                $this->formdata( $form);

            if( empty( $this->cachefile))
                return $this->error( 'Cachefile is empty; initialize first with method formdata()');

            if( ( $fp = fopen( $this->cachefile, 'wb'))) {
                if( $this->compress) {
                    $compressed = gzencode( join( '', $data));
                    if( strlen( $compressed) != fwrite( $fp, $compressed, strlen( $compressed))) {
                        fclose( $fp);
                        return $this->error( 'Compressed data only partially written; low on disk space ?');
                    }
                } else
                    foreach( $data as $line)
                        if( strlen( $line) != fwrite( $fp, $line, strlen( $line))) {
                            fclose( $fp);
                            return $this->error( 'Data only partially written; low on disk space ?');
                        }
                if( fclose( $fp))
                    return true;
                else
                    $this->error( 'Can't close file handle');
            } else
                return $this->error( 'Can't open cache file for writing');
        }


        /**
        * Stores error message
        *
        * This method is just for convinience. all it does it stores the parameter in a member variable $this->errmsg and always returns false
        *
        * @param string Optional error message string
        * @return boolean Always returns false
        * @access private
        */
        function error( $message = '') {
            $this->err[] = $message;
            return false;
        }

    }
?>