美文网首页
dedetag.class.php模板解析和属性解析,源码分析

dedetag.class.php模板解析和属性解析,源码分析

作者: 亚讯 | 来源:发表于2019-05-15 15:38 被阅读0次

模板解析:

 /**
     *  解析模板
     *
     * @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

相关文章

网友评论

      本文标题:dedetag.class.php模板解析和属性解析,源码分析

      本文链接:https://www.haomeiwen.com/subject/brnpaqtx.html