模板解析:
/**
* 解析模板
*
* @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
网友评论