dedecms代碼研究六

今天講的是dedecms最關鍵的東西,模板分析啦。也就是dedetag.class.php 里面的ParseTemplet方法 模板解析方法

先看看一個dedecms標簽,大家心里有個數:

{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}

參考上面標簽我們就可以進一步分析啦。

這里假定,你已經了解了dedecms的標簽形式,標簽格式,和標簽種類。

下面我們展開分析

先看方法前面初始化一些最基本的變量:

1)標簽起始符號和結束符號。如:“{”和"}"

$TagStartWord = $this->TagStartWord;

$TagEndWord = $this->TagEndWord;

2)設置臨時變量,用于臨時存儲查找到的新標簽在模板中的起始位置和結束位置。

$sPos = 0; $ePos = 0;

3)設定完整標簽起始字符串和結束字符串。比如:“{dede:”這種形式

$FullTagStartWord =? $TagStartWord.$this->NameSpace.":";

$sTagEndWord =? $TagStartWord."/".$this->NameSpace.":";

$eTagEndWord = "/".$TagEndWord;

這里值得注意的是結束部分分兩種,一種是類似于{aa:ff /}單體結構標簽,一種是類似于{aa:fff}{/aa:fff}符合結構標簽

4)獲取標簽其實字符串({dede:)長度和整個模板的長度

$tsLen = strlen($FullTagStartWord);

$sourceLen=strlen($this->SourceString);

?

上面就是初始變量設置部分啦。

接下來是個小判斷,如果整個模板的長度不大于標簽起始字符串的長度加3,就退出。

if( $sourceLen <= ($tsLen + 3) ){

??? return;

}

為什么要加3(也就是模板長度最少應該是標簽起始字符串長度加4)呢?

我們看看我們能寫出的最短標簽:

{dede:a/}

冒號后面是可能出現的最短字符串,就是3個,所以這里如果小于3就連最起碼的一個標簽都無法完整,所以要做這個判斷,至于等于嘛,我個人認為是沒必要的。

好繼續往下看下面兩句:

$cAtt = new DedeAttributeParse();

$cAtt->charToLow = $this->CharToLow;

創建了一個DedeAttributeParse類,并設定了CharToLow屬性,這個類看名字應該是標簽屬性分析類,charToLow就是是否把字符串自動轉化為小寫。

接下來就是一個長長的for循環了,遍歷模板字符串的每個字符進行分析,提取模板中的標簽。

for($i=0; $i < $sourceLen; $i++)

下面我們就來看看這個for循環里面是怎么分析的吧

先定義一個臨時變量,存儲當前找到的標簽的名字

$tTagName = '';

下面是一個判斷,注釋寫得很清楚,但我們現在還看不懂,所以先知道有這么個判斷就行啦

//如果不進行此判斷,將無法識別相連的兩個標記

if($i-1 >= 0){

??? $ss = $i-1;

}else{

??? $ss = 0;

}

設定了一個變量$ss,后面留意一下就是了。

下面就是查找標簽了

$sPos = strpos($this->SourceString,$FullTagStartWord,$ss);

$isTag = $sPos;

找到在模板字符串中從$ss指定的位置開始,第一個類似“{dede:”這種標簽頭的位置,并把$isTag變量設置為strpos的返回值,這是個偷懶的寫法,應該明確指出查到標簽了,就是true,而不是任意字符。

我們看到這里用到了$ss,作用是設定查找的起始位置。

我們繼續往下看吧

下面一個if語句好像是對第一個字符開始就是標簽的情況下的一種補充?

搞不懂了,本來就能找到的,加這句什么意思呢?多余哦,這個肯定有更好方法的。不多說這句了。

在下來的if就是如果沒找到標簽就不循環了,不解釋。

再下來,一個子循環

for($j=($sPos+$tsLen);$j<($sPos+$tsLen+$this->TagMaxLen);$j++)

$tsLen我們之前說了,是標簽頭(類似{dede:)長度

那這個for的解釋就是遍歷從標簽頭的下一個字符開始到標簽最大長度位置結束這中間的所有字符,看來是要找標簽名字啦

再看看for循環里面,很簡單的幾句,就是找出標簽的名字,如何找出來的呢?

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];}

這個for里面的if語句,兩種情況下名字結束,一種是字符位置到模板的字后一個位置,另一種是發現了空格、斷行、tab符、/等或找到了標簽結束符(如:"}")

通過這個for循環,標簽的名字就弄出來了,保存在變量$tTagName中。

下面是一個極其長的if語句啦,判斷$tTagName變量是否為空,如果是空則跳出循環(標簽出錯了嘛),不過跳出前還設置$i,有什么用?看不懂。

接下來重點就是找到標簽名字的情況啦。

先是設置幾個變量

$i = $sPos+$tsLen;

$endPos = -1;

$fullTagEndWordThis = $sTagEndWord.$tTagName.$TagEndWord;

把循環模板字符串的指針$i跳到標簽名字開始的地方。然后設置變量$endPos 為-1,組合出一種標簽結束符({/dede:xxx})

?

接下來是查找三個位置:$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就是在標簽名字找到后第一個"/}"出現的位置,$e2就是第一個“{dede:”出現的位置,$e3就是第一個{/dede:xxx}出現的位置。這里注意,獲取$e3值的時候,$fullTagEndWordThis是以當前找到的標簽為名字的結束字符串。

?

在下面幾句是統一$e1 $e2 $e3的值,使這三個變量如果找到要找的標簽字符串就保存位置,找不到就保存-1

$e1 = trim($e1); $e2 = trim($e2); $e3 = trim($e3);

$e1 = ($e1=='' ? '-1' : $e1);

$e2 = ($e2=='' ? '-1' : $e2);

$e3 = ($e3=='' ? '-1' : $e3);

?

接下來就要根據這三個值進行一些處理啦。處理什么呢?我們先看看這段代碼吧:

//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標簽結束有兩種方式,一種是(/})這種方式,還有一種是({/dede:xxx}),除此之外沒有他選,如果沒有這兩種結束,只能說明一個問題,模板內的標簽不完整。這個if語句做了一個假設,就是兩種標簽結束方式一定是有一種存在的。

?

if的第一個分支,假設$e3為-1,也就是(/})這種方式存在,所以設置了標簽結束符位置變量$endPos為變量$e1的值,而此時,標簽最終結束位置就知道了,是$endPos加上(/})的長度。

