dedecms核心類源碼分析
最近公司一個cms類型的項目,時間緊任務重。經過快速的決策后,選擇了dedecms開發1.0版本,滿足基本需求。以前從來沒有接觸過這個系統,而且此系統文檔是相當的不全。所以分析源代碼是最好的方式。學習一個系統,首要的是搞懂它的數據引擎、模板引擎。dedetag.class.php 這個文件就是核心底層模板解析引擎,其它的引擎都是在這個基礎上增加了自己的函數而已。
一、dedetag.class.php
1.DedeTag :最后存儲的CTags的結構類,提供一些讀取CTags屬性的方法。(這個很重要)
2.DedeAttribute : 最后存儲的CTags->attr 的屬性結構類(count,Items)
3.DedeAttributeParse: 屬性解析器,解析提供屬性結構到CTags變量。
4.DedeTagParse :核心解析器
1 1.LoadTemplate 載入模板。 2 1. while($line = fgets($fp,1024)) //line 379循環讀取文件賦值到變量 $this->SourceString .= $line; 3 2. if($this->LoadCache($filename))//line 384在有緩存的情況,讀取緩存文件到 CTags 4 3. $this->ParseTemplet();//line 390 沒有緩存則調用模板解析方法 5 2.ParseTemplet 解析模板 6 1.循環遍歷,按正則匹配每個標簽的位子和屬性,並保存到 CTags 7 2. $this->SaveCache(); //line 952 ,如果開啟緩存配置則保存到緩存文件 8 3.GetResult 輸出模板 9 1. $this->AssignSysTag();// line 738 處理特殊標記(global,include,foreach,var,runphp)(global模板,能處理模板上的函數,和10步一樣) 10 4.AssignSysTag 處理特殊標記(global,include,foreach,var,runphp)(global模板,能處理模板上的函數,和10步一樣) 11 5.Assign 封裝模板的類(arclist/channel/memeberlist.....)和字段的解析是channelunit.helper.php中MakeOneTag函數在處理, 12 $dtp->Assign($tagid,$funcname($ctag,$refObj)); // line 558 調動地底層模板解析引擎的賦值函數為便簽賦值。
二、arc.partview.class.php 分析
1 index.php調用arc.partview.class.php 2 1.$this->dtp = new DedeTagParse();//line 47 初始化底層模板解析引擎 3 2.$this->SetTemplet();//line 134 讀取要解析的模板 4 3.$this->dtp->LoadTemplet($temp); //line 142 實際調用的是底層模板解析引擎 5 4.$this->ParseTemplet(); //line 149 封裝的解析模板方法 6 5.MakeOneTag($this->dtp,$this); //line 224 這個函數放在 channelunit.func.php 文件中, 7 6.helper('channelunit');// line 29 調用helper工具加載對應的函數。channelunit.helper.php 8 7.if($tagname=='field' && $parfield=='Y') //line 515 解析field 模板 9 8.$funcname = 'lib_'.$tagname; //line 557 加載對應的封裝模板的類(arclist/channel/memeberlist.....) 10 9.$dtp->Assign($tagid,$funcname($ctag,$refObj)); // line 558 調動地底層模板解析引擎的賦值函數為便簽賦值。 11 10.$this->$dtp->EvalFunc($fieldvalue,$functionname,&$refObj); //line 965 動地底層模板解析引擎處理某字段的函數(模板中直接使用函數最終也是調用的此方法加載對應的函數返回值) 12 11.$this->dtp->GetResult();//line 169 調動地底層模板解析引擎的輸出方法 13 12.$this->dtp->AssignSysTag(); //line 738 調動地底層模板解析引擎處理特殊標記(global,include,foreach,var,runphp)(global模板,能處理模板上的函數,和10步一樣) 14 13.$this->dtp->CTags[$i]->TagValue = $str; 每一次解析模板賦值都會修改CTags的TagValue 15 16 總結:封面模板解析類是對 dedetag.class.php 的封裝。加入了一些自己的處理邏輯
三、寫自己的模板解析

