dedetemplate.class.php源码分析

来源:互联网 发布:玻璃体混浊 知乎 编辑:程序博客网 时间:2024/06/06 02:53

该文件同dedetag.class.php有点类似,标签编译的机制不同,模板解析类似。

下面直接贴源码:

<?php   if(!defined('DEDEINC')) exit("Request Error!");/** * 模板引擎文件 * * @version        $Id: dedetemplate.class.php 3 15:44 2010年7月6日Z tianya $ * @package        DedeCMS.Libraries * @copyright      Copyright (c) 2007 - 2010, DesDev, Inc. * @license        http://help.dedecms.com/usersguide/license.html * @link           http://www.dedecms.com *//** *  这个函数用于定义任意名称的块使用的接口 *  返回值应是一个二维数组 *  块调用对应的文件为 include/taglib/plus_blockname.php *  ---------------------------------------------------------------- *  由于标记一般存在默认属性,在编写块函数时,应该在块函数中进行给属性赋省缺值处理,如: *  $attlist = "titlelen=30,catalogid=0,modelid=0,flag=,addon=,row=8,ids=,orderby=id,orderway=desc,limit=,subday=0"; *  给属性赋省缺值 *  FillAtts($atts,$attlist); *  处理属性中使用的系统变量 var、global、field 类型(不支持多维数组) *  FillFields($atts,$fields,$refObj); * * @access    public * @param     array  $atts  属性 * @param     object  $refObj  所属对象 * @param     array  $fields  字段 * @return    string */function MakePublicTag($atts=array(),$refObj='',$fields=array()){    $atts['tagname'] = preg_replace("/[0-9]{1,}$/", "", $atts['tagname']);    $plusfile = DEDEINC.'/tpllib/plus_'.$atts['tagname'].'.php';    if(!file_exists($plusfile))    {        if(isset($atts['rstype']) && $atts['rstype']=='string')        {            return '';        }        else        {            return array();        }    }    else    {        include_once($plusfile);        $func = 'plus_'.$atts['tagname'];        return $func($atts, $refObj, $fields);    }}/** *  设定属性的默认值 * * @access    public * @param     array    $atts  属性 * @param     array    $attlist  属性列表 * @return    void */function FillAtts(&$atts, $attlist){    $attlists = explode(',', $attlist);    foreach($attlists as $att)    {        list($k, $v)=explode('=', $att);        if(!isset($atts[$k]))        {            $atts[$k] = $v;        }    }}/** *  把上级的fields传递给atts * * @access    public * @param     array  $atts  属性 * @param     object  $refObj  所属对象 * @param     array  $fields  字段 * @return    string */function FillFields(&$atts, &$refObj, &$fields){    global $_vars;    foreach($atts as $k=>$v)    {        if(preg_match('/^field\./i',$v))        {            $key = preg_replace('/^field\./i', '', $v);            if( isset($fields[$key]) )            {                $atts[$k] = $fields[$key];            }        }        else if(preg_match('/^var\./i', $v))        {            $key = preg_replace('/^var\./i', '', $v);            if( isset($_vars[$key]) )            {                $atts[$k] = $_vars[$key];            }        }        else if(preg_match('/^global\./i', $v))        {            $key = preg_replace('/^global\./i', '', $v);            if( isset($GLOBALS[$key]) )            {                $atts[$k] = $GLOBALS[$key];            }        }    }}/** * class Tag 标记的数据结构描述 * function C__Tag(); * * @package          Tag * @subpackage       DedeCMS.Libraries * @link             http://www.dedecms.com */class Tag{    var $isCompiler=FALSE;   //标记是否已被替代,供解析器使用    var $tagName="";         //标记名称    var $innerText="";       //标记之间的文本    var $startPos=0;         //标记起始位置    var $endPos=0;           //标记结束位置    var $cAtt="";            //标记属性描述,即是class TagAttribute    var $tagValue="";        //标记的值    var $tagID = 0;    /**     *  获取标记的名称和值     *     * @access    public     * @return    string     */    function GetName()    {        return strtolower($this->tagName);    }    function GetValue()    {        return $this->tagValue;    }    function IsAtt($str)    {        return $this->cAtt->IsAttribute($str);    }    function GetAtt($str)    {        return $this->cAtt->GetAtt($str);    }    /**     *  获取底层模板     *     * @return    string     */    function GetinnerText()    {        return $this->innerText;    }}/** * 模板解析器 * function C__DedeTemplate * * @package          DedeTemplate * @subpackage       DedeCMS.Libraries * @link             http://www.dedecms.com */class DedeTemplate{    var $tagMaxLen = 64;    var $charToLow = TRUE;    var $isCache = TRUE;    var $isParse = FALSE;    var $isCompiler = TRUE;    var $templateDir = '';    var $tempMkTime = 0;    var $cacheFile = '';    var $configFile = '';    var $buildFile = '';    var $refDir = '';    var $cacheDir = '';    var $templateFile = '';    var $sourceString = '';    var $cTags = array();    //var $definedVars = array();    var $count = -1;    var $loopNum = 0;    var $refObj = '';    var $makeLoop = 0;    var $tagStartWord =  '{dede:';    var $fullTagEndWord =  '{/dede:';    var $sTagEndWord = '/}';    var $tagEndWord = '}';    var $tpCfgs = array();    /**     *  析构函数     *     * @access    public     * @param     string    $templatedir  模板目录     * @param     string    $refDir  所属目录     * @return    void     */    function __construct($templatedir='',$refDir='')    {        //$definedVars[] = 'var';        //缓存目录        if($templatedir=='')        {            $this->templateDir = DEDEROOT.'/templates';        }        else        {            $this->templateDir = $templatedir;        }        //模板include目录        if($refDir=='')        {            if(isset($GLOBALS['cfg_df_style']))            {                $this->refDir = $this->templateDir.'/'.$GLOBALS['cfg_df_style'].'/';    // 支持配置风格            }            else            {                $this->refDir = $this->templateDir;            }        }        $this->cacheDir = DEDEROOT.$GLOBALS['cfg_tplcache_dir'];    }    //构造函数,兼容PHP4    function DedeTemplate($templatedir='',$refDir='')    {        $this->__construct($templatedir,$refDir);    }    /**     *  设定本类自身实例的类引用和使用本类的类实例(如果在类中使用本模板引擎,后一参数一般为$this)     *     * @access    public     * @param     object    $refObj   实例对象     * @return    string     */    function SetObject(&$refObj)    {        $this->refObj = $refObj;    }    /**     *  设定Var的键值对     *     * @access    public     * @param     string  $k  键     * @param     string  $v  值     * @return    string     */    function SetVar($k, $v)    {        $GLOBALS['_vars'][$k] = $v;    }    /**     *  设定Var的键值对     *     * @access    public     * @param     string  $k  键     * @param     string  $v  值     * @return    string     */    function Assign($k, $v)    {        $GLOBALS['_vars'][$k] = $v;    }    /**     *  设定数组     *     * @access    public     * @param     string  $k  键     * @param     string  $v  值     * @return    string     */    function SetArray($k, $v)    {        $GLOBALS[$k] = $v;    }    /**     *  设置标记风格     *     * @access    public     * @param     string   $ts  标签开始标记     * @param     string   $ftend  标签结束标记     * @param     string   $stend  标签尾部结束标记     * @param     string   $tend  结束标记     * @return    void     */    function SetTagStyle($ts='{dede:',$ftend='{/dede:',$stend='/}',$tend='}')    {        $this->tagStartWord =  $ts;        $this->fullTagEndWord =  $ftend;        $this->sTagEndWord = $stend;        $this->tagEndWord = $tend;    }    /**     *  获得模板设定的config值     *     * @access    public     * @param     string   $k  键名     * @return    string     */    function GetConfig($k)    {        return (isset($this->tpCfgs[$k]) ? $this->tpCfgs[$k] : '');    }    /**     *  设定模板文件     *     * @access    public     * @param     string  $tmpfile  模板文件     * @return    void     */    function LoadTemplate($tmpfile)    {        if(!file_exists($tmpfile))        {            echo " Template Not Found! ";            exit();        }        $tmpfile = preg_replace("/[\\/]{1,}/", "/", $tmpfile);      // 多个/,转为一个/        $tmpfiles = explode('/',$tmpfile);        $tmpfileOnlyName = preg_replace("/(.*)\//", "", $tmpfile);  // 去除文件路径,得到文件名        $this->templateFile = $tmpfile;     // 将记载模板文件原始路径,记录到 $this->templateFile        $this->refDir = '';        for($i=0; $i < count($tmpfiles)-1; $i++)    // 专门-1,去除了数组中最后的文件名,仅处理之前的路径        {            $this->refDir .= $tmpfiles[$i].'/';     // 得到 'refDir' - 模板include目录(方法很多,有点麻烦感觉)        }        // 模板缓存目录        if(!is_dir($this->cacheDir))        {            $this->cacheDir = $this->refDir;        }        if($this->cacheDir!='')        {            $this->cacheDir = $this->cacheDir.'/';        }        if(isset($GLOBALS['_DEBUG_CACHE']))        {            $this->cacheDir = $this->refDir;        }        // 得到缓存文件名(.inc)和配置文件名(_config.inc)        $this->cacheFile = $this->cacheDir.preg_replace("/\.(wml|html|htm|php)$/", "_".$this->GetEncodeStr($tmpfile).'.inc', $tmpfileOnlyName);        $this->configFile = $this->cacheDir.preg_replace("/\.(wml|html|htm|php)$/", "_".$this->GetEncodeStr($tmpfile).'_config.inc', $tmpfileOnlyName);        //不开启缓存、当缓存文件不存在、及模板为更新的文件的时候才载入模板并进行解析        if($this->isCache==FALSE || !file_exists($this->cacheFile) || filemtime($this->templateFile) > filemtime($this->cacheFile))        {            $t1 = ExecTime(); //debug            $fp = fopen($this->templateFile,'r');            $this->sourceString = fread($fp,filesize($this->templateFile));     // 缓存模板内容,保存到 $this->sourceString            fclose($fp);            $this->ParseTemplate();     // 解析模板字符串            //模板解析时间            //echo ExecTime() - $t1;        }        else        {            /*                缓存文件存在,我们可以获取到缓存文件(html和php混编)                这里需要引入:                    $this->tpCgfs 数组,应该是 混编的php中需要该变量                正如下面代码解释的:                    如果我们使用了 'config' 的标签(用来自定义变量),得将我们针对这个模板自定义的变量也保存起来。这样调用缓存时,这些变量的值,我们就需要从该变量配置文件中的到             */            //如果存在config文件,则载入此文件,该文件用于保存 $this->tpCfgs的内容,以供扩展用途            //模板中用{tag:config name='' value=''/}来设定该值            if(file_exists($this->configFile))            {                include($this->configFile);            }        }    }    /**     *  载入模板字符串     *     * @access    public     * @param     string  $str  模板字符串     * @return    void     */    function LoadString($str='')    {        $this->sourceString = $str;        $hashcode = md5($this->sourceString);        // 调用模板字符串,得到缓存文件名(.inc)和配置文件名(_config.inc),与从模板文件不同,生成的文件前缀是 'string_'        $this->cacheFile = $this->cacheDir."/string_".$hashcode.".inc";        $this->configFile = $this->cacheDir."/string_".$hashcode."_config.inc";        $this->ParseTemplate();     // 解析模板字符串    }    /**     *  调用此函数include一个编译后的PHP文件,通常是在最后一个步骤才调用本文件     *     * @access    public     * @return    string     */    function CacheFile()    {        global $gtmpfile;        $this->WriteCache();        return $this->cacheFile;    }    /**     *  显示内容,由于函数中会重新解压一次$GLOBALS变量,所以在动态页中,应该尽量少用本方法,     *  取代之是直接在程序中 include $tpl->CacheFile(),不过include $tpl->CacheFile()这种方式不能在类或函数内使用     *     * @access    public     * @param     string     * @return    void     */    function Display()    {        global $gtmpfile;        extract($GLOBALS, EXTR_SKIP);        $this->WriteCache();        include $this->cacheFile;    }    /**     *  保存运行后的程序为文件     *     * @access    public     * @param     string  $savefile  保存到的文件目录     * @return    void     */    function SaveTo($savefile)    {        extract($GLOBALS, EXTR_SKIP);        $this->WriteCache();            // 写入缓存文件(解析完毕后生成的缓存文件,是html和php混编)        // 通过ob输出控制函数,生成纯html文件,保存到指定的文件里        ob_start();        include $this->cacheFile;       // 引入缓存文件        $okstr = ob_get_contents();        ob_end_clean();        $fp = @fopen($savefile,"w") or die(" Tag Engine Create File FALSE! ");        fwrite($fp,$okstr);        fclose($fp);    }    // ------------------------------------------------------------------------    /**     * CheckDisabledFunctions     *     * COMMENT : CheckDisabledFunctions : 检查是否存在禁止的函数     *     * @access    public     * @param    string     * @return    bool     */    function CheckDisabledFunctions($str,&$errmsg='')    {        global $cfg_disable_funs;        $cfg_disable_funs = isset($cfg_disable_funs)? $cfg_disable_funs : 'phpinfo,eval,exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source,file_put_contents,fsockopen,fopen,fwrite';        // 模板引擎增加disable_functions        if (!defined('DEDEDISFUN')) {            $tokens = token_get_all_nl($str);            $disabled_functions = explode(',', $cfg_disable_funs);            foreach ($tokens as $token)            {                if (is_array($token))                {                    if ($token[0] = '306' && in_array($token[1], $disabled_functions))                    {                       $errmsg = 'DedeCMS Error:function disabled "'.$token[1].'" <a href="http://help.dedecms.com/install-use/apply/2013/0711/2324.html" target="_blank">more...</a>';                       return FALSE;                    }                }            }        }        return TRUE;    }    /**     *  解析模板并写缓存文件     *     * @access    public     * @param     string  $ctype  缓存类型     * @return    void     */    function WriteCache($ctype='all')    {        if(!file_exists($this->cacheFile) || $this->isCache==FALSE || ( file_exists($this->templateFile) && (filemtime($this->templateFile) > filemtime($this->cacheFile)) ) )        {                if(!$this->isParse)     // 一旦调用过 'ParseTemplate()',就会设置为true,表示已经解析过模板                {                    $this->ParseTemplate();     // 解析模板                }                /*                    将模板解析后的字符串,写入缓存文件(.inc)                 */                 $fp = fopen($this->cacheFile,'w') or dir("Write Cache File Error! ");                flock($fp,3);                $result = trim($this->GetResult());     // 获取模板解析后的字符串(html和php混编)                $errmsg = '';                //var_dump($result);exit();                if (!$this->CheckDisabledFunctions($result, $errmsg))       // 检查是否存在禁止的函数                {                    fclose($fp);                    @unlink($this->cacheFile);                    die($errmsg);                }                fwrite($fp,$result);                fclose($fp);                /*                    将 $this->tpCfgs 数组,写入到 缓存配置文件(_config.inc)                    如果我们使用了 'config' 的标签(用来自定义变量),得将我们针对这个模板自定义的变量也保存起来。这样调用缓存时,这些变量的值,我们就需要从该变量配置文件中的到                 */                if(count($this->tpCfgs) > 0)                {                    $fp = fopen($this->configFile,'w') or dir("Write Config File Error! ");                    flock($fp,3);                    fwrite($fp,'<'.'?php'."\r\n");                    foreach($this->tpCfgs as $k=>$v)                    {                        $v = str_replace("\"","\\\"",$v);                        $v = str_replace("\$","\\\$",$v);                        fwrite($fp,"\$this->tpCfgs['$k']=\"$v\";\r\n");                    }                    fwrite($fp,'?'.'>');                    fclose($fp);                }        }        /*        if(!file_exists($this->cacheFile) || $this->isCache==FALSE        || ( file_exists($this->templateFile) && (filemtime($this->templateFile) > filemtime($this->cacheFile)) ) )        {            if($ctype!='config')            {                if(!$this->isParse)                {                    $this->ParseTemplate();                }                $fp = fopen($this->cacheFile,'w') or dir("Write Cache File Error! ");                flock($fp,3);                fwrite($fp,trim($this->GetResult()));                fclose($fp);            }            else            {                if(count($this->tpCfgs) > 0)                {                    $fp = fopen($this->configFile,'w') or dir("Write Config File Error! ");                    flock($fp,3);                    fwrite($fp,'<'.'?php'."\r\n");                    foreach($this->tpCfgs as $k=>$v)                    {                        $v = str_replace("\"","\\\"",$v);                        $v = str_replace("\$","\\\$",$v);                        fwrite($fp,"\$this->tpCfgs['$k']=\"$v\";\r\n");                    }                    fwrite($fp,'?'.'>');                    fclose($fp);                }            }        }        else        {            if($ctype=='config' && count($this->tpCfgs) > 0 )            {                $fp = fopen($this->configFile,'w') or dir("Write Config File Error! ");                flock($fp,3);                fwrite($fp,'<'.'?php'."\r\n");                foreach($this->tpCfgs as $k=>$v)                {                    $v = str_replace("\"","\\\"",$v);                    $v = str_replace("\$","\\\$",$v);                    fwrite($fp,"\$this->tpCfgs['$k']=\"$v\";\r\n");                }                fwrite($fp,'?'.'>');                fclose($fp);            }        }        */    }    /**     *  获得模板文件名的md5字符串     *     * @access    public     * @param     string  $tmpfile  模板文件     * @return    string     */    function GetEncodeStr($tmpfile)    {        //$tmpfiles = explode('/',$tmpfile);        $encodeStr = substr(md5($tmpfile),0,24);        return $encodeStr;    }    /**     *  解析模板     *     * @access    public     * @return    void     */    function ParseTemplate()    {        // 最多可调用5次,没看到在哪里 ++ 了        if($this->makeLoop > 5)        {            return ;        }        $this->count = -1;        $this->cTags = array();        $this->isParse = TRUE;        $sPos = 0;        $ePos = 0;        $tagStartWord =  $this->tagStartWord;        $fullTagEndWord =  $this->fullTagEndWord;        $sTagEndWord = $this->sTagEndWord;        $tagEndWord = $this->tagEndWord;        $startWordLen = strlen($tagStartWord);        $sourceLen = strlen($this->sourceString);        if( $sourceLen <= ($startWordLen + 3) )        {            return;        }        $cAtt = new TagAttributeParse();        $cAtt->CharToLow = TRUE;        //遍历模板字符串,请取标记及其属性信息        $t = 0;        $preTag = '';        $tswLen = strlen($tagStartWord);        for($i=0; $i<$sourceLen; $i++)        {            $ttagName = '';            //如果不进行此判断,将无法识别相连的两个标记            if($i-1>=0)            {                $ss = $i-1;            }            else            {                $ss = 0;            }            $tagPos = strpos($this->sourceString,$tagStartWord,$ss);            //判断后面是否还有模板标记            if($tagPos==0 && ($sourceLen-$i < $tswLen            || substr($this->sourceString,$i,$tswLen)!=$tagStartWord ))            {                $tagPos = -1;                break;            }            //获取TAG基本信息            for($j = $tagPos+$startWordLen; $j < $tagPos+$startWordLen+$this->tagMaxLen; $j++)            {                if(preg_match("/[ >\/\r\n\t\}\.]/", $this->sourceString[$j]))                {                    break;                }                else                {                    $ttagName .= $this->sourceString[$j];                }            }            if($ttagName!='')            {                $i = $tagPos + $startWordLen;                $endPos = -1;                //判断  '/}' '{tag:下一标记开始' '{/tag:标记结束' 谁最靠近                $fullTagEndWordThis = $fullTagEndWord.$ttagName.$tagEndWord;                $e1 = strpos($this->sourceString, $sTagEndWord, $i);                $e2 = strpos($this->sourceString, $tagStartWord, $i);                $e3 = strpos($this->sourceString, $fullTagEndWordThis, $i);                $e1 = trim($e1); $e2 = trim($e2); $e3 = trim($e3);                $e1 = ($e1=='' ? '-1' : $e1);                $e2 = ($e2=='' ? '-1' : $e2);                $e3 = ($e3=='' ? '-1' : $e3);                if($e3==-1)                {                    //不存在'{/tag:标记'                    $endPos = $e1;                    $elen = $endPos + strlen($sTagEndWord);                }                else if($e1==-1)                {                    //不存在 '/}'                    $endPos = $e3;                    $elen = $endPos + strlen($fullTagEndWordThis);                }                //同时存在 '/}' 和 '{/tag:标记'                else                {                    //如果 '/}' 比 '{tag:'、'{/tag:标记' 都要靠近,则认为结束标志是 '/}',否则结束标志为 '{/tag:标记'                    if($e1 < $e2 &&  $e1 < $e3 )                    {                        $endPos = $e1;                        $elen = $endPos + strlen($sTagEndWord);                    }                    else                    {                        $endPos = $e3;                        $elen = $endPos + strlen($fullTagEndWordThis);                    }                }                //如果找不到结束标记,则认为这个标记存在错误                if($endPos==-1)                {                    echo "Tpl Character postion $tagPos, '$ttagName' Error!<br />\r\n";                    break;                }                $i = $elen;                //分析所找到的标记位置等信息                $attStr = '';                $innerText = '';                $startInner = 0;                for($j = $tagPos+$startWordLen; $j < $endPos; $j++)                {                    if($startInner==0)                    {                        if($this->sourceString[$j]==$tagEndWord)                        {                            $startInner=1; continue;                         }                        else                        {                            $attStr .= $this->sourceString[$j];                        }                    }                    else                    {                        $innerText .= $this->sourceString[$j];                    }                }                // 上面解析得到的标签名,转小写                $ttagName = strtolower($ttagName);                //if、php标记,把整个属性串视为属性                if(preg_match("/^if[0-9]{0,}$/", $ttagName))    // 标签名是 'if+多个数字'                {                    $cAtt->cAttributes = new TagAttribute();                    $cAtt->cAttributes->count = 2;                    $cAtt->cAttributes->items['tagname'] = $ttagName;                    $cAtt->cAttributes->items['condition'] = preg_replace("/^if[0-9]{0,}[\r\n\t ]/", "", $attStr);                    $innerText = preg_replace("/\{else\}/i", '<'."?php\r\n}\r\nelse{\r\n".'?'.'>', $innerText);                }                else if($ttagName=='php')       // 标签名是 'php'                {                    $cAtt->cAttributes = new TagAttribute();                    $cAtt->cAttributes->count = 2;                    $cAtt->cAttributes->items['tagname'] = $ttagName;                    $cAtt->cAttributes->items['code'] = '<'."?php\r\n".trim(preg_replace("/^php[0-9]{0,}[\r\n\t ]/",                                                          "",$attStr))."\r\n?".'>';                }                else                {                    //普通标记,解释属性                    $cAtt->SetSource($attStr);                }                /*                    解析后的所有标签,存储到 cTags 数组中                 */                $this->count++;                $cTag = new Tag();                $cTag->tagName = $ttagName;                $cTag->startPos = $tagPos;                $cTag->endPos = $i;                $cTag->cAtt = $cAtt->cAttributes;                $cTag->isCompiler = FALSE;                $cTag->tagID = $this->count;                $cTag->innerText = $innerText;                $this->cTags[$this->count] = $cTag;            }            else            {                $i = $tagPos+$startWordLen;                break;            }        }//结束遍历模板字符串        if( $this->count > -1 && $this->isCompiler )        {            $this->CompilerAll();        }    }    /**     *  把模板标记转换为PHP代码     *     * @access    public     * @return    void     */    function CompilerAll()    {        // 记录CompilerAll()的调用次数,防止可能出现的死循环        $this->loopNum++;        if($this->loopNum > 10)        {            return; //限制最大递归深度为 10 以防止因标记出错等可能性导致死循环        }        $ResultString = '';        $nextTagEnd = 0;        // 循环经 'parseTemplate()' 解析的标签数组(cTags)        for($i=0; isset($this->cTags[$i]); $i++)        {            $ResultString .= substr($this->sourceString, $nextTagEnd, $this->cTags[$i]->startPos - $nextTagEnd);            // 1.这里是非常关键的一个函数!对解析后的标签,进行处理、转换,生成 '动态php代码',标签解析完毕            $ResultString .= $this->CompilerOneTag($this->cTags[$i]);            $nextTagEnd = $this->cTags[$i]->endPos;        }        $slen = strlen($this->sourceString);        if($slen > $nextTagEnd)        {            $ResultString .= substr($this->sourceString,$nextTagEnd,$slen-$nextTagEnd);        }        // 2.将标签替换后的模板字符串,又赋值给 $this->sourceString(要解析的模板字符串) ----- 当多次解析后,标签全部解析完毕,它也代表结果字符串        $this->sourceString = $ResultString;        // 3.再次调用 '模板解析',针对的是可能内嵌的标签,保证所有模板的标签都被解析        $this->ParseTemplate();    }    /**     *  获得最终结果     *     * @access    public     * @return    string     */    function GetResult()    {        if(!$this->isParse)        {            $this->ParseTemplate();        }        $addset = '';        $addset .= '<'.'?php'."\r\n".'if(!isset($GLOBALS[\'_vars\'])) $GLOBALS[\'_vars\'] = array(); '."\r\n".'$fields = array();'."\r\n".'?'.'>';        return preg_replace("/\?".">[ \r\n\t]{0,}<"."\?php/", "", $addset.$this->sourceString);     // 得到模板解析后的字符串    }    /**     *  编译单个标记     *     * @access    public     * @param     string  $cTag  标签     * @return    string     */    function CompilerOneTag(&$cTag)    {        $cTag->isCompiler = TRUE;        $tagname = $cTag->tagName;        $varname = $cTag->GetAtt('name');        $rsvalue = "";        //用于在模板中设置一个变量以提供作扩展用途        //此变量直接提交到 this->tpCfgs 中,并会生成与模板对应的缓存文件 ***_config.php 文件        if( $tagname == 'config' )        {            $this->tpCfgs[$varname] = $cTag->GetAtt('value');           }        else if( $tagname == 'global' )        {            $cTag->tagValue = $this->CompilerArrayVar('global',$varname);            if( $cTag->GetAtt('function') != '' )            {                $cTag->tagValue = $this->CompilerFunction($cTag->GetAtt('function'), $cTag->tagValue);            }            $cTag->tagValue = '<'.'?php echo '.$cTag->tagValue.'; ?'.'>';        }        else if( $tagname == 'cfg' )        {            $cTag->tagValue = '$GLOBALS[\'cfg_'.$varname.'\']'; //处理函数            if( $cTag->GetAtt('function')!='' )            {                $cTag->tagValue = $this->CompilerFunction($cTag->GetAtt('function'), $cTag->tagValue);            }            $cTag->tagValue = '<'.'?php echo '.$cTag->tagValue.'; ?'.'>';        }        else if( $tagname == 'name' )        {            $cTag->tagValue = '$'.$varname; //处理函数            if( $cTag->GetAtt('function')!='' )            {                $cTag->tagValue = $this->CompilerFunction($cTag->GetAtt('function'), $cTag->tagValue);            }            $cTag->tagValue = '<'.'?php echo '.$cTag->tagValue.'; ?'.'>';        }        else if( $tagname == 'object' )        {            list($_obs,$_em) = explode('->',$varname);            $cTag->tagValue = "\$GLOBALS['{$_obs}']->{$_em}"; //处理函数            if( $cTag->GetAtt('function')!='' )            {                $cTag->tagValue = $this->CompilerFunction($cTag->GetAtt('function'), $cTag->tagValue);            }            $cTag->tagValue = '<'.'?php echo '.$cTag->tagValue.'; ?'.'>';        }        else if($tagname == 'var')        {            $cTag->tagValue = $this->CompilerArrayVar('var', $varname);            if( $cTag->GetAtt('function')!='' )            {                $cTag->tagValue = $this->CompilerFunction($cTag->GetAtt('function'), $cTag->tagValue);            }            // 增加默认空值处理            if ($cTag->GetAtt('default')!='')            {                $cTag->tagValue = '<'.'?php echo empty('.$cTag->tagValue.')? \''.addslashes($cTag->GetAtt('default')).'\':'.$cTag->tagValue.'; ?'.'>';            } else {                $cTag->tagValue = '<'.'?php echo '.$cTag->tagValue.'; ?'.'>';            }        }        else if($tagname == 'field')        {            $cTag->tagValue = '$fields[\''.$varname.'\']';            if( $cTag->GetAtt('function')!='' )            {                $cTag->tagValue = $this->CompilerFunction($cTag->GetAtt('function'), $cTag->tagValue);            }            $cTag->tagValue = '<'.'?php echo '.$cTag->tagValue.'; ?'.'>';        }        else if( preg_match("/^key[0-9]{0,}/", $tagname) || preg_match("/^value[0-9]{0,}/", $tagname))        {            if( preg_match("/^value[0-9]{0,}/", $tagname) && $varname!='' )            {                $cTag->tagValue = '<'.'?php echo '.$this->CompilerArrayVar($tagname,$varname).'; ?'.'>';            }            else            {                $cTag->tagValue = '<'.'?php echo $'.$tagname.'; ?'.'>';            }        }        else if( preg_match("/^if[0-9]{0,}$/", $tagname) )        {            $cTag->tagValue = $this->CompilerIf($cTag);        }        else if( $tagname=='echo' )        {            if(trim($cTag->GetInnerText())=='') $cTag->tagValue = $cTag->GetAtt('code');            else            {                $cTag->tagValue =  '<'."?php echo $".trim($cTag->GetInnerText())." ;?".'>';            }        }        else if( $tagname=='php' )        {            if(trim($cTag->GetInnerText())=='') $cTag->tagValue = $cTag->GetAtt('code');            else            {                $cTag->tagValue =  '<'."?php\r\n".trim($cTag->GetInnerText())."\r\n?".'>';            }        }        //遍历数组        else if( preg_match("/^array[0-9]{0,}/",$tagname) )        {            $kk = '$key';            $vv = '$value';            if($cTag->GetAtt('key')!='')            {                $kk = '$key'.$cTag->GetAtt('key');            }            if($cTag->GetAtt('value')!='')            {                $vv = '$value'.$cTag->GetAtt('value');            }            $addvar = '';            if(!preg_match("/\(/",$varname))            {                $varname = '$GLOBALS[\''.$varname.'\']';            }            else            {                $addvar = "\r\n".'$myarrs = $pageClass->'.$varname.";\r\n";                $varname = ' $myarrs ';            }            $rsvalue = '<'.'?php '.$addvar.' foreach('.$varname.' as '.$kk.'=>'.$vv.'){ ?'.">";            $rsvalue .= $cTag->GetInnerText();            $rsvalue .= '<'.'?php  }    ?'.">\r\n";            $cTag->tagValue = $rsvalue;        }        //include 文件        else if($tagname == 'include')        {            $filename = $cTag->GetAtt('file');            if($filename=='')            {                $filename = $cTag->GetAtt('filename');            }            $cTag->tagValue = $this->CompilerInclude($filename, FALSE);            if($cTag->tagValue==0) $cTag->tagValue = '';            $cTag->tagValue = '<'.'?php include $this->CompilerInclude("'.$filename.'");'."\r\n".' ?'.'>';        }        else if( $tagname=='label' )        {            $bindFunc = $cTag->GetAtt('bind');            $rsvalue = 'echo '.$bindFunc.";\r\n";            $rsvalue = '<'.'?php  '.$rsvalue.'  ?'.">\r\n";            $cTag->tagValue = $rsvalue;        }        else if( $tagname=='datalist' )        {            //生成属性数组            foreach($cTag->cAtt->items as $k=>$v)            {                $v = $this->TrimAtts($v);                $rsvalue .= '$atts[\''.$k.'\'] = \''.str_replace("'","\\'",$v)."';\r\n";            }            $rsvalue = '<'.'?php'."\r\n".'$atts = array();'."\r\n".$rsvalue;            // 引用的类中,定义了 'GetArcList()'            $rsvalue .= '$blockValue = $this->refObj->GetArcList($atts,$this->refObj,$fields); '."\r\n";            $rsvalue .= 'if(is_array($blockValue)){'."\r\n";            $rsvalue .= 'foreach( $blockValue as $key=>$fields )'."\r\n{\r\n".'?'.">";            $rsvalue .= $cTag->GetInnerText();            $rsvalue .= '<'.'?php'."\r\n}\r\n}".'?'.'>';            $cTag->tagValue = $rsvalue;        }        else if( $tagname=='pagelist' )        {            //生成属性数组            foreach($cTag->cAtt->items as $k=>$v)            {                $v = $this->TrimAtts($v);                $rsvalue .= '$atts[\''.$k.'\'] = \''.str_replace("'","\\'",$v)."';\r\n";            }            $rsvalue = '<'.'?php'."\r\n".'$atts = array();'."\r\n".$rsvalue;            // 引用的类中,定义了 'GetPageList()'            $rsvalue .= ' echo $this->refObj->GetPageList($atts,$this->refObj,$fields); '."\r\n".'?'.">\r\n";            $cTag->tagValue = $rsvalue;        }        else        {            $bindFunc = $cTag->GetAtt('bind');            $bindType = $cTag->GetAtt('bindtype');            $rstype =  ($cTag->GetAtt('resulttype')=='' ? $cTag->GetAtt('rstype') : $cTag->GetAtt('resulttype') );            $rstype = strtolower($rstype);            //生成属性数组            foreach($cTag->cAtt->items as $k=>$v)            {                if(preg_match("/(bind|bindtype)/i",$k))                {                    continue;                }                $v = $this->TrimAtts($v);                $rsvalue .= '$atts[\''.$k.'\'] = \''.str_replace("'","\\'",$v)."';\r\n";            }            $rsvalue = '<'.'?php'."\r\n".'$atts = array();'."\r\n".$rsvalue;            //绑定到默认函数还是指定函数(datasource属性指定)            if($bindFunc=='')            {                // 这里也需要注意下:会调用 'include/tpllib/plus_标签名' 文件,处理标签                $rsvalue .= '$blockValue = MakePublicTag($atts,$this->refObj,$fields); '."\r\n";            }            else            {                // 引用的类中,定义了 '绑定函数的方法-$bindFunc'                //自定义绑定函数如果不指定 bindtype,则指向$this->refObj->绑定函数名,即是默认指向被引用的类对象                if($bindType=='') $rsvalue .= '$blockValue = $this->refObj->'.$bindFunc.'($atts,$this->refObj,$fields); '."\r\n";                else $rsvalue .= '$blockValue = '.$bindFunc.'($atts,$this->refObj,$fields); '."\r\n";            }            //返回结果类型:默认为 array 是一个二维数组,string 是字符串            if($rstype=='string')            {                $rsvalue .= 'echo $blockValue;'."\r\n".'?'.">";            }            else            {                $rsvalue .= 'if(is_array($blockValue) && count($blockValue) > 0){'."\r\n";                $rsvalue .= 'foreach( $blockValue as $key=>$fields )'."\r\n{\r\n".'?'.">";                $rsvalue .= $cTag->GetInnerText();                $rsvalue .= '<'.'?php'."\r\n}\r\n}\r\n".'?'.'>';            }            $cTag->tagValue = $rsvalue;        }        return $cTag->tagValue;    }    /**     *  编译可能为数组的变量     *     * @access    public     * @param     string  $vartype  变量类型     * @param     string  $varname  变量名称     * @return    string     */    function CompilerArrayVar($vartype, $varname)    {        $okvalue = '';        if(!preg_match("/\[/", $varname))        {            if(preg_match("/^value/",$vartype))            {                $varname = $vartype.'.'.$varname;            }            $varnames = explode('.',$varname);            if(isset($varnames[1]))            {                $varname = $varnames[0];                for($i=1; isset($varnames[$i]); $i++)                {                    $varname .= "['".$varnames[$i]."']";                }            }        }        if(preg_match("/\[/", $varname))        {            $varnames = explode('[', $varname);            $arrend = '';            for($i=1;isset($varnames[$i]);$i++)            {                $arrend .= '['.$varnames[$i];            }            if(!preg_match("/[\"']/", $arrend)) {                $arrend = str_replace('[', '', $arrend);                $arrend = str_replace(']', '', $arrend);                $arrend = "['{$arrend}']";            }            if($vartype=='var')            {                $okvalue = '$GLOBALS[\'_vars\'][\''.$varnames[0].'\']'.$arrend;            }            else if( preg_match("/^value/", $vartype) )            {                $okvalue = '$'.$varnames[0].$arrend;            }            else if($vartype=='field')            {                $okvalue = '$fields[\''.$varnames[0].'\']'.$arrend;            }            else            {                $okvalue = '$GLOBALS[\''.$varnames[0].'\']'.$arrend;            }        }        else        {            if($vartype=='var')            {                $okvalue = '$GLOBALS[\'_vars\'][\''.$varname.'\']';            }            else if( preg_match("/^value/",$vartype) )            {                $okvalue = '$'.$vartype;            }            else if($vartype=='field')            {                $okvalue = '$'.str_replace($varname);            }            else            {                $okvalue = '$GLOBALS[\''.$varname.'\']';            }        }        return $okvalue;    }    /**     *  编译if标记     *     * @access    public     * @param     string  $cTag  标签     * @return    string     */    function CompilerIf($cTag)    {        $condition = trim($cTag->GetAtt('condition'));        if($condition =='')        {            $cTag->tagValue=''; return '';        }        if (version_compare(PHP_VERSION, '5.5.0', '>='))        {            $condition = preg_replace_callback("/((var\.|field\.|cfg\.|global\.|key[0-9]{0,}\.|value[0-9]{0,}\.)[\._a-z0-9]+)/is", "private_rt", $condition);        } else {            $condition = preg_replace("/((var\.|field\.|cfg\.|global\.|key[0-9]{0,}\.|value[0-9]{0,}\.)[\._a-z0-9]+)/ies", "private_rt('\\1')", $condition);        }        $rsvalue = '<'.'?php if('.$condition.'){ ?'.'>';        $rsvalue .= $cTag->GetInnerText();        $rsvalue .= '<'.'?php } ?'.'>';        return $rsvalue;    }    /**     *  处理block区块传递的atts属性的值     *     * @access    public     * @param     string  $v  值     * @return    string     */    function TrimAtts($v)    {        $v = str_replace('<'.'?','<?',$v);        $v = str_replace('?'.'>','?>',$v);        return  $v;    }    /**     *  函数 function 语法处理     *     * @access    public     * @param     string  $funcstr  函数字符串     * @param     string  $nvalue  函数值     * @return    string     */    function CompilerFunction($funcstr, $nvalue)    {        $funcstr = str_replace('@quote', '"', $funcstr);        $funcstr = str_replace('@me', $nvalue, $funcstr);        return $funcstr;    }    /**     *  引入文件 include 语法处理     *     * @access    public     * @param     string  $filename  文件名     * @param     string  $isload  是否载入     * @return    string     */    function CompilerInclude($filename, $isload=TRUE)    {        $okfile = '';        if( @file_exists($filename) )        {            $okfile = $filename;        }        else if( @file_exists($this->refDir.$filename) )        {            $okfile = $this->refDir.$filename;        }        else if( @file_exists($this->refDir."../".$filename) )        {            $okfile = $this->refDir."../".$filename;        }        if($okfile=='') return 0;        if( !$isload ) return 1;        $itpl = new DedeTemplate($this->templateDir);        $itpl->isCache = $this->isCache;        $itpl->SetObject($this->refObj);        $itpl->LoadTemplate($okfile);        return $itpl->CacheFile();    }}/** * class TagAttribute Tag属性集合 * function C__TagAttribute(); * 属性的数据描述 * * @package          TagAttribute * @subpackage       DedeCMS.Libraries * @link             http://www.dedecms.com */class TagAttribute{    var $count = -1;    var $items = array(); //属性元素的集合    /**     *  获得某个属性     *     * @access    public     * @param     string    $str  预处理字符串     * @return    string     */    function GetAtt($str)    {        if($str=="")        {            return "";        }        if(isset($this->items[$str]))        {            return $this->items[$str];        }        else        {            return "";        }    }    /**     *  同上     *     * @access    public     * @param     string    $str  预处理字符串     * @return    string     */    function GetAttribute($str)    {        return $this->GetAtt($str);    }    /**     *  判断属性是否存在     *     * @access    public     * @param     string  $str  预处理字符串     * @return    bool     */    function IsAttribute($str)    {        if(isset($this->items[$str])) return TRUE;        else return FALSE;    }    /**     *  获得标记名称     *     * @access    public     * @return    string     */    function GettagName()    {        return $this->GetAtt("tagname");    }    /**     *  获得属性个数     *     * @access    public     * @return    int     */    function Getcount()    {        return $this->count+1;    }}//End Class/** * 属性解析器 * function C__TagAttributeParse(); * * @package          TagAttribute * @subpackage       DedeCMS.Libraries * @link             http://www.dedecms.com */class TagAttributeParse{    var $sourceString = "";    var $sourceMaxSize = 1024;    var $cAttributes = "";    var $charToLow = TRUE;    function SetSource($str="")    {        $this->cAttributes = new TagAttribute();        $strLen = 0;        $this->sourceString = trim(preg_replace("/[ \r\n\t\f]{1,}/"," ",$str));        $strLen = strlen($this->sourceString);        if($strLen>0 && $strLen <= $this->sourceMaxSize)        {            $this->ParseAttribute();        }    }    /**     *  解析属性     *     * @access    public     * @return    void     */    function ParseAttribute()    {        $d = '';        $tmpatt = '';        $tmpvalue = '';        $startdd = -1;        $ddtag = '';        $hasAttribute=FALSE;        $strLen = strlen($this->sourceString);        // 获得Tag的名称,解析到 cAtt->GetAtt('tagname') 中        for($i=0; $i<$strLen; $i++)        {            if($this->sourceString[$i]==' ')            {                $this->cAttributes->count++;                $tmpvalues = explode('.', $tmpvalue);                $this->cAttributes->items['tagname'] = ($this->charToLow ? strtolower($tmpvalues[0]) : $tmpvalues[0]);                if( isset($tmpvalues[2]) )                {                    $okname = $tmpvalues[1];                    for($j=2;isset($tmpvalues[$j]);$j++)                    {                        $okname .= "['".$tmpvalues[$j]."']";                    }                    $this->cAttributes->items['name'] = $okname;                }                else if(isset($tmpvalues[1]) && $tmpvalues[1]!='')                {                    $this->cAttributes->items['name'] = $tmpvalues[1];                }                $tmpvalue = '';                $hasAttribute = TRUE;                break;            }            else            {                $tmpvalue .= $this->sourceString[$i];            }        }        //不存在属性列表的情况        if(!$hasAttribute)        {            $this->cAttributes->count++;            $tmpvalues = explode('.', $tmpvalue);            $this->cAttributes->items['tagname'] = ($this->charToLow ? strtolower($tmpvalues[0]) : $tmpvalues[0]);            if( isset($tmpvalues[2]) )            {                $okname = $tmpvalues[1];                for($i=2;isset($tmpvalues[$i]);$i++)                {                    $okname .= "['".$tmpvalues[$i]."']";                 }                $this->cAttributes->items['name'] = $okname;            }            else if(isset($tmpvalues[1]) && $tmpvalues[1]!='')            {                $this->cAttributes->items['name'] = $tmpvalues[1];            }            return ;        }        $tmpvalue = '';        //如果字符串含有属性值,遍历源字符串,并获得各属性        for($i; $i<$strLen; $i++)        {            $d = $this->sourceString[$i];            //查找属性名称            if($startdd==-1)            {                if($d != '=')                {                    $tmpatt .= $d;                }                else                {                    if($this->charToLow)                    {                        $tmpatt = strtolower(trim($tmpatt));                    }                    else                    {                        $tmpatt = trim($tmpatt);                    }                    $startdd=0;                }            }            //查找属性的限定标志            else if($startdd==0)            {                switch($d)                {                    case ' ':                        break;                    case '\'':                        $ddtag = '\'';                        $startdd = 1;                        break;                    case '"':                        $ddtag = '"';                        $startdd = 1;                        break;                    default:                        $tmpvalue .= $d;                        $ddtag = ' ';                        $startdd = 1;                        break;                }            }            else if($startdd==1)            {                if($d==$ddtag && ( isset($this->sourceString[$i-1]) && $this->sourceString[$i-1]!="\\") )                {                    $this->cAttributes->count++;                    $this->cAttributes->items[$tmpatt] = trim($tmpvalue);                    $tmpatt = '';                    $tmpvalue = '';                    $startdd = -1;                }                else                {                    $tmpvalue .= $d;                }            }        }//for        //最后一个属性的给值        if($tmpatt != '')        {            $this->cAttributes->count++;            $this->cAttributes->items[$tmpatt] = trim($tmpvalue);        }//print_r($this->cAttributes->items);    }// end func}//End Class/** *  私有标签编译,主要用于if标签内的字符串解析 * * @access    public * @param     string  $str  需要编译的字符串 * @return    string */function private_rt($str){    if (is_array($str)) {        $arr = explode('.', $str[0]);    } else {        $arr = explode('.', $str);    }        $rs = '$GLOBALS[\'';    if($arr[0] == 'cfg')    {        return $rs.'cfg_'.$arr[1]."']";    }    elseif($arr[0] == 'var')    {        $arr[0] = '_vars';        $rs .= implode('\'][\'', $arr);        $rs .= "']";        return $rs;    }    elseif($arr[0] == 'global')    {        unset($arr[0]);        $rs .= implode('\'][\'', $arr);        $rs .= "']";        return $rs;    }    else    {        if($arr[0] == 'field') $arr[0] = 'fields';        $rs = '$'.$arr[0]."['";        unset($arr[0]);        $rs .= implode('\'][\'', $arr);        $rs .= "']";        return $rs;    }}


0 0
原创粉丝点击