if語句的第二個分支和第一個類似,只是假定找到了({/dede:xxx})。

if語句的else部分,是假定兩個都找到了(有這種可能嗎?),那么就要進一步分析啦,如果(/})這種結束符出現的位置比下個標簽起始位置靠前,而且還比$e3的結束符({/dede:xxx})位置靠前,說明當前找到的(/})就是當前標簽的結束符;否則一定是({/dede:xxx})這種啦。

上面通過$e1 $e2 $e3的變量設置和一個if語句,最終是要得到兩個變量:$endPos和$elen,當前標簽結束符開始的位置和結束位置。

下面又是一個if語句,很簡單,通過endPos是否為-1判斷當前標簽是否正確結束。如果沒有正確結束則打印一段文字,然后就退出循環。這塊設計的是否可以再好點呢,比如把這塊出錯的標簽替換為一個錯誤信息,或在做模板分析前,統一檢查語法正確性,以保證更快速分析模板。

再繼續往下看,又是設置了兩個變量。

$i = $elen;

$ePos = $endPos;

由于找到當前循環要找的標簽,所以,設置主循環for的循環變量$i到下個標簽的起始位置。

設置當前標簽的結束符起始位置$ePos。

?

當前標簽的開始位置和結束位置都確定了,接下來就可以分析標簽的屬性了,我們繼續。

$attStr = '';

$innerText = '';

$startInner = 0;

三個變量,我們了解到,標簽內部有兩種東西,一種是屬性字符串,還有一種是內容字符串。$startInner 變量指示內容字符串是否開始(奇怪為什么不用布爾值呢)。

下面一個for循環開始提取這些字符串,從標簽名稱后面到結束符開始之前的部分。

for($j=($sPos+$tsLen);$j < $ePos;$j++)

?

看看循環里面是怎么提取屬性字符串和內容字符串的。

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];}

嗯,用了兩個if語句,第一個語句是用來判斷內容字符串是否開始的。第二個if語句根據內容字符串開始指示符判斷,分別讀取內容字符串和屬性字符串。

個人認為,通過特殊標識符截字更快一些。

這里面還有個問題就是,是否內容字符串開始是如何判斷的呢?

我們看看第一個if

if($startInner==0 && ($this->SourceString[$j]==$TagEndWord && $this->SourceString[$j-1]!="\\") )

$startInner==0這句就是做個過濾,當讀取內容字符串的時候就不會再走這個if了,關鍵是&&后面括號里面的內容。

如果當前字符為標簽結束符$TagEndWord(})而且結束符的前一個字符不是反斜杠的時候,就是屬性部分結束了,如果是反斜杠說明是一些模板內容之類的了。

?

通過上面的for循環我們就提取出了當前標簽的屬性和內容,接下來就開始分析屬性和內容啦

$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;}

通過屬性分析類來進行分析啦,然后創建DedeTag標簽類實例(就是創建一個標簽對象),然后把當前標簽的屬性都放進這個標簽對象。

包括標簽名稱、起始位置、結束位置、屬性數組、內部字符串等。

然后,把這個新的標簽對象放到DedeTagParse類的CTags數組中。

?

這樣一個標簽就分析完了,也結束了一次最外層的for循環。原來每循環一次只能分析出一個標簽,有多少個標簽就 有可能循環多少次。

?

整個模板分析結束后,如果允許緩存再調用SaveCache方法,把當前模板的標簽信息保存到緩存文件或者叫中間信息文件。

ps:其實整個模板解析方法也只是把模板標簽的信息記錄了下來放到新建的dedetag對象中,然后保存到當前DedeParse類的CTags數組中(類似于上一節我們從緩存文件分析的過程)

分析到這里,大家可能有個疑問?

織夢系統是怎么通過標簽獲取到數據的呢?這就是我們下部分需要分析的,其實細心的朋友應該已經知道,肯定是我們之前在視圖類里面的MakeOneTag這個方法里面

模板分析就講完啦,這樣該有的信息就都有了,我們又可以回到LoadTemplate方法繼續啦。 闞榮華博客:www.kanronghua.com 歡迎學習和交流

轉載于:https://www.cnblogs.com/ronghua/p/5936084.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/457176.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/457176.shtml
英文地址,請注明出處:http://en.pswp.cn/news/457176.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Django-session的存放位置

django-session 存放位置 設置session的保存位置&#xff0c;有三種方法&#xff1a; 保存在關系數據庫(db)保存在緩存數據庫(cache) 或者 關系緩存數據庫(cache_db) 保存在文件系統中(file) 第一種 保存在數據庫中&#xff1a;需要在 INSTALL_APPS 中添加&#xff1a;djan…

允許多域名跨域 php,PHP設置多域名允許跨域訪問

針對 PHP 語言設置多域名允許跨域訪問服務器變量&#xff1a;服務器變量存儲在 $_SERVER 數組中&#xff0c;在這個數組中有一個特殊的鍵值&#xff1a;HTTP_ORIGIN。這個鍵只在跨域的時候才會存在值&#xff0c;同源時為空字符串響應頭設置允許某域名訪問&#xff1a;access-c…

螞蟻分類信息系統 5.8 信息瀏覽量后臺自定義設置

mymps 螞蟻分類信息是一款基于PHPMySQL的建站系統,為在各種服務器上架設分類信息以及地方門戶網站提供完美的解決方案. mymps5.8 下載 螞蟻分類系統 5.8下載 螞蟻分類系統下載 mymps下載 螞蟻分類信息系統 5.8 原信息瀏覽量后臺無法自定義&#xff0c;現增加后臺自定義瀏覽量…

MS SQL-Server快捷鍵

快捷鍵 功能 CtrlShiftB 生成解決方案 CtrlF7 生成編譯 CtrlO 打開文件 CtrlShiftC 顯示類視圖窗口 CtrlShiftO 打開項目 F4 顯示屬性窗口 ShiftF4 …

python編寫四位數驗證碼

def verifycode(request):#引入繪圖模塊from PIL import Image, ImageDraw, ImageFont#引入隨機函數模塊import random#定義變量&#xff0c;用于畫面的背景色、寬、高bgcolor (random.randrange(20, 100), random.randrange(20, 100), random.randrange(20, 100))width 100h…

php 計算數據偏離度,關于偏離度的測算方法

2015年6月技術總結——關于偏離度的測算方法研究院公用事業部 路璐引言《原理》中說“偏離度是指每一種償債來源與財富創造能力的距離&#xff0c;所體現的是償債來源對債務安全的保障程度&#xff0c;唯有通過揭示償債來源與財富創造能力偏離度才能真正區別每一種償債來源的風…

Django中celery配置總結

情景&#xff1a; 用戶發起request&#xff0c;并等待response返回。在本些views中&#xff0c;可能需要執行一段耗時的程序&#xff0c;那么用戶就會等待很長時間&#xff0c; 造成不好的用戶體驗&#xff0c;比如發送郵件、手機驗證碼等。 使用celery后&#xff0c;情況就不…

AngularJs ng-repeat限制循環次數

重復數組&#xff1a;<ul ng-initname[1,2,3,3]><li ng-repeat"name in name track by $index">{{name}}</li> </ul> 對象&#xff1a; <ul ng-initobj{ a:"aaa", b:"bbb", c:"ccc" }><li ng-repeat…

qml demo分析(customgeometry-貝塞爾曲線)

