程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 網頁編程 >> PHP編程 >> 關於PHP編程 >> dedecms代碼詳解 很全面

dedecms代碼詳解 很全面

編輯:關於PHP編程


dedecms代碼研究(1)開篇
dedecms 相信大家一定都知道這個cms 系統,功能比較強大,有比較完善的內容發布,還有內容靜態化系
統,還有就是它有自己獨特的標簽系統和模板系統。而模板系統也是其他cms系統比較難模仿的的東西,這個
東西還是需要一點開發功力和技巧的。
本系列文章就研究一下dedecms的這套系統,挖掘一下看看裡面有什麼好東西。
建議大家先了解一下dedecms的功能。自己先動手用一下,對系統功能有個大概了解。
本文先帶領大家了解一下dedecms的代碼和功能架構。
其實,dedecms在架構上沒什麼應用架構模式可言,就是最簡單的PHP 應用而已。訪問不同的PHP 文件,
管理不同的系統功能。看目錄大家都能猜出來各個目錄干什麼用的。include目錄放系統的一些公用函數和類,
plus目錄放插件,templates目錄放模板,dede 目錄是管理後台目錄。
我們打開include 目錄,看看裡面都有什麼好東西。
calendar 一個選時間的js
captcha 一個驗證碼,還是開源組件
code 沒啥意思,翻頁的文字
data 裡面是一些系統用到的資源,比如聲音,分詞庫,字體,圖片等
dialog 裡面估計是一些AJAX彈出窗口的內容部分。以後碰到了再說
inc 裡面一些單獨的功能類函數,以後研究一下為什麼要單獨放
payment 支付接口
taglib 好東西,是dedecms的標簽存放的地方,打開看看,裡面一堆文件,貌似就是dedecms的模板標
簽啦
tpllib 模板庫?暫時搞不明白,希望隨著研究深入,能弄明白
其他 include 目錄下的文件估計都是一些最基本的功能文件啦,比如常用函數,模板系統之類的東西了。用
到再說吧
打開dede目錄(就是dedecms的管理目錄)看看,喲嗬,裡面的東西還挺多,看名字就知道了,都是各種
功能管理文件,一個功能一個文件。最原始的網站開發模式做出來的。我們姑且稱之為高效吧。畢竟PHPwind
和discuz之類也是用類似的方法開發的。
整個程序大體就這些東西啦。功能就不講了,建議不熟悉dedecms的朋友自己看一下dedecms的後台管
理功能,這樣能有助於我們更好理解和分析它。
另外提一點,我們的這次代碼分析,主要分析的是其頁面生成、顯示、模板處理、標簽處理部分,這套
系統也是dedecms比較引以為傲的東西,之後呢,還會分析一些筆者覺得比較酷的功能代碼。
最後,希望這次代碼分析旅程能讓大家有所收獲。
sourcejoy 之dedecms代碼研究(2)從index開始
現在繼續,今天講的主要是dedecms的入口代碼。
2
先打開index.php看看裡面是什麼吧。打開根目錄下的index.php嗯,映入眼簾的是一個if語句。
檢查/data/common.inc.php是否存在。如果不存在就跳轉到安裝界面。
我們來到/data/看看這個目錄和common.inc.php。
打開/data/,裡面很多目錄和文件,有上傳的臨時目錄,模板緩存,壓縮的臨時目錄,各種數據庫裡保存
的系統配置信息的緩存文件以及其他的一些東西,就一個字亂~
好吧,我們打開common.inc.php。哦,原來就是保存了數據庫連接的相關變量而已。說白了,就是dedecms
的數據庫連接配置文件,估計是安裝完系統生成的。所以/index.php 會檢查它是否存在。
我們回到/index.php中繼續往下看,第二個if語句,判斷GET請求“upcache”是否存在,存在就更新首
頁緩存,不存在就直接301 跳轉到index.html,也就是dedecms的靜態首頁(記住,dedecms的前台頁面都是系
統生成的靜態頁面)。
想想接下來我們要研究什麼?對,就是GET請求“upcache”存在的時候,更新首頁緩存這小段代碼啦。
代碼如下:
require_once (dirname(__FILE__) . "/include/common.inc.php");
require_once DEDEINC."/arc.partview.class.php";
$GLOBALS['_arclistEnv'] = 'index';
$row = $dsql->GetOne("Select * From `dede_homepageset`");
$row['templet'] = MfTemplet($row['templet']);
$pv = new PartView();
$pv->SetTemplet($cfg_basedir . $cfg_templets_dir . "/" . $row['templet']);
$pv->SaveToHtml(dirname(__FILE__).'/index.html');
include(dirname(__FILE__).'/index.html');
exit();
先是加載/include/common.inc.php,估計是一些常用函數和加載其他系統函數和類的文件。
接下來又加載了DEDEINC."/arc.partview.class.php",注意DEDEINC 這個常量,我們知道經過前期對
dedecms 目錄結構觀察,arc.partview.class.php 是在/include 目錄下的,而加載/include/common.inc.php 卻沒有用
DEDEINC 這個常量,這說明,common.inc.php 裡面定義了DEDEINC 這個常量,所以後面得以使用,也印證
了common.inc.php 大體作用就是系統運行基本部分,都在這裡面啦。我們不急著進common.inc.php,繼續把
index.php 的更新緩存代碼看完。
第三句,設置了一個全局變量:$GLOBALS['_arclistEnv'] = 'index';
第四行,獲取一個表'dede_homepageset'的所有記錄,當然如果你看下數據庫,裡面沒有
“dede_homepageset”這個表,我們判斷,是數據庫操作相關函數把表名用表名前綴替換了一下前半部分,這
個都不重要了,重要的是,通過這句,獲取了首頁的相關配置信息,我們打開數據庫裡面的dede_homepageset
表,暈,就一條記錄,倆字段,基本猜到了,一個是首頁模板名稱,一個是生成的靜態文件的位置。
回來繼續分析前面的代碼
$row['templet'] = MfTemplet($row['templet']);
通過MfTemplet函數好像把$row['templet']進行了某些轉換。我們記下MfTemplet函數,以待後面挖掘。
接下來,就是new 了一個PartView 類,看這個名字,我們就知道了前面加載arc.partview.class.php 的作
用啦。
至於這個PartView類有什麼作用,我們繼續看代碼。看了下面兩行,我想大家應該都明白啦
$pv->SetTemplet($cfg_basedir . $cfg_templets_dir . "/" . $row['templet']);
3
$pv->SaveToHtml(dirname(__FILE__).'/index.html');
創建個視圖對象(PartView類的實例,我們姑且叫視圖對象),設置模板,通過SaveToHtml方法,把最
後生成的頁面寫到指定位置。
首頁生成完畢,接下來就是把生成的靜態文件通過include的形式顯示出來,然後exit中斷頁面解析。
至此,/index.php 就分析完啦。
它先是通過/data/common.inc.php,判斷是否安裝了dedecms或者說判斷是否定義了數據庫配置信息,好
為後面操作打下基礎。
然後判斷是否有GET 請求"upcache",如果有就加載/include/common.inc.php 初始化系統,然後調用
partview類的相關方法來生成靜態首頁文件,最後顯示出來。
這麼來看dedecms也沒有太多的秘密嘛~
不過呢,我們這篇文章也遺留下了幾個問題:
1)加載了/include/common.inc.php,裡面做了哪些工作?
2)/include/arc.partview.class.php到底是干什麼的,/include/下還有很多arc開頭的文件都是干什麼的?
3)MfTemplet這個函數到底對模板文件路徑這個字符串做了什麼操作?
4)partview類的相關方法都有什麼秘密?
帶著這幾個疑問,我們將結束本文,後面的文章將將這些謎題一一揭開。
sourcejoy 之dedecms代碼研究(3)partview的迷惑
上次,我們從dedecms的index.php文件中了解到了很多信息,也提出了一些問題,本文開始就帶著前面的問題,
繼續我們的dedecms之旅吧。
先回顧一下之前我們在index.php文件研究中總結的東西。
首先加載common.inc.php,接下來組織模板,生成靜態頁面並跳到靜態頁面。
接下來,我們就先來看看common.inc.php 裡面都有什麼吧。打開/include/common.inc.php 裡面的注釋已
經說地比較清楚了。我們大概說說結構。
先是定義一堆常量。然後是做一些安全措施,對PHP 的系統環境進行一些設置,代碼裡面的注釋已經寫
地很清楚了。
接下來是把dedecms的系統配置參數文件包含進來:require_once(DEDEDATA."/config.cache.inc.php");
看文件名字,我們猜測這個配置文件可能是數據庫裡面的配置信息的緩存。
接下來加載了數據庫配置信息文件:require_once(DEDEDATA.'/common.inc.php');
這個文件,不是根據數據庫中信息生成的緩存,而是dedecms安裝的時候生成的。前一篇文章我們說過
index.php 文件開始,檢測dedecms是否安裝,就是看這個文件是否存在的。
再接下來,整理了很多目錄,比如:站點根目錄、模板目錄,插件目錄、數據目錄等,還整理了很多變
量。最後加載了數據庫操作類dedesql.class.php和常用函數文件common.func.php
4
嗯,common.inc.php的謎底揭開,裡面沒什麼好玩的東西啦,都是最基本的東西。
接下來我們就得看看arc.partview.class.php吧,這裡面可是dedecms關鍵呢
加載了channelunit.class.php,typelink.class.php,ftp.class.php
下面就是partview類的定義啦
因為index.php 中使用partview類的SetTemplet方法和SaveToHtml方法,所以,我們為了能更簡單地深
入,就從這兩個方法著手。
我們先看看partview的構造函數。
創建了一個DedeTagParse類的實例,看名字是標簽解析類哦。然後設置了幾個參數。
接下來,new了一個TypeLink 類,設置了一堆參數。搞的很雲裡霧裡的。
看看 SetTemplet吧。啊,這個還算簡單。
先,使用DedeTagParse類的LoadTemplet方法載入模板。
再,設置一些Fields數組的元素
最後,調用ParseTemplet方法。
ParseTemplet方法裡面弄了一堆$GLOBALS 數組的元素,然後調用了MakeOneTag函數。費解啊
再看看SaveToHtml方法吧,前面就是建目錄,最後用DedeTagParse的SaveTo 方法保存到文件。
呃~不給力啊。
只能回頭想想,都看到了什麼~
嗯,為了能生成首頁,搞了個很搞不懂的partview類,然後裡面調用了貌似萬能的DedeTagParse方法,解析模
板,生成靜態文件。
僅此而已。
裡面還夾雜了其他函數和類,但不管怎麼樣這個DedeTagParse是重點,下次得重點分析了。
今天就到這吧,鳥兒的~太亂了,一點章法都沒有~
sourcejoy 之dedecms代碼研究(4)繼續徘徊partview
之前,我們像掉進沼澤一樣,看到無盡的變量,數組元素,莫名其面的東西擺在我們面前。今天,我們繼續艱難前行,想辦法走
出partview類的泥潭。
上一篇,我們胡亂分析了partview類,完全搞不懂干什麼的,裡面弄了一堆變量,最清晰的我們只是知道幾個生成首頁的關鍵地
方調用了DedeTagParse 類的LoadTemplet 方法和SaveTo 方法。而在partview 類定義的文件頭部,包含了幾個文件,我們就
避開partview,先來看看這幾個包含的文件吧。
require_once(DEDEINC.'/channelunit.class.php');
require_once(DEDEINC.'/typelink.class.php');
require_once(DEDEINC.'/ftp.class.php');
5
ftp.class.php,不用說,就是ftp 相關操作類吧,我們之前看partview 代碼的時候,了解到,在生成靜態文件的時候,使用了ftp
相關方法,貌似就是可以遠程寫文件滴。至於怎麼操作FTP 的,其實就是封裝了php 函數庫中ftp 開頭的相關函數而已,代碼很
簡單,不說了。
typelink.class.php,我們也在partview 代碼裡面見過的,打開看看吧。大概看了一下代碼,裡面是type 的鏈接相關的東西,每
個方法都聲稱一個指定type 的鏈接html 字符串。
其實,我有點隱約感覺到在dedecms 中,type 就是指欄目,不知道是不是這樣。
再來看看channelunit.class.php 吧。
裡面是 ChannelUnit 類的定義,而且我們發現,這個ChannelUnit 類沒有被使用過。所以先不去看他。我們注意到,這裡面還加
載了兩個文件:
require_once(DEDEINC."/dedetag.class.php");
require_once(DEDEINC."/channelunit.func.php");
dedetag.class.php,打開看看,嗯,很復雜,但我們發現用於解析模板和生成文件的DedeTagParse 類在裡面,呵呵,先記住,
以後慢慢研究。
channelunit.func.php 裡面都什麼函數呢?打開一看,嗯,一堆變量,幾個獲取這種信息的函數,在我們浏覽過程中,發現了兩
個函數:MfTemplet 和MakeOneTag
我們知道,在index.php 中就用了MfTemplet 函數,回頭打開index.php 看看怎麼調用的:
$row['templet'] = MfTemplet($row['templet']);
我們之前了解過,$row['templet']保存的是default/index.htm 這個值,就是模板文件路徑。接下來我們看看MfTemplet 函數都做
了什麼吧。
//模板目錄規則
function MfTemplet($tmpdir)
{
$tmpdir = str_replace("{style}",$GLOBALS['cfg_df_style'],$tmpdir);
$tmpdir = ereg_replace("/{1,}","/",$tmpdir);
return $tmpdir;
}
注釋裡面寫的是“模板目錄規則”,再看看代碼,哦,仿佛明白了一點兒了,就是替換模板路徑裡面的{style}為全局變量
$GLOBALS['cfg_df_style']中的值。應該跟使用不同模板套系有關吧。意義不是很大就不再繼續研究了。
我們看另一個函數MakeOneTag,這個在partview類的ParseTemplet 方法中,此方法看名字就是解析模板,而方法的大部分代
碼都是在處理變量,看不大懂干什麼的,最後一句調用了MakeOneTag 函數。貌似主要解析模板就是靠這個函數了。使用如下:
MakeOneTag($this->dtp,$this);
第一個參數是DedeTagParse類的實力,第二個參數就是partview類實例的句柄啦。
我們看看channelunit.func.php 中這個函數是干什麼的吧。
嗯,只能大概看,因為好多東西,我們都不清楚啊,郁悶了。
這裡面遍歷了/include/taglib/下所有有lib 後綴的文件,並把文件路徑加入數組,然後對DedeTagParse 類的CTag 進行了遍歷,
6
由於我們沒有研究DedeTagParse 類,所以這塊暫時不懂呢,不過也算小有進展了。看來還得回到partview裡面去重新看看了。
構造函數沒什麼特別的,就是創建了DedeTagParse 類實例,進行了一些設置而已。我們知道index.php 創建partview實例後執
行了SetTemplet 方法,我們再看看SetTemplet 方法吧。
這裡面調用了DedeTagParse類實例的LoadTemplet 方法,看來我們就得從這裡入手,去抽絲剝繭啦。
分析不下去了~
留幾個疑問下次再說。
1)DedeTagParse 類LoadTemplet 方法說開去。
2)MakeOneTag 到底在搞什麼。
看來只有徹底先把DedeTagParse 類LoadTemplet 方法搞懂才能進一步啊,目前還是一頭霧水。
sourcejoy 之dedecms代碼研究(5)從DedeTagParse開始
前面,我們一直在dedecms 的外圍,被各種全局變量和各種調用所迷惑,我們抓住了一個關鍵的線索DedeTagParse 類,研究
明白它,就可以弄清楚很多東西了。
看看這個 NB 的DedeTagParse 類吧。
嗯,先看構造函數,沒什麼特別的,就是設置了一堆初始化參數。
接下來就找LoadTemplet 方法吧。
找到後,我們發現LoadTemplet 方法其實是指向LoadTemplate 方法的,無語啊,難道作者英文就差到此等地步?
看看那個LoadTemplate 方法吧。
裡面先用 SetDefault 方法設置了幾個初始變量:
$this->SourceString = '';
$this->CTags = '';
$this->Count=-1;
然後判斷模板文件是否存在。然後針對不同情況對$this->SourceString 賦值,並調用$this->ParseTemplet();方法。
這塊的代碼看出來,作者開發功力有待改進啊,都5.6 了,代碼重構還如此糟糕,唉~為什麼不能把$this->ParseTemplet();這句
放在if 外面呢?
文件不存在時候,很簡單,就是把“文件不存在”這句話放到$this->SourceString 中,然後調用$this->ParseTemplet();。
文件存在的時候,也很簡單,fgets 讀取文件內容(麻煩,為啥不用file_get_contents 呢),然後,又是一個if,通過
$this->LoadCache($filename)返回值判斷是否有緩存,如果返回true 說明讀取到緩存的模板了,就返回空字符串(怎麼可以這樣
呢?返回值也太不負責了吧),如果返回false 就調用$this->ParseTemplet();重新解析模板。
LoadTemplate 大致就是這些,無非是讀取模板文件內容,然後看是否有緩存,有就不解析模板,沒有就解析模板,僅此而已。
我們接下來看看$this->LoadCache 方法吧,找到方法定義的部分,呀喝,代碼還不少。
7
先是通過$this->IsCache 判斷是否允許緩存(這個屬性是在DedeTagParse 類實例化的時候設定的,跟dedecms 的系統配置中
是否加模板緩存的參數$cfg_tplcache 有關,這個在DedeTagParse 類的構造函數中有所體現,由於安裝dedecms 後,默認系統
配置為true,所以這裡默認就為true 啦),如果為false 的話,$this->LoadCache 就返回false 而不繼續向下走了,在LoadTemplate
方法中就會根據這個返回值來決定解析模板。
過了$this->IsCache 這關,程序繼續。下面就是找當前模板文件對應的緩存了。
dedecms 的文件緩存有點特別,我們找到模板緩存目錄(data/tplcache),觀察一下就會發現,很多名字有點相同的文件,仔細
看看還能找出點規律來。我們從代碼來印證一下吧。
上面說到LoadCache 方法中,我們過了$this->IsCache 這關,後面就是找模板文件了,我們看看後面的代碼:
$cdir = dirname($filename);
$cachedir = DEDEROOT.$cfg_tplcache_dir;
$ckfile = str_replace($cdir,'',$filename).substr(md5($filename),0,16).'.inc';
$ckfullfile = $cachedir.'/'.$ckfile;
$ckfullfile_t = $cachedir.'/'.$ckfile.'.txt';
前 3 句是拼緩存文件名字的,方法是取不帶目錄的模板文件名然後md5 進行hash,然後把hash 出來的字符串取前16個字符,
後面加上“inc”後綴就成了。
第 4 句是取得完整的緩存文件名。
第 5 距好像是另一個文件名字,就是在緩存文件名字後面再加個後綴“.txt”
上面就得到了兩個文件名字,但是我們不知道第二個文件名字干什麼用的,再繼續往下看咯啊的代碼吧。
$this->CacheFile = $ckfullfile;
$this->TempMkTime = filemtime($filename);
if(!file_exists($ckfullfile)||!file_exists($ckfullfile_t))
{
return false;
}
第 1 句就指定了當前模板的緩存文件
第 2 句讀取了文件的最後修改時間,設置了一個什麼時間的屬性,現在還不大明白。
接下來的if 語句就是如果找不到模板的兩個緩存文件(就是上面組合出來的兩個文件),就返回false 讓LoadTemplate 方法解
析模板去。
我們假設模板的緩存文件都有,繼續看代碼。下面代碼段寫了注釋,就是檢測模板最後更新時間,代碼很簡單,就是打開我們前
面說的那個$ckfullfile_t 變量指定的txt 文件,讀內容,然後把內容和緩存修改時間比較,原來.txt 文件是用來保存緩存文件的保存
時間的。如果時間不一致則就返回false 讓LoadTemplate 方法解析模板去。
我們假定緩存有效,那麼就可以繼續了。
緩存有效就會把緩存文件包含進來。
這塊就要根據緩存文件來具體分析了,所以,我們這裡假定載入的是index.htm 模板吧,在tplcache 裡面找到index.htm 開頭,
8
後綴為inc 的文件,打開。
我們這裡節選一部分:
$z[0]=Array("global","",236,264);
$z[0][4]['name']="cfg_soft_lang";
$z[1]=Array("global","",277,303);
$z[1][4]['name']="cfg_webname";
$z[2]=Array("global","",347,377);
$z[2][4]['name']="cfg_description";
$z[3]=Array("global","",414,441);
$z[3][4]['name']="cfg_keywords";
……
再回到我們的LoadCache 方法裡面。
前面說到include 了模板緩存文件,然後,下面的if 語句判斷的是緩存文件裡面的信息數組“$z”是否正常,如果正常就進行來
個foreach 循環,這個foreach 很重要。我們來看看代碼。
foreach($z as $k=>$v){
$this->Count++;
$ctag = new DedeTAg();
$ctag->CAttribute = new DedeAttribute();
$ctag->IsReplace = FALSE;
$ctag->TagName = $v[0];
$ctag->InnerText = $v[1];
$ctag->StartPos = $v[2];
$ctag->EndPos = $v[3];
$ctag->TagValue = '';
$ctag->TagID = $k;
if(isset($v[4]) && is_array($v[4])){
$i = 0;
foreach($v[4] as $k=>$v){
$ctag->CAttribute->Count++;
$ctag->CAttribute->Items[$k]=$v;
}
}
$this->CTags[$this->Count] = $ctag;
}
這是個遍歷緩存信息數組,然後每個$z數組的元素,都生成一個DedeTAg 對象,並把$z數組元素的一些信息賦給DedeTAg 對
象,我們經過看這段源代碼,發現,$z數組元素中,0 是標簽名稱(TagName),1 是內部文本(InnerText),2 是開始位置(StartPos),
3 是結束位置(EndPos)。新的DedeTag 對象的tagID 就是數組下標。
這裡面還有個循環,是循環$z 數組每個元素的第四個子元素。然後,然後把相關值賦到當前DedeTAg 對象的DedeAttribute 對
9
象中。
看到這裡,我們似乎明白點東西了。
1)tplcache 裡面存的並不是解析好的模板,而是一堆信息數組。
2)信息數組裡保存的都是一個模板頁裡面包含的所有標簽的信息。
3)上面的循環其實是把緩存裡面的標簽信息讀取並寫入DedeTAg 對象,然後保存到當前DedeParse 類的CTags 數組中,到目
前DedeParse 的實例得到了模板內容(在$this->SourceString 中),模板上所有標簽信息(在$this->CTags
中)。
好了,經過上面的一番操作,LoadCache 方法就這些了,緩存讀取完成,這樣就可以安心回到LoadTemplate 方法裡面去繼續分
析了。
sourcejoy 之dedecms代碼研究(6)ParseTemplet算法分析
國慶放假三天開始生病,病好了就開始瘋狂地忙碌,我暈,通過昨天的努力總算可以繼續更新了。今天講的是dedecms 最關鍵
的東西,模板分析啦。
先看看一個 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)呢?
10
我們看看我們能寫出的最短標簽:
{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];
11 http://www.pprar.com
}
這個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{
12
$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 語句根據內容字符串開始指示符判斷,分別讀取
13
內容字符串和屬性字符串。
個人認為,通過特殊標識符截字更快一些。
這裡面還有個問題就是,是否內容字符串開始是如何判斷的呢?
我們看看第一個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 方法,把當前模板的標簽信息保存到緩存文件或者叫中間信息文件。
模板分析就講完啦,這樣該有的信息就都有了,我們又可以回到LoadTemplate 方法繼續啦。
sourcejoy 之dedecms代碼研究(7)MakeOneTag
最近太忙了,忙得網站不能更新,書也好久沒看了。今天有空就繼續完成沒有完成的事業。今天就接上一篇文章繼續講dedecms
的靜態頁面生成過程。
簡單回顧一下,前面我們首頁動態文件index.php 調用arc.partview.class.php 來初始化首頁模板,arc.partview.class.php 通過
SetTemplet 調用DedeTagParse,在設置首頁模板的時候,解析了模板,並生成模板的緩存或者說序列化文件。雖然模板被解析
了,但是這種解析的只是把所有標簽在模板中的位置以及他們的參數記錄下來,並沒有填充數據,接下來估計就要干這些事情了。
我們回到partview的SetTemplet 方法,從$this->dtp->LoadTemplet($temp);這句往下看吧。
14
下面if($this->TypeID > 0)這個if 語句是設置當前頁面的“面包屑導航”和標題。如果你使用過dedecms,對dedecms系統的操
作比較熟悉,應該了解這個if裡面的$this->Fields['title']這種數組元素,在頁面模板中很多地方都是用了fields 數組呢。
設置了兩個變量,接下來就調用ParseTemplet 解析模板啦。
這個 ParseTemplet 很簡單,設置了一大堆全局變量,一看就知道了,頁面裡面用的什麼channelid,topid 之類的東西,有興趣
的朋友可以自己細致研究一下。我們注意到ParseTemplet方法最後一句,MakeOneTag($this->dtp,$this); 因為前面的東西都是
沒什麼實質性的,這個應該很關鍵了。找到這個函數看看吧。
先看看 MakeOneTag 函數的參數,前兩個參數都是引用傳遞的,第一個參數是傳入DedeTagParse 的實例,第二個參數是傳入
partview的實例。
開始用一個循環獲取系統所擁有的所有標簽列表
$dh = dir(DEDEINC.'/taglib');
while($filename = $dh->read()){
if(ereg("\.lib\.",$filename)){
$alltags[] = str_replace('.lib.php','',$filename);
}
}
$dh->Close();
代碼很簡單,就是遍歷/include/taglib 目錄,根據文件規則取文件名,形成一個所有標簽的數組。
接下來就是一個大的foreach 數組了,遍歷在partview 類中實例化並解析了當前模板的DedeTagParse 的實例的CTags 屬性,
我們通過前面對DedeTagParse 的分析知道CTags 實際上是模板中所有使用的標簽及其參數的集合而已,這裡就開始遍歷這些
模板上的標簽進行賦值,替換之類的啦。
我們繼續看這個foreach 都干了什麼。
先獲取循環中當前標簽的名字。
接下來是針對標簽名為field 的標簽的操作。
如果是field 標簽的時候,先獲取標簽的name 屬性,當名字是array的時候則通過DedeTagParse 的Assign 方法把partview的
Fields 數組賦給標簽名對應的值屬性(詳情看Assign 的代碼,由於比較簡單這裡略過)。如果標簽name 屬性不是array,則把
partview 的Fields 數組中指定名字的值賦給標簽對應的值。下面的else 是進行一些其他判斷也很簡單,然後field 標簽就解析完
了,這樣我們就知道了field 標簽就是相當於變量,此處就是給這些變量賦值,解析完之後,就繼續循環分析下個標簽了。
接下來的兩個if 是做標簽名字的兼容性了,我們看到arclist有一堆標簽名字~
再接下來就是看看模板中的標簽是否有對應的系統標簽了,如果有,就載入對應的文件,調用對應的函數,把函數返回值賦值給
模板標簽對應的值。
這樣,整個模板的標簽就完成賦值了。
至於系統中那些標簽(/include/taglib/下的那些標簽解析文件)其實就是一個函數,裡面讀取數據庫數據,組織數據,輸
出而已。
這節就到這裡了,主要講了分析好的模板標簽如何綁定最終數據的。其實比較簡單,因為模板分析的時候已經得到了當
前模板所使用的全部標簽,接下來只要遍歷標簽,調用不同的標簽函數,獲取函數返回數據就行了。
有些細心的朋友會發現,到這裡其實還沒有完,數據什麼的都得到了,接下來呢?是如何生成靜態文件的?
這就是下一節要講的東西啦。
15
附:dedecms(v5.6)系統目錄
a /* 生成HTML的目錄*/
|data /* 程序生成常用數據保存目錄*/
| |admin /* 保存系統後台常規配置,例如作者、快速導航、來源,以文本格式存放*/
| |backupdata /* 數據庫備份存放目錄,可以在系統後台設置處修改*/
| |cache /* 系統緩存*/
| |enums /* 聯動類別生成的緩存和js文件*/
| |js /* 欄目js調用生成的js文件*/
| |mark /* 圖片水印設置目錄*/
| |module /* 系統後台那些模塊安裝包存放的目錄,通常文件名稱加密過*/
| |rss /* 生成 RSSmap 存放的文件目錄*/
| |sessions /* 系統 sessions存放目錄,登陸後就會生成個session,目錄需可寫*/
| |textdata /* 文本數據,系統後台保存為文本數據存放目錄*/
| |tplcache /* 模板緩存目錄,這個緩存一般是那些動態頁*/
| |ziptmp /* 壓縮緩存目錄*/
|dede /* 系統後台管理目錄*/
| |img
| |inc
| |js
| |templets /* 系統後台的模板存放目錄*/
|images
| |js
| |swfupload
| | |images
| | |plugins
|include /* 系統核心類庫、函數存放目錄*/
| |calendar /* 日歷控件,就是日期編輯框跳出來的那個日歷框*/
| |code /* datalistcp動態分頁類的編碼語言包*/
| |data /* 系統核心設置的數據,比如詞庫默認驗證碼*/
| |dialog /* 系統對話框存放目錄,編輯器上面選擇的類似於縮略圖、插入圖片文件*
| | |img
| |FCKeditor /* 編輯器存放目錄,用的是開源編輯器FCK */
| | |editor
| | | |css
| | | | |images
| | | |dialog
| | | | |common
| | | | |fck_about
| | | | |fck_codes
| | | | |fck_flash
| | | | |fck_image
| | | | |fck_link
16
| | | |dtd
| | | |images
| | | | |smiley
| | | |js
| | | |lang
| | | |skins
| | | | |images
| |inc /* 這裡存放一些用才引入的比較長的函數*/
| |taglib /* 就是那些用在模板中的標簽存放的目錄了*/
| | |channel /* 解析一些特殊字段的擴展函數庫*/
| | |help /* 基本的標簽說明*/
| |tpllib /* 動態模板標簽,一般用在動態調用頁面上面的標簽*/
|install /* 安裝目錄*/
| |images
| |templates
|member /* 會員中心目錄*/
| |images
| | |pay
| |inc /* 會員中心*/
| |js
| |paycenter /* 支付接口配置*/
| | |alipay
| | |cbpayment
| | |nps
| | |tenpay
| | |yeepay
| |space /* 會員中心的空間模板*/
| | |coffee
| | | |images
| | |company
| | | |images
| | |flower
| | | |images
| | |gray
| | | |images
| | |lxblog
| | | |images
| | |lxbrown
| | | |images
| | |lxvista
| | | |images
| | |person
| | | |blue
| | | |common
17
| | | | |css
| | | | |images
| | | |images
| | |pwblue
| | | |images
| | |pwglight
| | | |images
| | |pwlake
| | | |images
| | |pwpink
| | | |images
| |templets
|plus /* 系統插件存放目錄*/
| |guestbook /* 留言板插件*/
| | |images
| |img
| | |face
| |paycenter
| | |alipay
| | |cbpayment
| | |nps
| | |tenpay
| | |yeepay
| |task
|special /* 專題存放目錄*/
|templets /* 織夢模板存放目錄*/
| |default /* 系統默認模板目錄*/
| |images
| | |mood
| | |photo
| |js
| |style /* 默認模板存放的CSS樣式*/
| |plus /* 插件頁面的模板文件存放目錄*/
| |system /* 系統核心底層模板文件夾*/
| |wap /* 那個wap模塊的樣式目錄了*/
|uploads /* 文件上傳存放目錄*/
| |allimg /* 圖片*/
| |flink /* 友情鏈接圖片*/
| |litimg /* 縮略圖*/
| |media /* 多媒體存放目錄*/
| |soft /* 軟件*/
| |userup /* 用戶文件存放,例如頭像*/

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved