Location: PHPKode > projects > PEAR::HTML_Template_Xipe - SimpleTemplate > Cache.php
<?php
//
// +----------------------------------------------------------------------+
// | PHP Version 4                                                        |
// +----------------------------------------------------------------------+
// | Copyright (c) 1997-2002 The PHP Group                                |
// +----------------------------------------------------------------------+
// | This source file is subject to version 2.02 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          |
// | hide@address.com so we can mail you a copy immediately.               |
// +----------------------------------------------------------------------+
// | Authors: Wolfram Kriesing <hide@address.com>                      |
// +----------------------------------------------------------------------+
//
//  $Log: Cache.php,v $
//  Revision 1.5  2002/10/16 19:01:49  mccain
//  - replace # by //
//  - do cache-depend of class vars properly
//
//  Revision 1.4  2002/09/22 18:50:20  mccain
//  - dont use short tags anymore, to be able to work with xml files
//  - bugfix in dependencies
//
//  Revision 1.3  2002/07/31 13:43:47  mccain
//  - change mode of the cache-file so the user can remove it too
//
//  Revision 1.2  2002/06/02 23:15:11  mccain
//  - removed not needed require
//
//  Revision 1.1  2002/05/26 17:04:30  mccain
//  - initial commit, after restructuring and enhancing the engine
//
//

require_once('SimpleTemplate/XMLConfig.php');

/**
*   this class does the caching of final pages in pure html (or anything you use it for)
*   ok first an apology to the PEAR::Cache-team :-) i tried to
*   use the PEAR::Cache and it worked fine (was easy to implement) until i got to the
*   point that i wanted to include the cached content,
*   since the philosophy of this template engine is still based
*   on including either the template or the cached output.
*   the PEAR::Cache has a wonderful Cache implementation but
*   what i need here is the final file and there are 2 things that
*   make it impossible (or would need a change in the concept of this template engine) to
*   use the PEAR::Cache
*   1.  PEAR::Cache saves the file encoded and adds extra stuff in the file not only
*       the actual result, the html page in the normal case
*       it always has the expiration data and extra user data saved in the
*       cached file, and additionally the real data are encoded either bbase64
*       or serialized, which means a simple include of the cached file is not possible
*       i would have to decode it first etc.
*   2.  PEAR::Cache doesnt provide a method to give back the full path and filename of the
*       cached file, it does always only return the content of it
*       which of course is right for the way PEAR::Cache works, since the
*       data have to be decoded
*   so i have to implement the caching my self :-/
*
*   to use the cache u have to set the option 'enable-Cache' to true
*   when making an instance of the template engine
*   and in the file that shall be cached do the following
*   (assuming $tpl is an instance of the template engine with the cache option on):
*
*   // before calling '$tpl->isCached()' be sure that all the variables
*   // that the cache-file depends on (see your xml config) are set to the proper values
*   // so the cahe methods can properly determine if the file is already cached
*   // BE WARNED: letting a cache-file depend on i.e. $_REQUEST makes it possible to
*   // run a DOS-attack, since a new cache file has to be created everytime any
*   // request parameter changes, i.e. http://your.site.com/index.php?whatever
*   // this might flood your webservers diskspace
*   $myTplFile = 'your/template/file.tpl';
*   if( !$tpl->isCached( $myTplFile ) )
*   {
*       do all the stuff needed to be done to build a cacheable page
*       all the time consuming stuff, like db-requests, or getting data
*       using a webservice (which was my actual need for caching)
*   }
*   // simply include the file as usual
*   $tpl->compile( $myTplFile );
*   include( $tpl->getCompiledTemplate() );
*
*
*   @package    SimpleTemplate
*   @author     Wolfram Kriesing <hide@address.com>
*/
class SimpleTemplate_Cache extends SimpleTemplate_XMLConfig
{

    /**
    *   @var    string      the complete filename including path to the cached file
    */
    var $_cachedFile = '';

    /**
    *   @var    string      the global object reference name
    */
    var $_cacheObjReference = '';

    /**
    *   checks if the file is cached
    *
    *   @access     public
    *   @version    02/05/26
    *   @author     Wolfram Kriesing <hide@address.com>
    *   @return     boolean     returns true if the output is cached, false otherwise
    */
    function isCached()
    {
        if( $this->getOption('cache','time') === false )
        {
            return false;
        }

        // lets make sure that we define the object reference globally, which is called
        // from within the template
        // if we wouldnt do this here we would have a problem if a compiled template
        // would be included but the reference to $this is not defined, which is needed to call _cacheEnd()
        // at the end of the template
        $this->_createCacheObjRef();

        $cacheFile = $this->_getCacheFileName();

        if( !file_exists( $cacheFile ) )            // if the cached file doesnt exist
        {
            $this->_log('CACHE: cached file doenst exist: '.$cacheFile);
            return false;
        }

        // has the cached file expired?
        // the filetime is the time when it expires, this way we dont have to save the
        // caching time anywhere
        if( time() > filemtime( $cacheFile ) )
        {
            // if the caching time is 0 it shall be cached forever, so set the expire time to one year ahead
            // again
            if( $this->getOption('cache','time') === 0 )
            {
                touch( $cacheFile , time()+60*60*24*365 );
                return true;
            }
            $this->_log('CACHE: cached file has expired: '.$cacheFile);
            return false;
        }

        // if the template needs a recompile we dont cache
        if( $this->_needsRecompile() )
        {
            $this->_log('CACHE: template needs recompile');
            // remove the cached file so we know that we will recreate it
            // i am doing this because getCompiledTemplate() would return the old cached filename
            // also if the tpl was recompiled, because after compilation, needsRecompile is false and the cached file
            // does also exists, only that the expiration time might have changed, which getCompileTemplate, which calls
            // isCached, has no chance to notice
            @unlink($cacheFile);
            return false;
        }
        return true;
    }

