dedecms的模板解析与生成原理研究成果1:ParseTemplete方法的浅

fieldset{padding:10px;}

dedecms的模板解析与生成原理研究成果一:ParseTemplete方法的浅析

在织梦include/dedetemplate.class.php里面有一个名为ParseTemplete的方法,对模板中的全局以及循环标签(以{dede:开头)进行分析。

    /**     *  解析模板     *     * @access    public     * @return    void     */    function ParseTemplate()    {        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) )//如果模板内容的长度还没有模板开始字符串长度+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)            {//如果循环位实际上超过1位,则从前一位取,因为$i的下次起始位置是由本次标签循环长度决定的                $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;            }            //获取TAGNAME基本信息            for($j = $tagPos+$startWordLen; $j < $tagPos+$startWordLen+$this->tagMaxLen; $j++)            {//当前标签开始标记位置 + 开始标签长度,循环读取字符直到自定义的tagMaxLen的长度(实际应用中可能更短,但不影响性能),如果遇到HTML标签结束符、换行、空格,则结束一次处理//遇到空格、换行符、回车符、段落符、点均表示TAGNAME获取完毕,我在实际测试中,用' '==$this->sourceString{$j} || "\r"==$this->sourceString{$j}更快                if(preg_match("/[ >\/\r\n\t\}\.]/", $this->sourceString[$j]))                {                    break;                }                else                {                    $ttagName .= $this->sourceString[$j];                }            }            if($ttagName!='')            {//如果处理到标签了,就开始继续获取标签结束部分                $i = $tagPos + $startWordLen;//将$i的值前进遇到标签+开始标签长度的位置                $endPos = -1;//但是可能存在这种情况 {dede:xxx}{dede:global.cfg_cmsurl/}{/dede:xxx}就还要判断是否存在其它起始标签//可能存在的情况://1、如果后面都没有 {/dede 了,就表示只可能存在 /} 这种结束符,不是 /} 就是 {/dede://2、反之如果不存在/},那就只可能是{/dede//3、如果都存在,好吧,就看后面新的起始标签{dede:的位置//1):如果 /} 出现在 {dede: 和 {dede: 的前面,不用说,它就是结束符了,因为表示{dede:xxx/}成立//2):反之表示上述结果不成立,那只可能是{/dede:了,因为不是 /} 就是 {/dede://4、标签结束位置,就是strpos获取到的结束标签起始位置 + 结束标签的长度//5、如果标签没有结束标签或者标签不和规范怎么办?那只能在具体分析的时候判断了                $fullTagEndWordThis = $fullTagEndWord.$ttagName.$tagEndWord;//{/dede: xxxx } 拼接出结束符                $e1 = strpos($this->sourceString, $sTagEndWord, $i);//是否存在 /} 位置                $e2 = strpos($this->sourceString, $tagStartWord, $i); //是否存在 {dede:                $e3 = strpos($this->sourceString, $fullTagEndWordThis, $i); //是否存在 {/dede                $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;                //分析所找到的标记位置等信息,分为{dede:xxx}{/dede:xxx}以及{dede:global.cfg_cmsurl/}两种//从标签开始位置到标签结束标记开始位置(注意 $endpos 和 $elen 的区别),循环分析//    如果首先遇到了},两种情况,一种是{dede:global.cfg_cmsurl/}分析完毕,一种是{dede:xxx loop=10}的属性部分获取完毕,//但是肯定的是,可以获取到的cfg_cmsurl/ loop=10均属于标签属性部分attStr,就是需要进行分析的部分//剩下的就是循环标签的内容部分了,{dede:global.cfg_cmsurl/}这种就是空                $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))                {                    $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')                {                    $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);                }//最后就是装箱集中给相应的taglib目录里面的标签处理类去处理                $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();        }    }

总结流程:

1、分析整个模板,逐字符分析

2、找到模板开始与结束标签后,要将下次循环的起始变量$i及时跳跃,节约时间

总结几点:

1、用for循环逐字符处理可能比preg_macth处理速度要快

2、写产品,要考虑到标签错误造成的各种可能

3、如果是开源产品,在设计模板标签的时候一定要合理,否则在分析的时候可能给自己带来不必要的难度

往往教导我们大家要好好学习天天向上,

dedecms的模板解析与生成原理研究成果1:ParseTemplete方法的浅

相关文章:

你感兴趣的文章:

标签云: