<?
/**********************************************
* 파일명: class.SkyTemplate.php
* 설  명: Sky Template Runtime Library
* 버  젼: v1.4.0
* 작성자: 하늘아부지 (daddy@withsky.net)
***********************************************/
/*
    LICENSE : LGPL

    # v1.4.0
        2005-07-30
*/

define("SKY_FUNC_PREFIX", "SkyTpl_Func_");

class SkyTemplate
{
    var $root         = ".";
    var $webRoot      = "";
    var $tplRoot      = "../tpl";
    var $delimiter    = "{,}";
    var $rewriteRoot  = "{tplRoot}/_rewrite";
    var $cacheRoot    = "{tplRoot}/_cache";
    var $compile      = true;       // always, daynmic(true or anything), simple, false
    var $cache        = false;
    var $tplRewrite   = false;      // overwrite, true, false
    var $mirror       = false;
    var $divReal      = true;       // divide tag action : true, false
	var $strict       = true;
    var $debug        = false;
    var $preFilter    = "";
    var $postFilter   = "";
	var $safeMode	  = false;

    // 내부적으로 사용하는 것들
    var $version      = "v1.4.0";
    var $timeOffset   = 10400;      // version relative
    var $daddy        = null;
    var $Top          = "";
    var $Info         = array();
	var $Defined      = array();
	var $Compiled     = array();
	var $Collect      = array();
    var $Var          = null;       // deprecated
    var $error        = "";

    // 캐쉬 관련
    var $objCache     = null;
    var $cacheHandler = "DefaultCacheHandler";

    // Constructor
    function SkyTemplate($master="")
    {
        if ($master) $this->masterSetup($master);
        $this->Var = &$GLOBALS; // deprecated
    }

    // Master Setup
    function masterSetup($master)
    {
        $masterFile = dirname(__FILE__)."/SkyTpl/setup.ini.Master.php";
        if (is_file($masterFile)) {
            $masterSetup = @parse_ini_file($masterFile, true);
			if (is_array($masterSetup[$master])) {
                foreach ($masterSetup[$master] as $key=>$val) {
                    $this->$key = str_replace(array("{''}", "{\\n}"), array("\"", "\n"), $val);
                }
                return true;
            }
        }
        return $this->error("masterSetup : Invalid Master Setup (".$master.")");
    }

    // define
    function define($rsc, $file="", $data=null, $colSize=1)
    {
        static $webRoot = false;
        if (!$webRoot) $webRoot = $this->getWebRoot();

        $argc = func_num_args();
        if (!is_array($rsc)) $rsc = array($rsc=>$file);
        if ($argc > 2) $argAssign = array_slice(func_get_args(), 2);
        foreach ($rsc as $var=>$file) {
			if (isset($this->Defined[$var])) {
				continue; // 이미 define된 블럭명인 경우 무시
			}
			$this->Defined[$var] = true;
			if (!preg_match("/^(http:\\/\\/|[a-z]:\\\)/", $file)) {
                if (preg_match("/^(\\/)/", $file)) {
                    $file = (strpos($file, $this->webRoot) === 0) ? $file : $this->webRoot.$file;
                } else {
                    $file = $this->root."/".$file;
                }
            }
            $file = preg_replace("/(\\\|\\/\\.\\/|(?<!:)[\/]{2,})/", "/", $file); // clean original path
			if ($pos = strpos($file, "#")) {
                $divide = substr($file, $pos+1);
                $file = substr($file, 0, $pos);
            } else {
                $divide = "";
            }

            $this->Info[$var]['source'] = $file;
            $this->Info[$var]['target'] = $this->getCompileFile($file);
			$this->compile($var, $divide);
            if ($argc > 2) call_user_func_array(array(&$this, "assign"), array_merge(array($var), $argAssign));
        }
        $this->Top = $var;
    }

    // template file include
    function tinclude($var, $file="", $data="")
    {
		if (!$var) {
			$var = ($pos = strpos($file, "#")) ? substr($file, $pos+1) : $this->getUniqueName($file, false);
		}
		if ($data) $this->assign($var, $data);
        if (!isset($this->Info[$var]['source'])) {
            $Top = $this->Top;
			$this->define($var, $file);
            $this->Top = $Top;
        }
        $this->tprint($var);
    }

