新鲜出炉,昨天周末写的 php版getElementsByTagName模拟javascript的getElementsByTagName , 根据标签名获取 标签列表.(多层嵌套也正常匹配)

   以下凑足200字....

     /*
    * 功能: php版getElementsByTag , 根据标签名获取 标签列表.(多层嵌套也正常匹配)
    * 参数:
            $str 被查找的字符串;
            $start_tag 开始标记;
            $close_tag 关闭标记;

            [可选] $tag_slashe 加在 标记 之前, 用于转义的字符(目的是防止混淆正常字符和标记), 默认false;
            [可选] $begin_pos , 用于指定开始查找的位置, 默认0 . 该参数在模仿类似js的语法: obj.getElementsByTagName() , 查找指定对象之下的标签列表时非常有用处;
            [可选] $end_pos , 用于指定结束位置, 默认0 .

    * 返回: 查找成功返回数组列表, 查找没有结果则返回false.
    * 作者: 王奇疏
    */  

  1 <?php
  2     echo '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />';
  3 
  4     /*
  5     * 功能: php版getElementsByTag , 根据标签名获取 标签列表.(多层嵌套也正常匹配)
  6     * 参数: 
  7             $str 被查找的字符串; 
  8             $start_tag 开始标记; 
  9             $close_tag 关闭标记; 
 10 
 11             [可选] $tag_slashe 加在 标记 之前, 用于转义的字符(目的是防止混淆正常字符和标记), 默认false; 
 12             [可选] $begin_pos , 用于指定开始查找的位置, 默认0 . 该参数在模仿类似js的语法: obj.getElementsByTagName() , 查找指定对象之下的标签列表时非常有用处;
 13             [可选] $end_pos , 用于指定结束位置, 默认0 . 
 14 
 15     * 返回: 查找成功返回数组列表, 查找没有结果则返回false.
 16     * 作者: 王奇疏
 17     */
 18     function getElementsByTag( $str , $start_tag , $close_tag , $tag_slashe=false , $begin_pos=0 , $end_pos=0 ) {
 19             $list = array(  );
 20             
 21             $start = $begin_pos;// 临时存储字符位置
 22             $end = $end_pos;
 23             $start_pos = $close_pos = 0; // 每一对标记的起止位置
 24             
 25             $stack = array(); // 一个数组, 用来借助栈的作用保存上一次循环的数据.    
 26             
 27             $s_len = strlen( $start_tag ); // 标记本身的长度
 28             $c_len = strlen( $close_tag );
 29             
 30             $slashe_ord = ord( $tag_slashe ); // 转义符
 31     
 32             while( false !== ( $start = stripos( $str , $start_tag , $start ) ) ) {
 33 
 34                 $i = 0; // 标记 计数器 
 35                 $j = 1024; // 最大循环计数器 , 防死循环
 36 
 37                 $start_pos = $start; // 初始化每对标记的起止位置
 38                 $close_pos = $start_pos + $s_len; // (close_tag的开始位置应在start_tag之后)
 39                 $stack = array();        
 40                 
 41                 while ( $j > 0 ){
 42                     // 一次搜索两种标记: $start_tag , $close_tag
 43                     if ( $start_pos > $start ) {
 44                         $start_pos = stripos( $str , $start_tag , $start_pos );
 45                     }
 46                     $close_pos = stripos( $str , $close_tag , $close_pos );
 47                     
 48                     // 如果 找到的标记 的前面一个字符是转义符 , 则再重新搜索一次.
 49                     if ( false !== $start_pos && $slashe_ord === ord( $str[ $start_pos - 1 ] ) ) {
 50                         $start_pos = stripos( $str , $start_tag , $start_pos + $s_len );
 51                     }
 52                     
 53                     if ( false !== $close_pos && $slashe_ord === ord( $str[ $close_pos - 1 ] ) ) {
 54                         $close_pos = stripos( $str , $close_tag , $close_pos + $c_len );
 55                     }
 56                     
 57                     // 把 关闭标记的位置 存进栈内, 保持只存2条.
 58                     if ( $j === 1024 ) { 
 59                         $stack[] = $close_pos; // (第1次多存1条) 
 60                     }
 61                     $stack[] = $close_pos;
 62 
 63                     // 开始标记 大于 上一个关闭标记,  
 64                     if ( $start_pos > ( $prev = array_shift( $stack ) ) ) {
 65                         $prev += $c_len;
 66                         break;
 67                     }
 68                     // 找不到开始标签时, 从哪开头?
 69                     elseif ( false === $start_pos ) {
 70                         $prev += $c_len;
 71                         break;
 72                     }
 73                     // 找不到闭合的标签时, 从哪开头?怎么处理?
 74                     elseif ( false === $close_pos ) {
 75                         show_match_error( $str , $start , $start_tag , $close_tag  );
 76                         return false;
 77                     }
 78                     else {
 79                         $start_pos += $s_len;
 80                         $close_pos += $c_len;
 81                     }
 82                     
 83                     --$j;
 84                 }
 85 
 86                 if ( $j == 0 ) {
 87                     show_match_error( $str , $start , $start_tag , $close_tag  );
 88                     return false;
 89                 }
 90                 
 91                 $list[] = substr( $str , $start , $prev - $start );
 92                 $start = $prev;
 93 
 94             }
 95             return $list;
 96     }
 97     
 98     // 仅仅用于显示匹配错误信息的函数
 99     function show_match_error( $str , $sub_start , $start_tag , $close_tag ) {
100         $count_line = substr( $str , 0 , $sub_start );
101         $count_line = substr_count( $count_line , PHP_EOL ) + 1;
102 
103         trigger_error( '<div style="padding: 10px;font-family:tahoma;font-size:12px; border:1px solid #c1c1c1; "><strong>出现标签未闭合的错误:</strong><br /><br />程序设定的<br />开始标记是: <font color="red">'.htmlspecialchars( "{$start_tag} " ).'</font> <br />闭合标记是: <font color="red">'.htmlspecialchars( "{$close_tag} " ).'</font> <br /><font color="red"><br />现检查到有一个标记没有闭合,不符合xhtml规范,已经停止匹配.</font><br /><br />未闭合标记的位置在原文中从<font color="red">第'.$count_line.'行</font>开始 , 在周围缺少闭合标签. <br />(<font color="red">第'.$count_line.'行</font>)大约是从以下字符开始, 请查看您原来的数据,检查标记是否闭合完整: <br />"<pre>'.htmlspecialchars( substr( $str , $sub_start , 80 ) ).'</pre> "</div>' ); 
104     }
105     

 



