dedecms代码研究六
今天講的是dedecms最關(guān)鍵的東西,模板分析啦。也就是dedetag.class.php 里面的ParseTemplet方法 模板解析方法
先看看一個(gè)dedecms標(biāo)簽,大家心里有個(gè)數(shù):
{dede:arclist row=10 orderby=pubdate type='image.' imgwidth='143' imgheight='106'}
<li><a href="[field:arcurl/]">[field:image/]<span class="title">[field:title/]</span></a></li>
{/dede:arclist}
參考上面標(biāo)簽我們就可以進(jìn)一步分析啦。
這里假定,你已經(jīng)了解了dedecms的標(biāo)簽形式,標(biāo)簽格式,和標(biāo)簽種類。
下面我們展開(kāi)分析
先看方法前面初始化一些最基本的變量:
1)標(biāo)簽起始符號(hào)和結(jié)束符號(hào)。如:“{”和"}"
$TagStartWord = $this->TagStartWord;
$TagEndWord = $this->TagEndWord;
2)設(shè)置臨時(shí)變量,用于臨時(shí)存儲(chǔ)查找到的新標(biāo)簽在模板中的起始位置和結(jié)束位置。
$sPos = 0; $ePos = 0;
3)設(shè)定完整標(biāo)簽起始字符串和結(jié)束字符串。比如:“{dede:”這種形式
$FullTagStartWord =? $TagStartWord.$this->NameSpace.":";
$sTagEndWord =? $TagStartWord."/".$this->NameSpace.":";
$eTagEndWord = "/".$TagEndWord;
這里值得注意的是結(jié)束部分分兩種,一種是類似于{aa:ff /}單體結(jié)構(gòu)標(biāo)簽,一種是類似于{aa:fff}{/aa:fff}符合結(jié)構(gòu)標(biāo)簽
4)獲取標(biāo)簽其實(shí)字符串({dede:)長(zhǎng)度和整個(gè)模板的長(zhǎng)度
$tsLen = strlen($FullTagStartWord);
$sourceLen=strlen($this->SourceString);
?
上面就是初始變量設(shè)置部分啦。
接下來(lái)是個(gè)小判斷,如果整個(gè)模板的長(zhǎng)度不大于標(biāo)簽起始字符串的長(zhǎng)度加3,就退出。
if( $sourceLen <= ($tsLen + 3) ){
??? return;
}
為什么要加3(也就是模板長(zhǎng)度最少應(yīng)該是標(biāo)簽起始字符串長(zhǎng)度加4)呢?
我們看看我們能寫出的最短標(biāo)簽:
{dede:a/}
冒號(hào)后面是可能出現(xiàn)的最短字符串,就是3個(gè),所以這里如果小于3就連最起碼的一個(gè)標(biāo)簽都無(wú)法完整,所以要做這個(gè)判斷,至于等于嘛,我個(gè)人認(rèn)為是沒(méi)必要的。
好繼續(xù)往下看下面兩句:
$cAtt = new DedeAttributeParse();
$cAtt->charToLow = $this->CharToLow;
創(chuàng)建了一個(gè)DedeAttributeParse類,并設(shè)定了CharToLow屬性,這個(gè)類看名字應(yīng)該是標(biāo)簽屬性分析類,charToLow就是是否把字符串自動(dòng)轉(zhuǎn)化為小寫。
接下來(lái)就是一個(gè)長(zhǎng)長(zhǎng)的for循環(huán)了,遍歷模板字符串的每個(gè)字符進(jìn)行分析,提取模板中的標(biāo)簽。
for($i=0; $i < $sourceLen; $i++)
下面我們就來(lái)看看這個(gè)for循環(huán)里面是怎么分析的吧
先定義一個(gè)臨時(shí)變量,存儲(chǔ)當(dāng)前找到的標(biāo)簽的名字
$tTagName = '';
下面是一個(gè)判斷,注釋寫得很清楚,但我們現(xiàn)在還看不懂,所以先知道有這么個(gè)判斷就行啦
//如果不進(jìn)行此判斷,將無(wú)法識(shí)別相連的兩個(gè)標(biāo)記
if($i-1 >= 0){
??? $ss = $i-1;
}else{
??? $ss = 0;
}
設(shè)定了一個(gè)變量$ss,后面留意一下就是了。
下面就是查找標(biāo)簽了
$sPos = strpos($this->SourceString,$FullTagStartWord,$ss);
$isTag = $sPos;
找到在模板字符串中從$ss指定的位置開(kāi)始,第一個(gè)類似“{dede:”這種標(biāo)簽頭的位置,并把$isTag變量設(shè)置為strpos的返回值,這是個(gè)偷懶的寫法,應(yīng)該明確指出查到標(biāo)簽了,就是true,而不是任意字符。
我們看到這里用到了$ss,作用是設(shè)定查找的起始位置。
我們繼續(xù)往下看吧
下面一個(gè)if語(yǔ)句好像是對(duì)第一個(gè)字符開(kāi)始就是標(biāo)簽的情況下的一種補(bǔ)充?
搞不懂了,本來(lái)就能找到的,加這句什么意思呢?多余哦,這個(gè)肯定有更好方法的。不多說(shuō)這句了。
在下來(lái)的if就是如果沒(méi)找到標(biāo)簽就不循環(huán)了,不解釋。
再下來(lái),一個(gè)子循環(huán)
for($j=($sPos+$tsLen);$j<($sPos+$tsLen+$this->TagMaxLen);$j++)
$tsLen我們之前說(shuō)了,是標(biāo)簽頭(類似{dede:)長(zhǎng)度
那這個(gè)for的解釋就是遍歷從標(biāo)簽頭的下一個(gè)字符開(kāi)始到標(biāo)簽最大長(zhǎng)度位置結(jié)束這中間的所有字符,看來(lái)是要找標(biāo)簽名字啦
再看看for循環(huán)里面,很簡(jiǎn)單的幾句,就是找出標(biāo)簽的名字,如何找出來(lái)的呢?
if($j>($sourceLen-1)){break;}else if( ereg("[/ \t\r\n]",$this->SourceString[$j]) || $this->SourceString[$j] == $this->TagEndWord ){break;}else{$tTagName .= $this->SourceString[$j];}這個(gè)for里面的if語(yǔ)句,兩種情況下名字結(jié)束,一種是字符位置到模板的字后一個(gè)位置,另一種是發(fā)現(xiàn)了空格、斷行、tab符、/等或找到了標(biāo)簽結(jié)束符(如:"}")
通過(guò)這個(gè)for循環(huán),標(biāo)簽的名字就弄出來(lái)了,保存在變量$tTagName中。
下面是一個(gè)極其長(zhǎng)的if語(yǔ)句啦,判斷$tTagName變量是否為空,如果是空則跳出循環(huán)(標(biāo)簽出錯(cuò)了嘛),不過(guò)跳出前還設(shè)置$i,有什么用?看不懂。
接下來(lái)重點(diǎn)就是找到標(biāo)簽名字的情況啦。
先是設(shè)置幾個(gè)變量
$i = $sPos+$tsLen;
$endPos = -1;
$fullTagEndWordThis = $sTagEndWord.$tTagName.$TagEndWord;
把循環(huán)模板字符串的指針$i跳到標(biāo)簽名字開(kāi)始的地方。然后設(shè)置變量$endPos 為-1,組合出一種標(biāo)簽結(jié)束符({/dede:xxx})
?
接下來(lái)是查找三個(gè)位置:$eTagEndWord(/})、$FullTagStartWord({dede:)、$fullTagEndWordThis({/dede:xxx})
$e1 = strpos($this->SourceString,$eTagEndWord, $i);
$e2 = strpos($this->SourceString,$FullTagStartWord, $i);
$e3 = strpos($this->SourceString,$fullTagEndWordThis,$i);
$e1就是在標(biāo)簽名字找到后第一個(gè)"/}"出現(xiàn)的位置,$e2就是第一個(gè)“{dede:”出現(xiàn)的位置,$e3就是第一個(gè){/dede:xxx}出現(xiàn)的位置。這里注意,獲取$e3值的時(shí)候,$fullTagEndWordThis是以當(dāng)前找到的標(biāo)簽為名字的結(jié)束字符串。
?
在下面幾句是統(tǒng)一$e1 $e2 $e3的值,使這三個(gè)變量如果找到要找的標(biāo)簽字符串就保存位置,找不到就保存-1
$e1 = trim($e1); $e2 = trim($e2); $e3 = trim($e3);
$e1 = ($e1=='' ? '-1' : $e1);
$e2 = ($e2=='' ? '-1' : $e2);
$e3 = ($e3=='' ? '-1' : $e3);
?
接下來(lái)就要根據(jù)這三個(gè)值進(jìn)行一些處理啦。處理什么呢?我們先看看這段代碼吧:
//not found '{/tag:'if($e3==-1) {$endPos = $e1;$elen = $endPos + strlen($eTagEndWord);}//not found '/}'else if($e1==-1) {$endPos = $e3;$elen = $endPos + strlen($fullTagEndWordThis);}//found '/}' and found '{/dede:'else{//if '/}' more near '{dede:'、'{/dede:' , end tag is '/}', else is '{/dede:'if($e1 < $e2 && $e1 < $e3 ){$endPos = $e1;$elen = $endPos + strlen($eTagEndWord);}else{$endPos = $e3;$elen = $endPos + strlen($fullTagEndWordThis);}}我們知道,dedecms標(biāo)簽結(jié)束有兩種方式,一種是(/})這種方式,還有一種是({/dede:xxx}),除此之外沒(méi)有他選,如果沒(méi)有這兩種結(jié)束,只能說(shuō)明一個(gè)問(wèn)題,模板內(nèi)的標(biāo)簽不完整。這個(gè)if語(yǔ)句做了一個(gè)假設(shè),就是兩種標(biāo)簽結(jié)束方式一定是有一種存在的。
?
if的第一個(gè)分支,假設(shè)$e3為-1,也就是(/})這種方式存在,所以設(shè)置了標(biāo)簽結(jié)束符位置變量$endPos為變量$e1的值,而此時(shí),標(biāo)簽最終結(jié)束位置就知道了,是$endPos加上(/})的長(zhǎng)度。
if語(yǔ)句的第二個(gè)分支和第一個(gè)類似,只是假定找到了({/dede:xxx})。
if語(yǔ)句的else部分,是假定兩個(gè)都找到了(有這種可能嗎?),那么就要進(jìn)一步分析啦,如果(/})這種結(jié)束符出現(xiàn)的位置比下個(gè)標(biāo)簽起始位置靠前,而且還比$e3的結(jié)束符({/dede:xxx})位置靠前,說(shuō)明當(dāng)前找到的(/})就是當(dāng)前標(biāo)簽的結(jié)束符;否則一定是({/dede:xxx})這種啦。
上面通過(guò)$e1 $e2 $e3的變量設(shè)置和一個(gè)if語(yǔ)句,最終是要得到兩個(gè)變量:$endPos和$elen,當(dāng)前標(biāo)簽結(jié)束符開(kāi)始的位置和結(jié)束位置。
下面又是一個(gè)if語(yǔ)句,很簡(jiǎn)單,通過(guò)endPos是否為-1判斷當(dāng)前標(biāo)簽是否正確結(jié)束。如果沒(méi)有正確結(jié)束則打印一段文字,然后就退出循環(huán)。這塊設(shè)計(jì)的是否可以再好點(diǎn)呢,比如把這塊出錯(cuò)的標(biāo)簽替換為一個(gè)錯(cuò)誤信息,或在做模板分析前,統(tǒng)一檢查語(yǔ)法正確性,以保證更快速分析模板。
再繼續(xù)往下看,又是設(shè)置了兩個(gè)變量。
$i = $elen;
$ePos = $endPos;
由于找到當(dāng)前循環(huán)要找的標(biāo)簽,所以,設(shè)置主循環(huán)for的循環(huán)變量$i到下個(gè)標(biāo)簽的起始位置。
設(shè)置當(dāng)前標(biāo)簽的結(jié)束符起始位置$ePos。
?
當(dāng)前標(biāo)簽的開(kāi)始位置和結(jié)束位置都確定了,接下來(lái)就可以分析標(biāo)簽的屬性了,我們繼續(xù)。
$attStr = '';
$innerText = '';
$startInner = 0;
三個(gè)變量,我們了解到,標(biāo)簽內(nèi)部有兩種東西,一種是屬性字符串,還有一種是內(nèi)容字符串。$startInner 變量指示內(nèi)容字符串是否開(kāi)始(奇怪為什么不用布爾值呢)。
下面一個(gè)for循環(huán)開(kāi)始提取這些字符串,從標(biāo)簽名稱后面到結(jié)束符開(kāi)始之前的部分。
for($j=($sPos+$tsLen);$j < $ePos;$j++)
?
看看循環(huán)里面是怎么提取屬性字符串和內(nèi)容字符串的。
if($startInner==0 && ($this->SourceString[$j]==$TagEndWord && $this->SourceString[$j-1]!="\\") ){$startInner=1;continue;}if($startInner==0){$attStr .= $this->SourceString[$j];}else{$innerText .= $this->SourceString[$j];}嗯,用了兩個(gè)if語(yǔ)句,第一個(gè)語(yǔ)句是用來(lái)判斷內(nèi)容字符串是否開(kāi)始的。第二個(gè)if語(yǔ)句根據(jù)內(nèi)容字符串開(kāi)始指示符判斷,分別讀取內(nèi)容字符串和屬性字符串。
個(gè)人認(rèn)為,通過(guò)特殊標(biāo)識(shí)符截字更快一些。
這里面還有個(gè)問(wèn)題就是,是否內(nèi)容字符串開(kāi)始是如何判斷的呢?
我們看看第一個(gè)if
if($startInner==0 && ($this->SourceString[$j]==$TagEndWord && $this->SourceString[$j-1]!="\\") )
$startInner==0這句就是做個(gè)過(guò)濾,當(dāng)讀取內(nèi)容字符串的時(shí)候就不會(huì)再走這個(gè)if了,關(guān)鍵是&&后面括號(hào)里面的內(nèi)容。
如果當(dāng)前字符為標(biāo)簽結(jié)束符$TagEndWord(})而且結(jié)束符的前一個(gè)字符不是反斜杠的時(shí)候,就是屬性部分結(jié)束了,如果是反斜杠說(shuō)明是一些模板內(nèi)容之類的了。
?
通過(guò)上面的for循環(huán)我們就提取出了當(dāng)前標(biāo)簽的屬性和內(nèi)容,接下來(lái)就開(kāi)始分析屬性和內(nèi)容啦
$cAtt->SetSource($attStr);if($cAtt->cAttributes->GetTagName()!=''){$this->Count++;$CDTag = new DedeTag();$CDTag->TagName = $cAtt->cAttributes->GetTagName();$CDTag->StartPos = $sPos;$CDTag->EndPos = $i;$CDTag->CAttribute = $cAtt->cAttributes;$CDTag->IsReplace = FALSE;$CDTag->TagID = $this->Count;$CDTag->InnerText = $innerText;$this->CTags[$this->Count] = $CDTag;}通過(guò)屬性分析類來(lái)進(jìn)行分析啦,然后創(chuàng)建DedeTag標(biāo)簽類實(shí)例(就是創(chuàng)建一個(gè)標(biāo)簽對(duì)象),然后把當(dāng)前標(biāo)簽的屬性都放進(jìn)這個(gè)標(biāo)簽對(duì)象。
包括標(biāo)簽名稱、起始位置、結(jié)束位置、屬性數(shù)組、內(nèi)部字符串等。
然后,把這個(gè)新的標(biāo)簽對(duì)象放到DedeTagParse類的CTags數(shù)組中。
?
這樣一個(gè)標(biāo)簽就分析完了,也結(jié)束了一次最外層的for循環(huán)。原來(lái)每循環(huán)一次只能分析出一個(gè)標(biāo)簽,有多少個(gè)標(biāo)簽就 有可能循環(huán)多少次。
?
整個(gè)模板分析結(jié)束后,如果允許緩存再調(diào)用SaveCache方法,把當(dāng)前模板的標(biāo)簽信息保存到緩存文件或者叫中間信息文件。
ps:其實(shí)整個(gè)模板解析方法也只是把模板標(biāo)簽的信息記錄了下來(lái)放到新建的dedetag對(duì)象中,然后保存到當(dāng)前DedeParse類的CTags數(shù)組中(類似于上一節(jié)我們從緩存文件分析的過(guò)程)
分析到這里,大家可能有個(gè)疑問(wèn)?
織夢(mèng)系統(tǒng)是怎么通過(guò)標(biāo)簽獲取到數(shù)據(jù)的呢?這就是我們下部分需要分析的,其實(shí)細(xì)心的朋友應(yīng)該已經(jīng)知道,肯定是我們之前在視圖類里面的MakeOneTag這個(gè)方法里面
模板分析就講完啦,這樣該有的信息就都有了,我們又可以回到LoadTemplate方法繼續(xù)啦。 闞榮華博客:www.kanronghua.com 歡迎學(xué)習(xí)和交流
轉(zhuǎn)載于:https://www.cnblogs.com/ronghua/p/5936084.html
總結(jié)
以上是生活随笔為你收集整理的dedecms代码研究六的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Mac系统安装nginx+rtmp模块
- 下一篇: 程序员搜索技巧