    /**
    *   get the filename of the destination file
    *
    *   @access     public
    *   @version    02/05/26
    *   @author     Wolfram Kriesing <hide@address.com>
    *   @return     string  returns the complete filename
    */
    function _getCacheFileName()
    {
        if( $this->_cachedFile )
            return $this->_cachedFile;

        // add the hash which is calculated over the dependency data, like $_REQUEST, to have a unique filename
        // for the different dependency data

        $extension = '';
        if( ($depends = $this->getOption('cache','depends')) )
        {
//print $depends;
            $depends = explode(' ',$depends);
            // init $vars with the names that shall be cached, to be sure that if the values dont change but the names
            // we have to create a new name
            $vars = $depends;
            foreach( $depends as $aDepend )
            {
                // if the variable name is like $var['varName'] do only globalize '$var'
                // even though things like _REQUEST,_SESSION, etc. dont need to be globalized - but it does no harm either
                // and if it is a $class-> globalize only $class
                $globalize = preg_replace('/\[.*\]/','',$aDepend);
                $globalize = preg_replace('/->.*/','',$globalize);
                // serilaize the var since it might also be an array or object, or whatever
//print("global $globalize;\$var = serialize($aDepend);<br>");
                eval("global $globalize;\$var = serialize($aDepend);");
//print("$aDepend = $var<br>");
//print $this->_templateFile." ... $var<br>";
                $vars = md5("$vars:$var");
            }
            $extension = md5($vars).'.';
        }
//print "extension=$extension<br>depends = ";print_r($depends);

        $cacheFileName = $this->_compiledFilePrefix.$extension.$this->getOption('cacheFileExtension');
        $this->_cachedFile = $cacheFileName;
        return $this->_cachedFile;
    }

    /**
    *   this is called from within the template to write the content that shall be cached
    *
    *   @access     public
    *   @version    02/05/26
    *   @author     Wolfram Kriesing <hide@address.com>
    */
    function _cacheEnd()
    {
        $content = ob_get_contents();

        // get the destination filename
        $file = $this->_getCacheFileName();

//print '_cacheEnd write into: '.$file.' <br><br>';

        if( file_exists($file) )
            unlink($file);

        if( ($cfp = fopen( $file , 'w' )) )
        {
            if( $this->getOption('debug') > 0 )
                fwrite($cfp,'CACHED CONTENT<br>'.$content);
            else
                fwrite($cfp,$content);
            fclose($cfp);
            chmod($file,0777);
        }

        // set file modification time to the time when it expires,
        // this saves us the checking of the config data, either in options array or the xml file
        $expires = time()+$this->getOption('cache','time');
        if( $this->getOption('cache','time') === 0 )    // if the caching time is 0 set it to one year ahead
        {
            $expires = time()+60*60*24*365;
        }

        touch( $file , $expires );

        $this->_log('CACHE: caching file for '.$this->getOption('cache','time').' seconds (0 means forever), until: '.
                    date( 'd.m.Y H:i:s' , $expires ));

        ob_end_flush();
    }

    /**
    *   this adds the calls to the template needed for caching this file
    *   it is used as a filter by the parse method
    *
    *   @see        parse()
    *   @access     private
    *   @version    02/05/26
    *   @author     Wolfram Kriesing <hide@address.com>
    *   @param      string  the actual file content
    *   @return     string  the modified file content
    */
    function _makeCacheable( $input )
    {
        $input = sprintf(   '<'.'?php ob_start() ?'.'>%s<'.'?php $%s->_cacheEnd() ?'.'>' ,
                            $input ,
                            $this->_createCacheObjRef() );
        return $input;
    }


    /**
    *
    *
    *   @access     private
    *   @version    02/05/26
    *   @author     Wolfram Kriesing <hide@address.com>
    *   @return     string  the name of the global object ref
    */
    function _createCacheObjRef()
    {
        if( $this->_cacheObjReference )
            return $this->_cacheObjReference;
        // create global (!) object which can be called from within the template file
        // to call the cacheStart/cacheEnd-methods
        $this->_cacheObjReference = '_'.md5($this->_templateFile.'cache');   // has to start with a valid character for a varibale name, '_'
        // its a reference to this instance, so we can simply call the cache methods
        // we need to do it like this since we dont know the instance name of the template
        // engine and of this special instance neither, that's the easiest way to do it
        $GLOBALS[$this->_cacheObjReference] = &$this;
//print "_createCacheObjRef = {$this->_cacheObjReference} for {$this->_templateFile}<br>";
        return $this->_cacheObjReference;
    }



    //
    //  overwritten methods
    //

    /**
    *   return the cached file name if it is set, and it is only
    *   set if the file is cached and valid, that means not expired etc.
    *
    *   @access     public
    *   @version    02/05/26
    *   @author     Wolfram Kriesing <hide@address.com>
    *   @return     string  the filename used to include
    */
    function getCompiledTemplate()
    {
        if( $this->isCached() )
        {
            return $this->_cachedFile;
        }
        return parent::getCompiledTemplate();
    }

    /**
    *   do only really compile if the file is not cached
    *
    *   @access     public
    *   @version    02/05/26
    *   @author     Wolfram Kriesing <hide@address.com>
    *   @return     boolean     actually always true
    */
    function compile()
    {
        if( $this->isCached() )
            return true;
        return parent::compile();
    }

}
?>
Return current item: PEAR::HTML_Template_Xipe - SimpleTemplate