    // block data assign
    function assign($var, $data=null, $colSize=1, $stop=true)
    {
		$argc = func_num_args();
		if ($argc >= 3) {
            if ($data === true) {
                $data = array_pad(array(), $colSize, array(&$GLOBALS));
            }
            $this->Info[$var]['data'] = $data;
            $this->Info[$var]['size'] = count($data);
            $this->Info[$var]['col'] = $colSize;
            $this->Info[$var]['mod'] = ($this->Info[$var]['col']) ? $this->Info[$var]['col']*ceil($this->Info[$var]['size']/$this->Info[$var]['col'])-$this->Info[$var]['size'] : 0;
        } else {
            if ($argc == 1 || $data === true) {
                $this->Info[$var]['data'] = array(&$GLOBALS);
                $this->Info[$var]['size'] = $this->Info[$var]['col'] = 1;
            } else {
                $this->Info[$var]['data'] = $data;
                $this->Info[$var]['size'] = $this->Info[$var]['col'] = count($data);
            }
            $this->Info[$var]['mod'] = 0;
        }
        $this->Info[$var]['index'] = 0;
        $this->Info[$var]['stop'] = $stop;
        $this->Info[$var]['parsed'] = false;
    }

    // block 버퍼링, BearTemplate 호환
    function parse($var, $data=null)
    {
		$func = SKY_FUNC_PREFIX . (isset($this->Info[$var]['func']) ? $this->Info[$var]['func'] : $var);
        if (!function_exists($func)) return $this->error("call : Block Not Exists! (".$var.")");
        if (func_num_args() > 1) $this->assign($var, $data); else $this->assign($var);
        ob_start();
		call_user_func($func, &$this, &$this->Info[$var]['data'], &$this->Info[$var]['index'], $this->Info[$var]['size'], $this->Info[$var]['col'], $this->Info[$var]['mod'], $this->Info[$var]['stop']);
        $GLOBALS[$var] .= ob_get_contents();
        ob_end_clean();
        $this->Info[$var]['data'] = null;
		$this->Info[$var]['parsed'] = true;
    }

    // block 실행
    function call($var)
    {
		$func = SKY_FUNC_PREFIX . (isset($this->Info[$var]['func']) ? $this->Info[$var]['func'] : $var);
		if (!function_exists($func)) return $this->error("call : Block Not Exists! (".$var.")");
		if ($this->Info[$var]['parsed']) {
			echo $GLOBALS[$var];
            unset($GLOBALS[$var]);
            $this->Info[$var]['parsed'] = false;
        } else {
			call_user_func($func, &$this, &$this->Info[$var]['data'], &$this->Info[$var]['index'], $this->Info[$var]['size'], $this->Info[$var]['col'], $this->Info[$var]['mod'], $this->Info[$var]['stop']);
        }
		$this->Info[$var]['printed'] = true;
    }

    // 출력
    function tprint($var="")
    {
        if (!$var) $var = $this->Top;
		if ($this->cache) {
            if (!$this->objCache) $this->setCacheHandler();
            $this->objCache->tprint($var);
        } else {
            $this->call($var);
        }
		$this->Info[$var]['printed'] = true;
    }

    // 값을 반환
    function &fetch($var="")
    {
        if (!$var) $var = $this->Top;
        if (!isset($GLOBALS[$var])) {
            ob_start();
            $this->tprint($var);
            $GLOBALS[$var] = ob_get_contents();
            ob_end_clean();
        }
		$this->Info[$var]['fetched'] = true;

		return $GLOBALS[$var];
    }

	// 블럭 모으기
	function collect($collect, $func="")
	{
		if ($func) {
			$this->Collect[$collect][] = $func;
		} else {
			if ($this->Collect[$collect]) {
				foreach ($this->Collect[$collect] as $k=>$v) {
					if (!$this->assigned($v)) $this->assign($v, true);
					$this->call($v);
				}
			}
		}
	}

    // compile if invalid
    function compile($var, $divide="")
    {
        if ($divide) {
            $target = $this->tplRoot."/".substr($this->Info[$var]['target'], 0, -4)."#".$divide.".php";
            $func = $divide;
        } else {
            $target = $this->tplRoot."/".$this->Info[$var]['target'];
            $func = $this->getUniqueName($this->Info[$var]['target'], true);
        }
        $this->Info[$var]['func'] = $func;
		$flag = true;
		if ($this->compile && !in_array($this->Info[$var]['source'], $this->Compiled)) {
            if ($this->compile === "always" || !is_file($target) || ($this->compile !== "simple" && !preg_match("/^http:\/\//", $this->Info[$var]['source']) && @filemtime($this->Info[$var]['source']) + $this->timeOffset != @filemtime($target))) {
                if (!$this->daddy) {
                    include_once dirname(__FILE__)."/class.SkyTemplate_withDaddy.php";
                    $this->daddy = new SkyTemplate_withDaddy(&$this);
                }
                $flag = $this->daddy->compile($func, $this->Info[$var]['source'], $this->Info[$var]['target']);
				$this->Compiled[] = $this->Info[$var]['source'];
			}
        }
		if (!$flag) {
            return false;
		}

		// include
        if (is_file($target)) {
			include_once $target;
            return true;
        } else {
            return $this->error("compile : Compiled File Not Exists! (".$var.")", true);
        }
    }