一、效果展示 本篇文章還是帶來一個簡單的qt示例分析&#xff0c;且看圖1效果。 圖1 貝塞爾曲線 二、源碼分析 該示例代碼所在目錄quick\scenegraph\customgeometry&#xff0c;感興趣的同學可以自己去找&#xff0c;這篇文章我還是主要講解源碼&#xff0c;不涉及其他方面。 廢…

php 打包上傳的文件,PHP單文件上傳原理及上傳函數的打包

PHP單文件上傳原理及上傳函數的封裝服務器(臨時文件)——>指定目錄,當文件進入服務器時它就是臨時文件了,這時操作中要用臨時文件的名稱tmp_name。//在客戶端設置上傳文件的限制(文件類型和大小)是不安全的&#xff0c;因為客戶能通過源代碼修改限制&#xff0c;所以在服務端…

celery AttributeError: 'str' object has no attribute 'items'

AttributeError: str object has no attribute items python manage.py celery worker --loglevelinfo 查了好久發現原來是因為以前下的redis版本過高&#xff08; 3.0.1 &#xff09;&#xff0c;重裝redis pip install redis2.10.6

equals和==的區別(轉)

基本數據類型&#xff0c;也稱原始數據類型。byte,short,char,int,long,float,double,boolean&#xff0c;他們之間的比較&#xff0c;應用雙等號&#xff08;&#xff09;,比較的是他們的值。 復合數據類型(類) 當他們用&#xff08;&#xff09;進行比較的時候&#xff0c;…

Python 中的range,以及numpy包中的arange函數

range函數函數說明&#xff1a; range(start, stop[, step]) -> range object&#xff0c;根據start與stop指定的范圍以及step設定的步長&#xff0c;生成一個序列。  參數含義&#xff1a;start:計數從start開始。默認是從0開始。例如range&#xff08;5&#xff09;等價…

php角色權限安全,php – 安全的chmod權限?

新創建的文件和目錄的默認權限由umask環境變量設置.文件的所有者和root可以更改權限.如果您不需要在應用程序中使用chmod,請將其保留在禁用列表中.你應該看看安全性的方式是&#xff1a;現在比我更聰明的人讓chmod成為我應用程序中更安全的部分之一.因此,我將花費我的時間使其他…

namespace! 報錯

在工程文件中url 定義namespace需要 from django.contrib import admin from django.urls import path,include from django.conf.urls import url urlpatterns [path(admin/, admin.site.urls),url(r^liu/,include(myApp.urls,namespaceapp)), ] 在myApp 的urls中 加這句 a…

test.php.bak,MongoDB熱備份工具:解決官方版備份缺陷

賀春旸&#xff0c;凡普金科DBA團隊負責人&#xff0c;《MySQL管理之道&#xff1a;性能調優、高可用與監控》第一、二版作者&#xff0c;曾任職于中國移動飛信、安卓機鋒網。致力于MariaDB、MongoDB等開源技術的研究&#xff0c;主要負責數據庫性能調優、監控和架構設計。工具…

zookeeper工作原理、安裝配置、工具命令簡介

1 Zookeeper簡介Zookeeper 是分布式服務框架&#xff0c;主要是用來解決分布式應用中經常遇到的一些數據管理問題&#xff0c;如&#xff1a;統一命名服務、狀態同步服務、集群管理、分布式應用配置項的管理等等。 ZooKeeper是一個分布式的&#xff0c;開放源碼的分布式應用程序…

流式大數據處理的三種框架:Storm,Spark和Samza

許多分布式計算系統都可以實時或接近實時地處理大數據流。本文將對三種Apache框架分別進行簡單介紹&#xff0c;然后嘗試快速、高度概述其異同。 Apache Storm 在Storm中&#xff0c;先要設計一個用于實時計算的圖狀結構&#xff0c;我們稱之為拓撲&#xff08;topology&#x…

models.ForeignKey( ,on_delete=models.CASCADE)? ? # 關聯外鍵

# 關聯外鍵 sgrade models.ForeignKey("Grades",on_deletemodels.CASCADE) 版本跟新后忘記加on_deletemodels.CASCADE 報錯困擾了很久 多讀報錯信息 from django.db import models# Create your models here. class Grades(models.Model):gname models.Cha…

java 產生無重復的隨機數,Java創建無重復的隨機數

小編典典最簡單的方法是創建一個可能數字的列表(1..20或任何數字)&#xff0c;然后用對其進行混洗Collections.shuffle。然后&#xff0c;只需考慮你想要的許多元素。如果你的范圍最終等于你需要的元素數量(例如&#xff0c;用于洗牌的卡片)&#xff0c;則這非常好。如果你想要…