Skip to content
Snippets Groups Projects
Select Git revision
  • aab20d9139af6d0ce5b1184b466fb184387eb710
  • master default protected
  • Legacy_Php7
3 results

cache.class.php

Blame
  • user avatar
    Axel Hahn authored
    - added: modal infobox in build page if you switch the branch
    - added: git uses a cache for taglist and revision infos (ttl is 5 min)
    60bb1266
    History
    cache.class.php 17.34 KiB
    <?php
    /** 
     * AXELS CACHE CLASS<br>
     * <br>
     * THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE <br>
     * LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR <br>
     * OTHER PARTIES PROVIDE THE PROGRAM ?AS IS? WITHOUT WARRANTY OF ANY KIND, <br>
     * EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED <br>
     * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE <br>
     * ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. <br>
     * SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY <br>
     * SERVICING, REPAIR OR CORRECTION.<br>
     * <br>
     * --------------------------------------------------------------------------------<br>
     * <br>
     * --- HISTORY:<br>
     * 2009-07-20  1.0  cache class on www.axel-hahn.de<br>
     * 2011-08-27  1.1  comments added; sCacheFile is private<br>
     * 2012-02-04  2.0  cache serialzable types; more methods, i.e.:<br>
     *                   - comparison of timestimp with a sourcefile<br>
     *                   - cleanup unused cachefiles<br>
     * 2012-05-15  2.1  isExpired() returns as bool; new method iExpired() to get <br>
     *                  expiration in sec<br>
     * 2014-02-27  2.2  - rename to AhCache<br>
     *                  - _cleanup checks with file_exists<br>
     * 2014-03-31  2.3  - added _setup() that to includes custom settings<br>
     *                  - limit number of files in cache directory<br>
     * --------------------------------------------------------------------------------<br>
     * @version 2.3
     * @author Axel Hahn
     * @link http://www.axel-hahn.de/php_contentcache
     * @license GPL
     * @license http://www.gnu.org/licenses/gpl-3.0.html GPL 3.0
     * @package Axels Cache
     */
    class AhCache {
    
        /**
         * a module name string is used as relative cache path
         * @var string 
         */
        var $sModule = '';
    
        /**
         * id of cachefile (filename will be generated from it)
         * @var string 
         */
        var $sCacheID = '';
    
        /**
         * where to store all cache data - it can be outside docRoot. If it is 
         * below docRoot think about forbidding access
         * If empty it will be set in the constructor to [webroot]/~cache/
         * or $TEMP/~cache/ for CLI
         * @var string 
         */
        private $_sCacheDir = '';
        // private $_sCacheDir='/tmp/';
    
        /**
         * absolute filename of cache file
         * @var string 
         */
        private $_sCacheFile = '';
    
        /**
         * divider to limit count of cachefiles
         * @var type 
         */
        private $_sCacheDirDevider = false;
        
        /**
         * fileextension for storing cachefiles (without ".")
         * @var string
         */
        private $_sCacheExt = 'cacheclass2';
    
        /**
         * Expiration timestamp; 
         * It will be calculated with current time + ttl in the write() method
         * TTL can be read with getExpire()
         * @var integer
         */
        private $_tsExpire = -1;
    
        /**
         * TTL (time to live) in s; 
         * TTL can be set in methods setTtl($iTtl) or write($data, $iTtl)
         * TTL can be read with getTtl()
         * @var integer
         */
        private $_iTtl = -1;
    
        /**
         * cachedata and file infos of cachefile (returned array of php function stat)
         * @var array
         */
        private $_aCacheInfos = array();
    
        /* ----------------------------------------------------------------------
          constructor
          ---------------------------------------------------------------------- */
    
        /**
         * constructor
         * @param  string  $sModule   name of module or app that uses the cache
         * @param  string  $sCacheID  cache-id (must be uniq for a module; used to generate filename of cachefile)
         * @return boolean 
         */
        public function __construct($sModule = ".", $sCacheID = '.') {
            $this->_setup();
            if (!$sModule)
                die("ERROR: no module was given.<br>");
            if (!$sCacheID)
                die("ERROR: no id was given.<br>");
    
            $this->sModule = $sModule;
            $this->sCacheID = $sCacheID;
    
            $this->_getCacheFilename();
            $this->read();
    
            return true;
        }
    
        /* ----------------------------------------------------------------------
          private funtions
          ---------------------------------------------------------------------- */
    
        // ----------------------------------------------------------------------
        /**
         * load custom config from cache.class_config.php and set a cache
         * directory
         */
        private function _setup() {
            $sCfgfile="cache.class_config.php";
            if (file_exists(__DIR__ . "/".$sCfgfile)){
                include($sCfgfile);
            }
            if (!$this->_sCacheDir) {
                if (getenv("TEMP"))
                    $this->_sCacheDir = str_replace("\\", "/", getenv("TEMP"));
                if ($_SERVER['DOCUMENT_ROOT'])
                    $this->_sCacheDir = $_SERVER['DOCUMENT_ROOT'];
                if (!$this->_sCacheDir)
                    $this->_sCacheDir = ".";
                $this->_sCacheDir .= "/~cache";
            }
        }
                
        // ----------------------------------------------------------------------
        /**
         * recursive cleanup of a given directory; this function is used by
         * public function cleanup()
         * @since 2.0
         * @param string $sDir full path of a local directory
         * @param string $iTS  timestamp
         * @return     true
         */
        private function _cleanupDir($sDir, $iTS) {
            echo "<ul><li class=\"dir\">DIR: <strong>$sDir</strong><ul>";
    
            if (!file_exists($sDir)) {
                echo "\t Directory does not exist - [$sDir]</ul></li></ul>";
                return;
            }
            if (!($d = dir($sDir))) {
                echo "\t Cannot open directory - [$sDir]</ul></li></ul>";
                return;
            }
            while ($entry = $d->read()) {
                $sEntry = $sDir . "/" . $entry;
                if (is_dir($sEntry) && $entry != '.' && $entry != '..') {
                    $this->_cleanupDir($sEntry, $iTS);
                }
                if (file_exists($sEntry)) {
                    $ext = pathinfo($sEntry, PATHINFO_EXTENSION);
                    $ext = substr($sEntry, strrpos($sEntry, '.') + 1);
    
                    $exts = explode(".", $sEntry);
                    $n = count($exts) - 1;
                    $ext = $exts[$n];
    
                    if ($ext == $this->_sCacheExt) {
    
                        $aTmp = stat($sEntry);
                        $iAge = date("U") - $aTmp['mtime'];
                        if ($aTmp['mtime'] <= $iTS) {
                            echo "<li class=\"delfile\">delete cachefile: $sEntry ($iAge s)<br>";
                            unlink($sEntry);
                        } else {
                            echo "<li class=\"keepfile\">keep cachefile: $sEntry ($iAge s; " . ($aTmp['mtime'] - $iTS) . " s left)</li>";
                        }
                    }
                }
            }
            echo "</ul></li></ul>";
    
            // try to delete if it should be empty
            @rmdir($sDir);
            return true;
        }
    
        // ----------------------------------------------------------------------
        /**
         * private function _getAllCacheData() - read cachedata and its meta infos
         * @since 2.0
         * @return     array  array with data, file stat
         */
        private function _getAllCacheData() {
            if (!$this->_sCacheFile)
                return false;
            $this->_aCacheInfos = array();
            if (file_exists($this->_sCacheFile)) {
                $aTmp = unserialize(file_get_contents($this->_sCacheFile));
                $this->_aCacheInfos['data'] = $aTmp['data'];
                $this->_iTtl = $aTmp['iTtl'];
                $this->_tsExpire = $aTmp['tsExpire'];
                $this->_aCacheInfos['stat'] = stat($this->_sCacheFile);
            }
            return $this->_aCacheInfos;
        }
    
        // ----------------------------------------------------------------------
        /**
         * private function _getCacheFilename() - get full filename of cachefile
         * @return     string  full filename of cachefile
         */
        private function _getCacheFilename() {
            $sMyFile=md5($this->sCacheID);
            if($this->_sCacheDirDevider && $this->_sCacheDirDevider>0){
                $sMyFile=preg_replace('/([0-9a-f]{'.$this->_sCacheDirDevider.'})/', "$1/", $sMyFile);
            }
            $sMyFile.=".".$this->_sCacheExt;
            $sMyFile=str_replace("/.", ".", $sMyFile);
            // $this->_sCacheFile = $this->_sCacheDir . "/" . $this->sModule . "/" . md5($this->sCacheID) . "." . $this->_sCacheExt;
            $this->_sCacheFile = $this->_sCacheDir . "/" . $this->sModule . "/" . $sMyFile;
            return $this->_sCacheFile;
        }
    
        /* ----------------------------------------------------------------------
          public funtions
          ---------------------------------------------------------------------- */
    
        // ----------------------------------------------------------------------
        /**
         * Cleanup cache directory; delete all cachefiles older than n seconds
         * Other filetypes in the directory won't be touched.
         * Empty directories will be deleted.
         * 
         * Only the directory of the initialized module/ app will be deleted.
         * $o=new Cache("my-app"); $o->cleanup(60*60*24*3); 
         * 
         * To delete all cachefles of all modules you can use
         * $o=new Cache(); $o->cleanup(0); 
         * 
         * @since 2.0
         * @param int $iSec max age of cachefile; older cachefiles will be deleted
         * @return     true
         */
        public function cleanup($iSec = false) {
            echo date("d.m.y - H:i:s") . " START CLEANUP $this->_sCacheDir/" . $this->sModule . ", $iSec s<br>
                    <style>
                        .dir{color:#888;}
                        .delfile{color:#900;}
                        .keepfile{color:#ccc;}
                    </style>";
            $this->_cleanupDir($this->_sCacheDir . "/" . $this->sModule, date("U") - $iSec);
            echo date("d.m.y - H:i:s") . " END CLEANUP <br>";
            return true;
        }
    
        // ----------------------------------------------------------------------
        /**
         * public function delete - delete a single cachefile if it exist
         * @return     boolean
         */
        public function delete() {
            if (!file_exists($this->_sCacheFile))
                return false;
            if (unlink($this->_sCacheFile)) {
                $this->_aCacheInfos['data'] = false;
                $this->_aCacheInfos['stat'] = false;
                return true;
            }
            return false;
        }
    
        // ----------------------------------------------------------------------
        /**
         * public function dump() - dump variables of cache class
         * @return     true
         */
        public function dump() {
            echo "
                    <hr>
                    <strong>cache->dump()<br></strong>
                    <strong>module: </strong>" . $this->sModule . "<br>
                    <strong>ID: </strong>" . $this->sCacheID . "<br>
                    <strong>filename: </strong>" . $this->_sCacheFile;
            if (!file_exists($this->_sCacheFile))
                echo " (does not exist yet)";
            echo "<br>
                    <strong>age: </strong>" . $this->getAge() . " s<br>
                    <strong>ttl: </strong>" . $this->getTtl() . " s<br>
                    <strong>expires: </strong>" . $this->getExpire() . " (" . date("d.m.y - H:i:s", $this->getExpire()) . ")<br>
                    <pre>";
            print_r($this->_aCacheInfos);
            echo "</pre><hr>";
            return true;
        }
    
        // ----------------------------------------------------------------------
        /**
         * public function getCacheAge() - get age in seconds of exisiting cachefile
         * @return     int  age in seconds; -1 if cachefiles does not exist
         */
        public function getAge() {
            if (!isset($this->_aCacheInfos['stat']))
                $this->_getAllCacheData();
            if (!isset($this->_aCacheInfos['stat']))
                return -1;
            return date("U") - $this->_aCacheInfos['stat']['mtime'];
        }
    
        // ----------------------------------------------------------------------
        /**
         * public function getExpire() - get TS of cache expiration
         * @since 2.0
         * @return     int  unix ts of cache expiration
         */
        public function getExpire() {
            return $this->_tsExpire;
        }
    
        // ----------------------------------------------------------------------
        /**
         * public function getTtl() - get TTL of cache in seconds
         * @since 2.0
         * @return     int  get ttl of cache
         */
        public function getTtl() {
            return $this->_iTtl;
        }
    
        // ----------------------------------------------------------------------
        /**
         * public function isExpired() - cache expired? To check it 
         * you must use ttl while writing data, i.e.
         * $oCache->write($sData, $iTtl);
         * @since 2.0
         * @return     bool  cache is expired?
         */
        public function isExpired() {
            if (!$this->_tsExpire)
                return true;
            return ((date("U") - $this->_tsExpire)>0);
        }
        // ----------------------------------------------------------------------
        /**
         * public function iExpired() - get time in seconds when cachefile expires
         * you must use ttl while writing data, i.e.
         * $oCache->write($sData, $iTtl);
         * @since 2.1
         * @return     int  expired time in seconds; negative if cache is not expired
         */
        public function iExpired() {
            if (!$this->_tsExpire)
                return true;
            return date("U") - $this->_tsExpire;
        }
    
        // ----------------------------------------------------------------------
        /**
         * function isNewerThanFile($sRefFile) - is the cache (still) newer than
         * a reference file? This function returns difference of mtime of both
         * files.
         * @since 2.0
         * @param   string   $sRefFile  local filename
         * @return  integer  time in sec how much the cache file is newer; negative if reference file is newer
         */
        public function isNewerThanFile($sRefFile) {
            if (!file_exists($sRefFile))
                return false;
            if (!isset($this->_aCacheInfos['stat']))
                return false;
    
            $aTmp = stat($sRefFile);
            $iTimeRef = $aTmp['mtime'];
            
            //echo $this->_sCacheFile."<br>".$this->_aCacheInfos['stat']['mtime']."<br>".$iTimeRef."<br>".($this->_aCacheInfos['stat']['mtime'] - $iTimeRef);
            return $this->_aCacheInfos['stat']['mtime'] - $iTimeRef;
        }
    
        // ----------------------------------------------------------------------
        /**
         * public function getCacheData() - read cachedata if it exist
         * @return     various  cachedata or false if cache does not exist
         */
        public function read() {
            if (!isset($this->_aCacheInfos['data']))
                $this->_getAllCacheData();
            if (!isset($this->_aCacheInfos['data']))
                return false;
            return $this->_aCacheInfos['data'];
        }
    
        // ----------------------------------------------------------------------
        /**
         * public function setData($data) - set cachedata into cache object
         * data can be any serializable type, like string, array or object
         * Remark: You additionally need to call the write() method to store data in the filesystem
         * @since 2.0
         * @param      various  $data  data to store in cache
         * @return   boolean
         */
        public function setData($data) {
            return $this->_aCacheInfos['data'] = $data;
        }
    
        // ----------------------------------------------------------------------
        /**
         * public function setTtl() - set TTL of cache in seconds
         * You need to write the cache data to ap
         * Remark: You additionally need to call the write() method to store a new ttl value with 
         * data in the filesystem
         * @since 2.0
         * @param type $iTtl  ttl value in seconds
         * @return     int  get ttl of cache
         */
        public function setTtl($iTtl) {
            return $this->_iTtl = $iTtl;
        }
    
        // ----------------------------------------------------------------------
        /**
         * public function touch() - touch cachefile if it exist
         * For cachedata with a ttl a new expiration will be set
         * @return boolean 
         */
        public function touch() {
            if (!file_exists($this->_sCacheFile))
                return false;
    
            // touch der Datei reicht nicht mehr, weil tsExpire verloren ginge
            if (!$this->_iTtl)
                $bReturn = touch($this->_sCacheFile);
            else
                $bReturn = $this->write();
    
            $this->_getAllCacheData();
    
            return $bReturn;
        }
    
        // ----------------------------------------------------------------------
        /**
         * Write data into a cache. 
         * - data can be any serializable type, like string, array or object
         * - set ttl in s (from now); optional parameter
         * @param      various  $data  data to store in cache
         * @param      int      $iTtl  time in s if content cache expires (min. 0)
         * @return     bool     success of write action
         */
        public function write($data = false, $iTtl = -1) {
            if (!$this->_sCacheFile)
                return false;
    
            $sDir = dirname($this->_sCacheFile);
            if (!is_dir($sDir))
                if (!mkdir($sDir, 0750, true))
                    die("ERROR: unable to create directory " . $sDir);
    
            if (!$data === false)
                $this->setData($data);
    
            if (!$iTtl >= 0) {
                $this->setTtl($iTtl);
            }
    
            $aTmp = array(
                'iTtl' => $this->_iTtl,
                'tsExpire' => date("U") + $this->_iTtl,
                'module' => $this->sModule,
                'cacheid' => $this->sCacheID,
                'data' => $this->_aCacheInfos['data'],
            );
            return file_put_contents($this->_sCacheFile, serialize($aTmp));
        }
    
    }
    
    // ----------------------------------------------------------------------
    ?>