1 <?php 2 require_once(dirname(__FILE__)."/../include/common.inc.php"); 3 require_once(DEDEINC.'/channelunit.class.php'); 4 require_once(DEDEINC.'/typelink.class.php'); 5 require_once(DEDEINC.'/ftp.class.php'); 6 /** 7 * 模板調用事例 8 * 9 * @version 2018-11-01 10 * @package 1.0.1 11 * @author main_wu 12 */ 13 class TestView 14 { 15 public $dsql; 16 public $dtp; 17 public $TypeID; 18 public $Fields; 19 public $TypeLink; 20 public $pvCopy; 21 public $refObj; 22 public $ftp; 23 public $remoteDir; 24 25 public function __construct($typeid=0, $needtypelink=true) 26 { 27 global $_sys_globals,$ftp; 28 $this->TypeID = $typeid; 29 $this->dsql = $GLOBALS['dsql']; 30 $this->dtp = new DedeTagParse(); 31 $this->dtp->SetNameSpace("dede", "{", "}"); 32 $this->dtp->SetRefObj($this); 33 $this->ftp = &$ftp; 34 $this->remoteDir = ''; 35 //加載模板->解析模板 36 $this->dtp->LoadTemplet(DEDETEMPLATE ."/default/test.htm"); 37 38 //字段,通過{dede:filed.myname}訪問到。在display時,由$this->dtp->AssignSysTag()處理 39 //如果需要面包屑導航等信息,則要new TypeLink,原理也是往Fields中在賦值 40 if ($needtypelink) { 41 $this->TypeLink = new TypeLink($typeid); 42 if (is_array($this->TypeLink->TypeInfos)) { 43 foreach ($this->TypeLink->TypeInfos as $k=>$v) { 44 if (preg_match("/[^0-9]/", $k)) { 45 $this->Fields[$k] = $v; 46 } 47 } 48 } 49 $_sys_globals['curfile'] = 'partview'; 50 $_sys_globals['typename'] = $this->Fields['typename']; 51 52 //設置環境變量 53 SetSysEnv($this->TypeID, $this->Fields['typename'], 0, '', 'partview'); 54 $this->Fields['position'] = $this->TypeLink->GetPositionLink(true); 55 $this->Fields['title'] = $this->TypeLink->GetPositionLink(false); 56 } 57 $this->Fields['myname']="mian_wu"; 58 //全局變量,可以通過{dede:globel.testlist}訪問到。在display時,由$this->dtp->AssignSysTag()處理 59 $GLOBALS['testlist']=array(1,2,3,4,5); 60 61 62 //解析框架自定義模板,arclist/channel/memeberlist..... 63 MakeOneTag($this->dtp, $this); 64 //解析自己定義模板,沒有自定義則此代碼不需要。 65 foreach ($this->dtp->CTags as $tid => $ctag) { 66 if ($ctag->GetName()=="myfield") { 67 //自定義單標簽 68 //$this->assgin() //去解析自定義模板 69 $this->dtp->Assign($tid, $this->Fields[$ctag->GetAtt('name')]); 70 } elseif ($ctag->GetName()=="mytag") { 71 //自定義復合標簽 72 //自定義模板解析的兩種方式 73 //1.可以寫在taglib/tagName.lib.php的形式,模板會自動加載並解析。 74 //2.直接在CTags里面通過名字匹配到id,並assin進入值。 75 $this->dtp->Assign($tid, $this->Mytag($ctag->GetInnerText())); 76 } 77 } 78 } 79 public function Mytag($innertext) 80 { 81 //重新初始化底層解析模板。 82 $dtp2 = new DedeTagParse(); 83 $dtp2->SetNameSpace('field', '[', ']'); 84 $dtp2->LoadSource($innertext); 85 foreach ($dtp2->CTags as $k=>$ctag) { 86 if ($ctag->GetName()=='myname') { 87 //此處賦的值,自行處理 88 $dtp2->Assign($k, "mian_wu"); 89 } 90 } 91 return $dtp2->GetResult(); 92 } 93 public function Display() 94 { 95 //底層調用了$this->dtp->AssignSysTag();(global,include,foreach,var,runphp) 96 $this->dtp->Display(); 97 } 98 } 99 100 $a=new TestView(2); 101 $a->Display();

1 <html lang="en"> 2 <head> 3 <meta charset="UTF-8"> 4 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 5 <meta http-equiv="X-UA-Compatible" content="ie=edge"> 6 <title>{dede:global.cfg_webname/}</title> 7 </head> 8 <body> 9 {dede:foreach array="testlist"} 10 [field:key /]- [field:value /] 11 {/dede:foreach} 12 13 <div> 14 {dede:arclist limit='0,4' flag="p"} 15 <div class='d1arc'><a href="[field:arcurl/]">[field:title/]</a></div> 16 {/dede:arclist} </div> 17 </div> 18 {dede:field.myname /} 19 {dede:mytag} 20 [field:myname/] 21 {/dede:mytag} 22 </body> 23 </html>
四、數據庫操作分析
1 common.inc.php 核心類分析 2 1.定義全局變量 3 2.過濾請求參數 4 3.加載數據庫配置 require_once(DEDEDATA.'/common.inc.php');//line 152 5 4.設置模板目錄變量 6 5.分頁參數的設置 7 6.自動加載類庫處理 8 7.引入數據庫類 9 8.全局常用函數 require_once(DEDEINC.'/common.func.php'); //在line 368,做了兼容處理,擴展的函數可以直接寫在extend.func.php中。 11 9.路由處理,請求分發 12 10.載入小助手配置 13 dedesqli.class.php
$sql="xxxx";
$dsql->Execute('name', $sql);
while ($row = $dsql->GetObject('name')) {
$arr[] = $row;
}
14 1.$dsql = $dsqli = $db = new DedeSqli(FALSE); //line 21 在工程所有文件中均不需要單獨初始化這個類,可直接用 $dsql 或 $db 進行操作 15 2.SetQuery($sql) :會自動把SQL語句里的#@__替換為$this->dbPrefix(在配置文件中為$cfg_dbprefix)。並設置$this->queryString 16 3.Execute($id="me", $sql='') //如果sql不為空,內部會調用SetQuery,替換前綴。 18 $this->result[$id] = mysqli_query($this->linkID, $this->queryString); //line 315 執行sql返回執行結果。 19 4.GetOne($sql='',$acctype=MYSQLI_ASSOC) 20 if(!preg_match("/LIMIT/i",$sql)) $this->SetQuery(preg_replace("/[,;]$/i", '', trim($sql))." LIMIT 0,1;");//line 351 //內部通過正則加上了limit 0,1 21 $this->Execute("one"); //line 354 調用Execute 22 5.GetArray和GetObject
function GetObject($id="me")
{
if($this->result[$id]===0)
{
return FALSE;
}
else
{
return mysqli_fetch_object($this->result[$id]);
}
}
23 6.ExecuteNoneQuery($sql='')執行一個不返回結果的SQL語句,如update,delete,insert等
五、總結
dedecms 就兩個核心類 dedetag.class.php、common.inc.php。一行行讀源碼,就可以知道它得工作原理。二次開發時,需要哪部分就找到對應的源碼進行分析。其它的就應該沒什么困難了。