<?
/**********************************************
* 파일명: class.DefaultCacheHandler.php
* 설  명: 기본 캐쉬 핸들러 for SkyTemplate
* 버  젼: v1.0.1
* 작성자: 하늘아부지 (daddy@withsky.net)
***********************************************/

class DefaultCacheHandler
{
    var $root         = ".";
    var $webRoot      = "";
    var $tplRoot      = "";
    var $cacheRoot    = "";
    var $debug        = false;

    // 내부적으로 사용하는 것들
    var $Top          = "";
    var $Caching      = array();
    var $CacheInfo    = array();
    var $CacheResult  = array();
    var $CacheArray   = array();

    // Construtor
    function DefaultCacheHandler($TPL)
    {
        if ($TPL) {
            $this->setInfo(&$TPL);
        }
    }

    // 기본 정보
    function setInfo($TPL)
    {
        if (is_object($TPL)) {
            $this->TPL        = &$TPL;
            $this->Top        = &$TPL->Top;
            $this->root       = $TPL->root;
            $this->webRoot    = $TPL->webRoot;
            $this->tplRoot    = $TPL->tplRoot;
            $this->cacheRoot  = $TPL->cacheRoot;
            $this->debug      = $TPL->debug;
        } else {
            $this->error("setInfo : No SkyTemplate Object!!", true);
            exit;
        }

        $this->cacheRoot  = str_replace("{tplRoot}", $this->tplRoot, $this->cacheRoot);
        if (!is_dir($this->cacheRoot)) {
            if (!$this->makeDir($this->cacheRoot)) {
                $this->error("setInfo : Directory Not Exists! (".$this->cacheRoot.")", true);
                exit;
            }
        }
    }

    // 출력
    function tprint($var)
    {
        // 제외 블럭 체크
        if ($this->Caching) {
            $parent = $this->Caching[count($this->Caching)-1];
            $isExcept = in_array($var, $this->CacheInfo[$parent]['except']);
        } else {
            $isExcept = false;
        }

        // 제외 블럭일 때 부모 버퍼링 중지
        if ($isExcept) {
            $this->CacheResult[$parent][] = ob_get_contents();
            $this->CacheResult[$parent][] = "#".$var;
            ob_end_flush();
        }
        if ($this->isCached($var)) {
            $this->printCache($var);
        } else if (isset($this->CacheInfo[$var]['block'])) {
            array_push($this->Caching, $var);
            ob_start();
            $this->TPL->call($var);
            $this->CacheResult[$var][] = ob_get_contents();
            ob_end_flush();
            $this->saveCache($var);
            array_pop($this->Caching);
        } else {
            $this->TPL->call($var);
        }
        // 제외 블럭일 때 부모 버퍼링 재시작
        if ($isExcept) {
            ob_start();
        }
    }

    // 캐쉬 설정
    function setCache($var, $check=0, $cacheId="", $except="")
    {
        $this->Top = $var;
        $this->CacheInfo[$var]['block'] = $var;
        $this->CacheInfo[$var]['cid'] = ($cacheId) ? $cacheId : str_replace("/", "%", $_SERVER['PHP_SELF'])."%".$var;
        $this->CacheInfo[$var]['time'] = intval($check);
        if (preg_match("/^(=|event[:\s])/i", $check, $match)) {
            $event = trim(substr($check, strlen($match[0])));
            if ($event == "") $event = "_empty";
            $this->CacheInfo[$var]['event'] = $event;
            $parentCid = "_event/".$this->CacheInfo[$var]['cid'];
            $this->CacheInfo[$var]['cid'] = $parentCid."/".$this->CacheInfo[$var]['event'];
            $this->CacheInfo[$var]['basetime'] = 0;
        } else if (!is_numeric($check) && !preg_match("/^(http:\\/\\/)/", $check)) {
            if (!preg_match("/^(\\/)/", $check)) $check = $this->root."/".$check;
            $this->CacheInfo[$var]['basetime'] = @filemtime($check);
        } else {
            $this->CacheInfo[$var]['basetime'] = 0;
        }
        $this->CacheInfo[$var]['target'] = $this->cacheRoot."/".$this->CacheInfo[$var]['cid'];
        if (!$except || !is_array($except)) $except = array($except);
        $this->CacheInfo[$var]['except'] = $except;
        // event
        if (isset($this->CacheInfo[$var]['event']) && !$this->isCached($var)) $this->clearCache(null, $parentCid);
    }

