php实现html标签闭合检测与修复方法


Posted in PHP onJuly 09, 2015

本文实例讲述了php实现html标签闭合检测与修复方法。分享给大家供大家参考。具体如下:

html标签闭合检测与修复,说的有点大 , 并没有考虑的很完整,没有使用正则表达式, 适用于html文件中只有开始标签没有结束标签, 是有结束标签没有开始标签的情况。标签闭合的位置需要根据需求调整

<?php
$str = '
<div data="<li></li>">
  <img src="http://www.baidu.com/123123.png"/>
  <div2>
    <a>content</a>
  </div2>
    <ul>
      <li>
      </li>
    </ul>
    <p>
    content full
    </p>
    this is content</test1>
    this is content</test2>
    <test4 data="liujinjing"> This is cont
    <li></li>
    <test3 data="liujinjing"> This is content
<div3>
</div3>
</div4>
</div>
</div>
<div6 style="width:90px; "> this is content';
$str_len = strlen($str);
//记录起始标签
$pre_data = array();
//记录起始标签位置
$pre_pos = array();
$last_data = array();
$error_data = array();
$error_pos = array();
$i = 0;
//标记为 < 开始
$start_flag = false;
while( $i < $str_len ) {
  if($str[$i]=="<" && $str[$i+1]!='/' && $str[$i+1]!='!') {
    $i++;
    $_tmp_str = '';
    //标记为 < 开始
    $start_flag = true;
    //标记空白
    $space_flag = false;
    while($str[$i]!=">" && $str[$i]!="'" && $str[$i]!='"' && $str[$i] !='/' && $i<$str_len){
      if($str[$i]==' ') {
        $space_flag = true;
      }
      if(!$space_flag) {
        $_tmp_str .= $str[$i];  
      }
      $i++;
    }
    $pre_data[] = $_tmp_str;
    $pre_pos[] = $i;
  } else if ($str[$i]=="<" && $str[$i+1]=='/') {
    $i += 2;
    $_tmp_str = '';
    while($str[$i]!=">" && $i<$str_len){
      $_tmp_str .= $str[$i];
      $i++;
    }
    $last_data[] = $_tmp_str;
    //查看开始标签的上一个值
    if(count($pre_data)>0) {
      $last_pre_node = getLastNode($pre_data, 1);
      if($last_pre_node == $_tmp_str) {
        //配对上, 删除对应位置的值
        array_pop($pre_data);
        array_pop($pre_pos);
        array_pop($last_data);
      } else {
      //没有配对上, 有两种情况
        //情况一: 只有闭合标签, 没有开始标签
        //情况二:只有开始标签, 没有闭合标签
        array_pop($last_data);
        $error_data[] = $_tmp_str;
        $error_pos[] = $i;
      }
    } else {
        array_pop($last_data);
        $error_data[] = $_tmp_str;
        $error_pos[] = $i;      
    }
  }else if ($str[$i]=="<" && $str[$i+1]=="!") {
    $i++;
    while($i<$str_len) {
      if($str[$i]=="-" && $str[$i+1]=="-" && $str[$i+2]==">") {
        $i++;
        break;
      } else {
        $i++;
      }
    }
    $i++;
  }else if($str[$i]=='/' && $str[$i+1]=='>') {
    //跳过自动单个闭合标签
    if($start_flag) {
      array_pop($pre_data);
      array_pop($pre_pos);
      $i+=2;
    }
  }else if($str[$i]=="/" && $str[$i+1]=="*"){
    $i++;
    while($i<$str_len) {
      if($str[$i]=="*" && $str[$i+1]=="/") {
        $i++;
        break;
      } else {
        $i++;
    }
    $i++;
  }
  }else if($str[$i]=="'"){
    $i++;
    while($str[$i]!="'" && $i<$str_len) {
      $i++;
    }
    $i++;
  } else if($str[$i]=='"'){
    $i++;
    while($str[$i]!='"' && $i<$str_len ) {
      $i++;
    }
    $i++;
  } else {
    $i++;
  }
}
//确定起始标签的位置
function confirm_pre_pos($str, $pre_pos){
  $str_len = strlen($str);
  $j=$pre_pos;
  while($j < $str_len) {
    if($str[$j] == '"') {
      $j++;
      while ($j<$str_len) {
        if($str[$j]=='"') {
          $j++;
          break;
        }
        $j++;
      }
    }
    else if($str[$j] == "'") {
      $j++;
      while ($j<$str_len) {
        if($str[$j]=="'") {
          $j++;
          break;
        }
        $j++;
      }
    }
    else if($str[$j]==">") {
      $j++;
      while ($j<$str_len) {
        if($str[$j]=="<") {
          //退回到原有内容位置
          $j--;
          break;
        }
        $j++;
      }
      break;
    } 
    else {
      $j++;
    }
  }
  return $j;
}
//确定起始标签的位置
function confirm_err_pos($str, $err_pos){
  $j=$err_pos;
  $j--;
  while($j > 0) {
    if($str[$j] == '"') {
      $j--;
      while ($j<$str_len) {
        if($str[$j]=='"') {
          $j--;
          break;
        }
        $j--;
      }
    }
    else if($str[$j] == "'") {
      $j--;
      while ($j<$str_len) {
        if($str[$j]=="'") {
          $j--;
          break;
        }
        $j--;
      }
    }
    else if($str[$j]==">") {
      $j++;
      break;
    } 
    else {
      $j--;
    }
  }
  return $j;
}
//获取数组的倒数第num个值
function getLastNode(array $arr, $num){
  $len = count($arr);
  if($len > $num) {
    return $arr[$len-$num];
  } else {
    return $arr[0];
  }
}
//整理数据, 主要是向后看, 进一步进行检查
function sort_data(&$pre_data, &$pre_pos, &$error_data, &$error_pos){
  $rem_key_array = array();
  $rem_i_array = array();
  //获取需要删除的值
  foreach($error_data as $key=>$value){
    $count = count($pre_data);
    for($i=($count-1) ; $i>=0; $i--) {
      if($pre_data[$i] == $value && !in_array($i, $rem_i_array)) {
        $rem_key_array[] = $key;
        $rem_i_array[] = $i;
        break;
      }
    }
  }
  //删除起始标签相应的值
  foreach($rem_key_array as $_item) {
    unset($error_pos[$_item]);
    unset($error_data[$_item]);
  }
  //删除结束标签相应的值
  foreach($rem_i_array as $_item) {
    unset($pre_data[$_item]);
    unset($pre_pos[$_item]);
  }
}
//整理数据, 闭合标签
function modify_data($str, $pre_data, $pre_pos, $error_data, $error_pos){
  $move_log = array();
  //只有闭合标签的数据
  foreach ($error_data as $key => $value) {
    // code...
    $_tmp_move_count = 0;
    foreach ($move_log as $pos_key => $move_value) {
      // code...
      if($error_pos[$key]>=$pos_key) {
        $_tmp_move_count += $move_value;
      }
    }
    $data = insert_data($str, $value, $error_pos[$key]+$_tmp_move_count, false);
    $str = $data['str'];
    $move_log[$data['pos']] = $data['move_count'];
  }
  //只有起始标签的数据
  foreach ($pre_data as $key => $value) {
    // code...
    $_tmp_move_count = 0;
    foreach ($move_log as $pos_key => $move_value) {
      // code...
      if($pre_pos[$key]>=$pos_key) {
        $_tmp_move_count += $move_value;
      }
    }
    $data = insert_data($str, $value, $pre_pos[$key]+$_tmp_move_count, true);
    $str = $data['str'];
    $move_log[$data['pos']] = $data['move_count'];
  }
  return $str;
}
//插入数据, $type 表示插入数据的方式
function insert_data($str, $insert_data, $pos, $type) {
  $len = strlen($str);
  //起始标签类型
  if($type==true) {
    $move_count = strlen($insert_data)+3;
    $pos = confirm_pre_pos($str, $pos);
    $pre_str = substr($str, 0, $pos);
    $end_str = substr($str, $pos);
    $mid_str = "</" . $insert_data . ">";
  //闭合标签类型
  } else {
    $pos = confirm_err_pos($str, $pos);
    $move_count = strlen($insert_data) + 2;
    $pre_str = substr($str, 0, $pos);
    $end_str = substr($str, $pos);
    $mid_str = "<" . $insert_data . ">";
  }
  $str = $pre_str.$mid_str.$end_str;
  return array('str'=>$str, 'pos'=>$pos, 'move_count'=>$move_count);
}
sort_data($pre_data, $pre_pos, $error_data, $error_pos);
$new_str = modify_data($str, $pre_data, $pre_pos, $error_data, $error_pos);
echo $new_str;
// print_r($pre_data);
// print_r($pre_pos);
// print_r($error_data);
// print_r($error_pos);
// echo strlen($str);
// foreach($pre_pos as $value){
//   $value = confirm_pre_pos($str, $value);
//   for($i=$value-5; $i<=$value; $i++) {
//     echo $str[$i];
//   }
//   echo "\n";
// }
// foreach($error_pos as $value){
//   for($i=$value-5; $i<=$value; $i++) {
//     echo $str[$i];
//   }
//   echo "\n";
// }
?>

