dedecms講解-dedetag.class.php模板解析和屬性解析,原始碼分析
阿新 • • 發佈:2019-01-25
其他相對簡單,這2個稍微複雜:
模板解析:
/** * 解析模板 * * @access public * @return string */ function ParseTemplet() { $TagStartWord = $this->TagStartWord; // 標籤開始定界符,{ $TagEndWord = $this->TagEndWord; // 標籤結束定界符,} $sPos = 0; $ePos = 0; $FullTagStartWord = $TagStartWord.$this->NameSpace.":"; // 標籤完整開始定界符,{dede: $sTagEndWord = $TagStartWord."/".$this->NameSpace.":"; // 塊狀標籤結束符,{/dede: $eTagEndWord = "/".$TagEndWord; // 單標籤結束符,/} $tsLen = strlen($FullTagStartWord); // 標籤完整的開始定界符長度 $sourceLen=strlen($this->SourceString); // 模板字串長度 if( $sourceLen <= ($tsLen + 3) ) // 模板字串長度至少比 '{dede:' 長度>3,因為:{dede:a/} - 這種是最短的了,不過好像可以=3啊 { return; } $cAtt = new DedeAttributeParse(); $cAtt->charToLow = $this->CharToLow; //遍歷模板字串,請取標記及其屬性資訊 for($i=0; $i < $sourceLen; $i++) { $tTagName = ''; //如果不進行此判斷,將無法識別相連的兩個標記 if($i-1 >= 0) { $ss = $i-1; } else { $ss = 0; } $sPos = strpos($this->SourceString,$FullTagStartWord,$ss); // 模板字串中查詢開始標記 $isTag = $sPos; // 是否能找到開始標記 /* 使用strpos(),當在第一個字元查詢到指定字元,返回的是 0,其實已經找到了,但是$isTag = 0。但是下方也使用的是 '==='(恆等) 來判斷,沒有必需再多這一步啊 */ if($i==0) { $headerTag = substr($this->SourceString,0,strlen($FullTagStartWord)); if($headerTag==$FullTagStartWord) { $isTag=TRUE; $sPos=0; } } if($isTag===FALSE) // 未檢視到開始標記,無標籤,解析完畢 { break; } //判斷是否已經到倒數第三個字元(可能性機率極小,取消此邏輯) /* if($sPos > ($sourceLen-$tsLen-3) ) { break; } */ /* 功能:"標籤名" */ // 從開始標記後一個字元開始查詢,最大長度不能超過規定的 TagMaxLen-標籤允許的最大長度(單標籤|塊標籤的開始塊) for($j=($sPos+$tsLen);$j<($sPos+$tsLen+$this->TagMaxLen);$j++) { if($j>($sourceLen-1)) // 已經查詢到模板字串的最後一位,跳出迴圈 { break; } // 匹配到 '/'、'空白' || 標籤結束定界符'}',跳出迴圈 else if( preg_match("/[\/ \t\r\n]/", $this->SourceString[$j]) || $this->SourceString[$j] == $this->TagEndWord ) { break; } // 其他字元,都賦值給 $tTagName,得到 '標籤名' else { $tTagName .= $this->SourceString[$j]; } } /* 匹配標籤的結束位置 */ // 匹配到標籤名,接著下面邏輯 if($tTagName!='') { $i = $sPos+$tsLen; $endPos = -1; $fullTagEndWordThis = $sTagEndWord.$tTagName.$TagEndWord; // 得到此標籤的 '塊標籤結束定界符',"{/dede:channel}" $e1 = strpos($this->SourceString,$eTagEndWord, $i); // 查詢單標籤結束定界符的首次出現位置,'/}' $e2 = strpos($this->SourceString,$FullTagStartWord, $i); // 查詢標籤開始定界符的首次出現位置,'{dede:' $e3 = strpos($this->SourceString,$fullTagEndWordThis,$i); // 查詢此標籤的 '塊標籤結束定界符'的首次出現位置,"{/dede:channel}" //$eTagEndWord = /} $FullTagStartWord = {tag: $fullTagEndWordThis = {/tag:xxx] - 作者也給出了形式 // 未找到對應的,設定為-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:' /* 針對3種情況的分析 */ // 1.剩餘模板字串中未找到 '塊標籤結束符',說明只能是 '單標籤',不可能出現巢狀標籤 if($e3==-1) { $endPos = $e1; // 得到 '單標籤' 結束位置 $elen = $endPos + strlen($eTagEndWord); // 得到結束長度 } //not found '/}' // 2.剩餘模板字串中未找到 '單標籤結束符',說明只能是 '塊標籤',此種情況可能出現標籤巢狀,但是也不允許,同名標籤的巢狀。直接獲取到整個標籤即可,不用考慮內部巢狀問題 else if($e1==-1) { $endPos = $e3; // 得到 '塊標籤' 結束位置 $elen = $endPos + strlen($fullTagEndWordThis); // 得到結束長度 } //found '/}' and found '{/dede:' // 3.剩餘模板字串中 '塊標籤結束符' 和 '單標籤結束符' 都匹配到,再分下面2種情況: else { //if '/}' more near '{dede:'、'{/dede:' , end tag is '/}', else is '{/dede:' // 1>'/}'比'{/dede:channel}'近,同時'/}'比'{dede:'近,就說明該標籤是 '單標籤'。(保證了'/}'不是新的標籤的結束符) if($e1 < $e2 && $e1 < $e3 ) { $endPos = $e1; $elen = $endPos + strlen($eTagEndWord); } // 2>其他情況,說明是 '塊標籤' else { $endPos = $e3; $elen = $endPos + strlen($fullTagEndWordThis); } } //not found end tag , error if($endPos==-1) { echo "Tag Character postion $sPos, '$tTagName' Error!<br />\r\n"; break; } $i = $elen; // 一個標籤匹配完畢,另$i指標指向標籤結束的後一位 $ePos = $endPos; //分析所找到的標記位置等資訊 $attStr = ''; $innerText = ''; $startInner = 0; 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]; // 獲取 '塊標籤' 的內部字串 } } //echo "<xmp>$attStr</xmp>\r\n"; // 傳入屬性字串,解析屬性字串 $cAtt->SetSource($attStr); if($cAtt->cAttributes->GetTagName()!='') { $this->Count++; // 標籤個數+1 // 例項化DedeTag標籤描述物件,設定一些必要的值 $CDTag = new DedeTag(); $CDTag->TagName = $cAtt->cAttributes->GetTagName(); $CDTag->StartPos = $sPos; $CDTag->EndPos = $i; $CDTag->CAttribute = $cAtt->cAttributes; $CDTag->IsReplace = FALSE; // 此時的替換為false,只是解析出來標籤,並未執行標籤,得通過Assign()。 $CDTag->TagID = $this->Count; $CDTag->InnerText = $innerText; // 最終,全部解析的標籤,記錄到 CTags 陣列中 $this->CTags[$this->Count] = $CDTag; } } else { $i = $sPos+$tsLen; break; } }//結束遍歷模板字串 // 設定了快取,可將解析後的 CTags 陣列,寫入到快取檔案中的 $z 陣列 if($this->IsCache) { $this->SaveCache(); } }
屬性解析:
function ParseAttribute() { $d = ''; $tmpatt = ''; $tmpvalue = ''; $startdd = -1; $ddtag = ''; $hasAttribute=FALSE; $strLen = strlen($this->sourceString); // 獲得Tag的名稱,解析到 cAtt->GetAtt('tagname') 中 for($i=0; $i<$strLen; $i++) { if($this->sourceString[$i]==' ') // 發現存在空格,表示有屬性,例如:{dede:channel row="2"} { $this->cAttributes->Count++; // count從-1變為0,開始計算屬性個數 $tmpvalues = explode('.', $tmpvalue); // 支援 '.' 分隔到name。例如:{dede:field.name ...} $this->cAttributes->Items['tagname'] = ($this->charToLow ? strtolower($tmpvalues[0]) : $tmpvalues[0]); // tagname屬性,新增到items陣列中 if(isset($tmpvalues[1]) && $tmpvalues[1]!='') // 如果有name屬性,也新增到items陣列中(name並未計入 count 計數) { $this->cAttributes->Items['name'] = $tmpvalues[1]; } $tmpvalue = ''; $hasAttribute = TRUE; // 標籤有屬性 break; } else { $tmpvalue .= $this->sourceString[$i]; } } //不存在屬性列表的情況 if(!$hasAttribute) { /* 不存在屬性,則僅解析出 'tagname' 和 'name'(如果有name的話),count+1(name不作為count計數) */ $this->cAttributes->Count++; $tmpvalues = explode('.', $tmpvalue); $this->cAttributes->Items['tagname'] = ($this->charToLow ? strtolower($tmpvalues[0]) : $tmpvalues[0]); if(isset($tmpvalues[1]) && $tmpvalues[1]!='') { $this->cAttributes->Items['name'] = $tmpvalues[1]; } return ; } $tmpvalue = ''; //如果字串含有屬性值,遍歷源字串,並獲得各屬性 for($i; $i<$strLen; $i++) { $d = $this->sourceString[$i]; //查詢屬性名稱 if($startdd==-1) // 第一步,準備獲取 '屬性名' { if($d != '=') { $tmpatt .= $d; } else { /* 匹配到 '=',得到 '屬性名' */ if($this->charToLow) { $tmpatt = strtolower(trim($tmpatt)); } else { $tmpatt = trim($tmpatt); } $startdd=0; } } //查詢屬性的限定標誌 else if($startdd==0) // 進入第二步,判斷屬性值的標誌符號 { switch($d) { case ' ': // '=' 後字串是 ' ',繼續下個字元 break; case '"': $ddtag = '"'; // =" 格式,表示屬性以 "" 包圍 $startdd = 1; break; case '\'': $ddtag = '\''; // =' 格式,表示屬性以 '' 包圍 $startdd = 1; break; default: $tmpvalue .= $d; // 支援屬性值不使用 ' 或 " $ddtag = ' '; // 此情況下,屬性的結束符就是 ' ',發現空格,表示屬性值就結束了(屬性值中不能出現空格) $startdd = 1; break; } } else if($startdd==1) // 進入第三步,獲取屬性值 { // 一旦第二步中的 '屬性結束符',說明屬性結束(這裡得注意個細節:結束符的前一位,不允許是 '\' 轉譯字元,轉譯字元說明i "當前結束符並非真正結束符") if($d==$ddtag && ( isset($this->sourceString[$i-1]) && $this->sourceString[$i-1]!="\\") ) { $this->cAttributes->Count++; // count+1 $this->cAttributes->Items[$tmpatt] = trim($tmpvalue); // 屬性新增到items陣列 // 重置幾個變數,開始下一個屬性的匹配! $tmpatt = ''; $tmpvalue = ''; $startdd = -1; } else { $tmpvalue .= $d; } } }//for /* 這裡也得注意下: 最後一個屬性的處理:當 {dede:channel row=2},此時,並未發現以 ' '結尾,上面的最後一步未匹配到,所以得在這裡做一次處理! 也支援了:{dede:channel row="2},這種錯誤寫法! */ //最後一個屬性的給值 if($tmpatt != '') { $this->cAttributes->Count++; $this->cAttributes->Items[$tmpatt] = trim($tmpvalue); } //print_r($this->cAttributes->Items); }// end func