    // 캐쉬 체크
    function isCached($var="", $force=false)
    {
        if (!$var) $var = $this->Top;
        if (!$force && isset($this->CacheInfo[$var]['cached'])) {
            return $this->CacheInfo[$var]['cached'];
        } else {
            if (!isset($this->CacheInfo[$var]['block']) || !is_file($this->CacheInfo[$var]['target'])) {
                return $this->CacheInfo[$var]['cached'] = false;
            }
            if ($this->CacheInfo[$var]['time']) {
                $ctime = @filemtime($this->CacheInfo[$var]['target']);
                if (time()-$ctime > $this->CacheInfo[$var]['time']) return $this->CacheInfo[$var]['cached'] = false;
            } else if ($this->CacheInfo[$var]['basetime']) {
                $ctime = @filemtime($this->CacheInfo[$var]['target']);
                if ($ctime != $this->CacheInfo[$var]['basetime']) return $this->CacheInfo[$var]['cached'] = false;
            }
            return $this->CacheInfo[$var]['cached'] = true;
        }
    }

    // 캐쉬 출력
    function printCache($var)
    {
        if ($fp = @fopen($this->CacheInfo[$var]['target'], "rb")) {
            $this->CacheArray[$var] = unserialize(@fread($fp, filesize($this->CacheInfo[$var]['target'])));
            fclose($fp);
            $this->error("printCache : Cache Printed! (".$var.")");
            echo $this->CacheArray[$var][0];
            for ($i=1, $c=count($this->CacheArray[$var]); $i<$c; $i+=2) {
                $call = substr($this->CacheArray[$var][$i], 1);
                $this->tprint($call);
                echo $this->CacheArray[$var][$i+1];
            }
        } else {
            return $this->error("printCache : File Read Error! (".$this->CacheInfo[$var]['target'].")");
        }
    }

    // 캐쉬 저장
    function saveCache($var)
    {
        $cacheDir = dirname($this->CacheInfo[$var]['target']);
        if (!is_dir($cacheDir) && !$this->makeDir($cacheDir)) return $this->error("saveCache : Directory Not Exists! (".$cacheDir.")", true);
        if ($fp = @fopen($this->CacheInfo[$var]['target'], "wb")) {
            fwrite($fp, serialize($this->CacheResult[$var]));
            fclose($fp);
            @chmod($this->CacheInfo[$var]['target'], 0666);
            if ($this->CacheInfo[$var]['time'] == 0 && $this->CacheInfo[$var]['basetime']) @touch($this->CacheInfo[$var]['target'], $this->CacheInfo[$var]['basetime']);
            $this->error("saveCache : Cache Saved! (".$var.")");
            return true;
        } else {
            return $this->error("saveCache : File Write Error! (".$this->CacheInfo[$var]['target'].")");
        }
    }

    // 캐쉬 삭제
    function clearCache($var=null, $cacheId=null)
    {
        if ($var) {
            if (!$cacheId) $cacheId = $this->CacheInfo[$var]['cid'];
            $this->delete($this->cacheRoot."/".$cacheId);
        } else {
            if ($cacheId) {
                if (!is_array($cacheId)) $cacheId = array($cacheId);
                foreach ($cacheId as $cid) {
                    $this->delete($this->cacheRoot."/".$cid);
                }
            } else {
                $this->delete($this->cacheRoot); // clear all
            }
        }
    }

    // 디렉토리 생성
    function makeDir($path, $mod=0777)
    {
        $token = explode("/", $path);
        $dir = array_shift($token)."/";
        if (is_array($token)) {
            foreach ($token as $val) {
                if (!$val) continue;
                $dir .= $val."/";
                if (is_dir($dir)) continue;
                @mkdir($dir, $mod);
                @chmod($dir, $mod);
                if (!is_dir($dir)) return false;
            }
        }
        return true;
    }

    // 파일, 디렉토리 삭제
    function delete($path, $delDir=false)
    {
        if (is_dir($path)) {
            $d = dir($path);
            while ($el = $d->read()) {
                if (preg_match("/^[.]{1,2}$/", $el)) continue;
                $elPath = $path."/".$el;
                $this->delete($elPath, true); // 하위는 디렉토리까지 삭제
            }
            return ($delDir) ? @rmdir($path) : true;
        } else {
            return @unlink($path);
        }
    }

    // 에러
    function error($msg, $debug=false)
    {
        if ($debug || $this->debug) echo "\n<font color='purple'><b>CacheHandler</b> :: ".$msg."</font><br>\n";
        return false;
    }
}
?>