新鲜出炉,昨天周末写的 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
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<?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.如果标签没有闭合,提示错误
--------------