    // webRoot 경로
    function getWebRoot()
    {
        if (!$this->webRoot) {
            $nowReal = str_replace("\\", "/", realpath("."));
            $nowVirtual = preg_replace("/^\\/~[^\/]+/", "", dirname($_SERVER['PHP_SELF']));
            $pos = strpos($nowReal, $nowVirtual);
            if (is_int($pos)) {
                $rootReal = substr($nowReal, 0, $pos);
            } else {
                $depth = count(explode("/", $nowVirtual)) - 1;
                $rootReal = implode("/", array_slice(explode("/", $nowReal), 0, -$depth));
            }
            $this->webRoot = $rootReal;
        } else if (!preg_match("/^\\//", $this->webRoot)) {
            $this->webRoot = realpath($this->webRoot);
        }
        return $this->webRoot;
    }

    // compile 파일 경로
    function getCompileFile($sfile)
    {
		if (preg_match("/^http:\\/\\//", $sfile)) {
            $tfile = str_replace("/", "%", $sfile);
        } else {
            if (preg_match("/^\\//", $sfile)) {
                $tfile = (strpos($sfile, $this->webRoot) === 0) ? str_replace(array("\\", $this->webRoot), array("/", ""), $sfile) : $sfile;
            } else {
                $sfile = ($sfile = realpath($sfile)) ? $sfile : realpath($this->root."/{$sfile}");
                $tfile = str_replace(array("\\", $this->webRoot), array("/", ""), str_replace(':',';',realpath($sfile)));
            }
            if (!$this->mirror) $tfile = str_replace("/", "%", $tfile);
        }
        return $tfile.".php";
    }

	// define 했는가?
	function defined($var)
	{
		if (isset($this->Defined[$var]) || function_exists(SKY_FUNC_PREFIX.$var)) {
			return true;
		} else {
			return false;
		}
	}

	// assgin 했는가?
	function assigned($var)
	{
		return isset($this->Info[$var]['index']);
	}

	// parse 했는가?
	function parsed($var)
	{
		return $this->Info[$var]['parsed'] ? true : false;
	}

	// tprint 했는가?
	function tprinted($var="")
	{
        if (!$var) $var = $this->Top;
		return $this->Info[$var]['printed'] ? true : false;
	}

	// fetch 했는가?
	function fetched($var="")
	{
        if (!$var) $var = $this->Top;
		return $this->Info[$var]['fetched'] ? true : false;
	}

    // compile 파일의 메인 함수명
    function getUniqueName($cfile, $isTarget=true)
    {
        if (!$isTarget) $cfile = $this->getCompileFile($cfile);
        $id = crc32($cfile);
        return ($id >= 0) ? $id : abs($id)."m";
    }

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

    // 캐쉬 핸들러
    function setCacheHandler($objCache="")
    {
        if (!$this->cache) return false;
        if (!$this->objCache) {
            if (!$objCache) $objCache = $this->cacheHandler;
            $fileobjCache = dirname(__FILE__)."/SkyTpl/class.".$objCache.".php";
            if (is_file($fileobjCache)) {
                include_once $fileobjCache;
                $this->objCache = new $objCache(&$this);
                return true;
            } else {
                return $this->error("setCacheHandler : Invalid Cache Handler!", true);
            }
        }
        return true;
    }

    // 캐쉬 설정
    function setCache()
    {
        if (!$this->cache) return false;
        if (!$this->objCache) $this->setCacheHandler();
        $args = func_get_args();
        return call_user_func_array(array(&$this->objCache, "setCache"), $args);
    }

    // 캐쉬 체크
    function isCached()
    {
        if (!$this->cache) return false;
        if (!$this->objCache) $this->setCacheHandler();
        $args = func_get_args();
        return call_user_func_array(array(&$this->objCache, "isCached"), $args);
    }

    // 캐쉬 삭제
    function clearCache()
    {
        if (!$this->cache) return false;
        if (!$this->objCache) $this->setCacheHandler();
        $args = func_get_args();
        return call_user_func_array(array(&$this->objCache, "clearCache"), $args);
    }
}
?>