国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > CMS > 織夢(mèng)DEDE > 正文

dedecms源代碼深入研究(5)ParseTemplet算法分析

2024-07-12 08:43:55
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

今天講的是dedecms最關(guān)鍵的東西,模板分析啦。

先看看一個(gè)dedecms標(biāo)簽,大家心里有個(gè)數(shù):

  1. {dede:arclist row=10 orderby=pubdate type='image.' imgwidth='143' imgheight='106'} 
  2.  
  3. href="[field:arcurl/]">[field:image/]class="title">[field:title/] 
  4. {/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:”這種形式

  1. $FullTagStartWord =  $TagStartWord.$this->NameSpace.":"
  2.  
  3. $sTagEndWord =  $TagStartWord."/".$this->NameSpace.":"
  4.  
  5. $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,就退出。

  1. if$sourceLen <= ($tsLen + 3) ){ 
  2.  
  3.     return
  4.  

為什么要加3(也就是模板長(zhǎng)度最少應(yīng)該是標(biāo)簽起始字符串長(zhǎng)度加4)呢?

我們看看我們能寫(xiě)出的最短標(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)化為小寫(xiě)。

接下來(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ě)得很清楚,但我們現(xiàn)在還看不懂,所以先知道有這么個(gè)判斷就行啦

  1. //如果不進(jìn)行此判斷,將無(wú)法識(shí)別相連的兩個(gè)標(biāo)記 
  2.  
  3. if($i-1 >= 0){ 
  4.  
  5.     $ss = $i-1; 
  6.  
  7. }else
  8.  
  9.     $ss = 0; 
  10.  

設(shè)定了一個(gè)變量$ss,后面留意一下就是了。

下面就是查找標(biāo)簽了

  1. $sPos = strpos($this->SourceString,$FullTagStartWord,$ss); 
  2.  
  3. $isTag = $sPos

找到在模板字符串中從$ss指定的位置開(kāi)始,第一個(gè)類似“{dede:”這種標(biāo)簽頭的位置,并把$isTag變量設(shè)置為strpos的返回值,這是個(gè)偷懶的寫(xiě)法,應(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)的呢?

  1. if($j>($sourceLen-1)){ 
  2.  
  3.   break
  4.  
  5. }else ifereg("[/ /t/r/n]",$this->SourceString[$j]) || $this->SourceString[$j] == $this->TagEndWord ){ 
  6.  
  7.   break
  8.  
  9. }else
  10. //Vevb.com 
  11.   $tTagName .= $this->SourceString[$j]; 
  12.  

這個(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è)變量

  1. $i = $sPos+$tsLen
  2.  
  3. $endPos = -1; 
  4.  
  5. $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})

  1. $e1 = strpos($this->SourceString,$eTagEndWord$i); 
  2.  
  3. $e2 = strpos($this->SourceString,$FullTagStartWord$i); 
  4. //Vevb.com 
  5. $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

  1. $e1 = trim($e1); $e2 = trim($e2); $e3 = trim($e3);  
  2.  
  3. $e1 = ($e1=='' ? '-1' : $e1); 
  4.  
  5. $e2 = ($e2=='' ? '-1' : $e2); 
  6.  
  7. $e3 = ($e3=='' ? '-1' : $e3); 

接下來(lái)就要根據(jù)這三個(gè)值進(jìn)行一些處理啦。處理什么呢?我們先看看這段代碼吧:

  1. //not found '{/tag:' 
  2.  
  3. if($e3==-1) { 
  4.  
  5.   $endPos = $e1
  6.  
  7.   $elen = $endPos + strlen($eTagEndWord); 
  8.  
  9.  
  10. //not found '/}' 
  11.  
  12. else if($e1==-1) { 
  13.  
  14.   $endPos = $e3
  15.  
  16.   $elen = $endPos + strlen($fullTagEndWordThis); 
  17.  
  18.  
  19. //found '/}' and found '{/dede:' 
  20.  
  21. else
  22.  
  23.     //if '/}' more near '{dede:'、'{/dede:' , end tag is '/}', else is '{/dede:' 
  24.  
  25.   if($e1 < $e2 &&  $e1 < $e3 ){ 
  26.  
  27.     $endPos = $e1
  28.  
  29.     $elen = $endPos + strlen($eTagEndWord); 
  30. //Vevb.com 
  31.   }else
  32.  
  33.     $endPos = $e3
  34.  
  35.     $elen = $endPos + strlen($fullTagEndWordThis); 
  36.  
  37.   } 
  38.  

我們知道,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ù)。

  1. $attStr = ''
  2.  
  3. $innerText = ''
  4.  
  5. $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)容字符串的。

  1. if($startInner==0 && ($this->SourceString[$j]==$TagEndWord && $this->SourceString[$j-1]!="//") ){ 
  2.  
  3.   $startInner=1; 
  4.  
  5.   continue
  6.  
  7.  
  8. if($startInner==0){ 
  9.  
  10.   $attStr .= $this->SourceString[$j]; 
  11. //Vevb.com 
  12. }else
  13.  
  14.   $innerText .= $this->SourceString[$j]; 
  15.  

嗯,用了兩個(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)容啦

  1. $cAtt->SetSource($attStr); 
  2.  
  3. if($cAtt->cAttributes->GetTagName()!=''){ 
  4.  
  5.   $this->Count++; 
  6.  
  7.   $CDTag = new DedeTag(); 
  8.  
  9.   $CDTag->TagName = $cAtt->cAttributes->GetTagName(); 
  10.  
  11.   $CDTag->StartPos = $sPos
  12.  
  13.   $CDTag->EndPos = $i
  14.  
  15.   $CDTag->CAttribute = $cAtt->cAttributes; 
  16.  
  17.   $CDTag->IsReplace = FALSE; 
  18.  
  19.   $CDTag->TagID = $this->Count
  20.  
  21.   $CDTag->InnerText = $innerText
  22.  
  23.   $this->CTags[$this->Count] = $CDTag
  24.  

通過(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)簽信息保存到緩存文件或者叫中間信息文件。

模板分析就講完啦,這樣該有的信息就都有了,我們又可以回到LoadTemplate方法繼續(xù)啦。

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 海盐县| 云浮市| 沂源县| 舞阳县| 天门市| 孟连| 莱西市| 望城县| 乌拉特前旗| 长兴县| 乌拉特中旗| 揭西县| 东山县| 文成县| 定兴县| 疏附县| 稷山县| 巴彦县| 柘荣县| 中牟县| 德令哈市| 九龙县| 偃师市| 蒙山县| 栾城县| 凤城市| 龙山县| 广昌县| 菏泽市| 缙云县| 望江县| 磐安县| 汾西县| 赤水市| 平潭县| 贵州省| 克什克腾旗| 济南市| 阳西县| 宁都县| 临清市|