希望本文所述对大家的php程序设计有所帮助。

PHP 相关文章推荐
解析PHP自带的进位制之间的转换函数
Jun 08 PHP
Codeigniter注册登录代码示例
Jun 12 PHP
PHP获取MySql新增记录ID值的3种方法
Jun 24 PHP
常见php数据文件缓存类汇总
Dec 05 PHP
php的sso单点登录实现方法
Jan 08 PHP
详解PHP+AJAX无刷新分页实现方法
Nov 03 PHP
PHP读取大文件的多种方法介绍
Apr 04 PHP
全面了解PHP中的全局变量
Jun 17 PHP
PHP Trait代码复用类与多继承实现方法详解
Jun 17 PHP
php设计模式之工厂方法模式分析【星际争霸游戏案例】
Jan 23 PHP
TP5框架model常见操作示例小结【增删改查、聚合、时间戳、软删除等】
Apr 05 PHP
php将xml转化对象的实例详解
Nov 17 PHP
php可扩展的验证类实例(可对邮件、手机号、URL等验证)
Jul 09 #PHP
php类常量用法实例分析
Jul 09 #PHP
php求一个网段开始与结束IP地址的方法
Jul 09 #PHP
PHP实现全角字符转为半角方法汇总
Jul 09 #PHP
php将金额数字转化为中文大写
Jul 09 #PHP
php实现的简易扫雷游戏实例
Jul 09 #PHP
php链表用法实例分析
Jul 09 #PHP
You might like
深入了解php4(1)--回到未来
2006/10/09 PHP
PHP批量生成缩略图的代码
2008/07/19 PHP
php多文件上传功能实现原理及代码
2013/04/18 PHP
基于PHP开发中的安全防范知识详解
2013/06/06 PHP
PHP字符串的递增和递减示例介绍
2014/02/11 PHP
Nginx服务器上安装并配置PHPMyAdmin的教程
2015/08/18 PHP
laravel执行php artisan migrate报错的解决方法
2019/10/09 PHP
Yii使用DbTarget实现日志功能的示例代码
2020/07/21 PHP
JavaScript模板引擎用法实例
2015/07/10 Javascript
js实现带圆角的两级导航菜单效果代码
2015/08/24 Javascript
JS异步文件上传(兼容IE8+)
2017/04/02 Javascript
js异步编程小技巧详解
2017/08/14 Javascript
vue数字类型过滤器的示例代码
2017/09/07 Javascript
如何为你的JS项目添加智能提示与类型检查详解
2019/03/12 Javascript
typescript nodejs 依赖注入实现方法代码详解
2019/07/21 NodeJs
python通过索引遍历列表的方法
2015/05/04 Python
Python实现登陆文件验证方法
2018/10/06 Python
Python计算库numpy进行方差/标准方差/样本标准方差/协方差的计算
2018/12/28 Python
Python 等分切分数据及规则命名的实例代码
2019/08/16 Python
pandas中的数据去重处理的实现方法
2020/02/10 Python
python 实用工具状态机transitions
2020/11/21 Python
建筑专业自荐信范文
2014/01/05 职场文书
机电一体化应届生求职信范文
2014/01/24 职场文书
考博专家推荐信
2014/05/10 职场文书
孩子教育的心得体会
2014/09/01 职场文书
新闻学专业职业生涯规划范文:我的人生我做主
2014/09/12 职场文书
标准大学生职业生涯规划书写作指南
2014/09/18 职场文书
乡镇群众路线教育实践活动整改措施
2014/10/04 职场文书
2015年中秋节活动总结
2015/03/23 职场文书
婚礼迎宾词大全
2015/08/10 职场文书
2016年秋季运动会加油稿
2015/12/21 职场文书
Redis遍历所有key的两个命令(KEYS 和 SCAN)
2021/04/12 Redis
ConstraintValidator类如何实现自定义注解校验前端传参
2021/06/18 Java/Android
DjangoRestFramework 使用 simpleJWT 登陆认证完整记录
2021/06/22 Python
Mysql将字符串按照指定字符分割的正确方法
2022/05/30 MySQL
Python加密与解密模块hashlib与hmac
2022/06/05 Python