测试数据
<?php

    // 测试数据:-----------
    $str = <<<EOF
        <html>
        <head>
            <title> 测试模板文件 </title>
            <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        </head>

        <body>
            <br />
            1.变量:<br />
            $title.<br />
            \$title2这里的变量对$标记使用了转义符号\$,所以不会被当成php变量\$title2来解释<br /><br />

            1.2<br />
            链式变量:$shop->getobj()->show(2)<br /><br />

            2.分支语句<br />
            欢迎~if( $user == 'admin' ){  管理\~\}员hhhhhhhhhhhhh}else{  : echo $user; }~登陆<br /><br /><br />
            ~if( $user == 'admin' ){ 
                : 
                echo 'set first char to :   ,  then you can  write php syntax here.';
                var_dump( 'hello php syntax.' );

                if( $ty ){
                    echo 'good.........';
                }
                else{
                    echo 'bad..........';
                }

                if( $ty ){
                    echo 'good.........';
                }
                else{
                    echo 'bad..........';
                }


                if( $ty ){
                    echo 'good.........';


                if( $ty ){
                    echo 'good.........';
                }
                else{
                    echo 'bad..........';
                }



                }
                else{
                    echo 'bad..........';
                }


            }
            else(){
                no : ,  you can  write  html here .
                \}
            }
            ~<br /><br /><br />
            
            3.函数标签<br />
            直接调用函数标签,作用是输出函数:<var_dump( 'abc' )><br /><br />
            
            4.循环标签.<br />
            <loop $array $i=0><br />
                ·[<a href="#">$sort</a>] <a href="#author">$user </a> : $title. <img src="./yes.jpg" alt="图文提示" /> - $time<br />
            </loop><br /><br />
        <li>
            <li>
                <li>
                </li>
            </li>
        </li>
        <li>
            <li>
            </li>
        </li>


        <div id="test">
            <div>

                <div>
                    aaaaa
                </div>

                <div>
                    <div>
                        <div>
                            <div>
                                bbb    <div>
                                .ccc.
                                        </div>
                            </div>
                        </div>
                    </div>
                </div>

                <div>
                    dddddd
                </div>

                <div>
                    eeeeeeeeee
                </div>
            </div>

            <div>
                ffff
            </div>
            ggggggggggggg
        </div>

        <div>h</div>
        <div>i</div>

        <div></div>
        <div>
        </div>


        <div>1</div>
        <div>2</div>

        <div>
        </div>


EOF;
    
    $test = getElementsByTag( $str , '<div' , '</div>' , '\\' );
    // $test = getElementsByTag( $str , '{' , '}' , '\\' );
    // $test = getElementsByTag( $str , '~if' , '~' , '\\' );
    // $test = getElementsByTag( $str , '<li' , '</li>' , '\\' );
    echo '<pre>';print_r( $test );echo '</pre>';exit;


 如果标签正常闭合,就能正常正确地匹配节点,测试结果如下图:
--------------

 
2.如果标签没有闭合,提示错误
--------------

posted on 2013-05-06 16:25  王奇疏  阅读(2349)  评论(0编辑  收藏  举报