PHP 修复未正常关闭的HTML标签实现代码(支持嵌套和就近闭合)


Posted in PHP onJune 07, 2012

fixHtmlTag
version 0.2
这个版本解决了上次遗留的问题,即就近闭合和嵌套闭合问题。具体可以看代码的注释。

<?php /** 
* fixHtmlTag 
* 
* HTML标签修复函数,此函数可以修复未正确闭合的 HTML 标签 
* 
* 由于不确定性因素太多,暂时提供两种模式“嵌套闭合模式”和 
* “就近闭合模式”,应该够用了。 
* 
* 这两种模式是我为了解释清楚此函数的实现而创造的两个名词, 
* 只需明白什么意思就行。 
* 1,嵌套闭合模式,NEST,为默认的闭合方式。即 "<body><div>你好" 
* 这样的 html 代码会被修改为 "<body><div>你好</div></body>" 
* 2,就近闭合模式,CLOSE,这种模式会将形如 "<p>你好<p>为什么没有 
* 闭合呢" 的代码修改为 "<p>你好</p><p>为什么没有闭合呢</p>" 
* 
* 在嵌套闭合模式(默认,无需特殊传参)下,可以传入需要就近闭合的 
* 标签名,通过这种方式将类似 "<body><p>你好</p><p>我也好" 转换为 
* "<body><p>你好</p><p>我也好</p></body>"的形式。 
* 传参时索引需要按照如下方式写,不需要修改的设置可以省略 
* 
* $param = array( 
* 'html' => '', //必填 
* 'options' => array( 
* 'tagArray' => array(); 
* 'type' => 'NEST', 
* 'length' => null, 
* 'lowerTag' => TRUE, 
* 'XHtmlFix' => TRUE, 
* ) 
* ); 
* fixHtmlTag($param); 
* 
* 上面索引对应的值含义如下 
* string $html 需要修改的 html 代码 
* array $tagArray 当为嵌套模式时,需要就近闭合的标签数组 
* string $type 模式名,目前支持 NEST 和 CLOSE 两种模式,如果设置为 CLOSE,将会忽略参数 $tagArray 的设置,而全部就近闭合所有标签 
* ini $length 如果希望截断一定长度,可以在此赋值,此长度指的是字符串长度 
* bool $lowerTag 是否将代码中的标签全部转换为小写,默认为 TRUE 
* bool $XHtmlFix 是否处理不符合 XHTML 规范的标签,即将 <br> 转换为 <br /> 
* 
* @author IT不倒翁 <itbudaoweng@gmail.com> 
* @version 0.2 
* @link http://yungbo.com IT不倒翁 
* @link http://enenba.com/?post=19 某某 
* @param array $param 数组参数,需要赋予特定的索引 
* @return string $result 经过处理后的 html 代码 
* @since 2012-04-14 
*/ 
function fixHtmlTag($param = array()) { 
//参数的默认值 
$html = ''; 
$tagArray = array(); 
$type = 'NEST'; 
$length = null; 
$lowerTag = TRUE; 
$XHtmlFix = TRUE; 
//首先获取一维数组,即 $html 和 $options (如果提供了参数) 
extract($param); 
//如果存在 options,提取相关变量 
if (isset($options)) { 
extract($options); 
} 
$result = ''; //最终要返回的 html 代码 
$tagStack = array(); //标签栈,用 array_push() 和 array_pop() 模拟实现 
$contents = array(); //用来存放 html 标签 
$len = 0; //字符串的初始长度 
//设置闭合标记 $isClosed,默认为 TRUE, 如果需要就近闭合,成功匹配开始标签后其值为 false,成功闭合后为 true 
$isClosed = true; 
//将要处理的标签全部转为小写 
$tagArray = array_map('strtolower', $tagArray); 
//“合法”的单闭合标签 
$singleTagArray = array( 
'<meta', 
'<link', 
'<base', 
'<br', 
'<hr', 
'<input', 
'<img' 
); 
//校验匹配模式 $type,默认为 NEST 模式 
$type = strtoupper($type); 
if (!in_array($type, array('NEST', 'CLOSE'))) { 
$type = 'NEST'; 
} 
//以一对 < 和 > 为分隔符,将原 html 标签和标签内的字符串放到数组中 
$contents = preg_split("/(<[^>]+?>)/si", $html, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE); 
foreach ($contents as $tag) { 
if ('' == trim($tag)) { 
$result .= $tag; 
continue; 
} 
//匹配标准的单闭合标签,如<br /> 
if (preg_match("/<(\w+)[^\/>]*?\/>/si", $tag)) { 
$result .= $tag; 
continue; 
} 
//匹配开始标签,如果是单标签则出栈 
else if (preg_match("/<(\w+)[^\/>]*?>/si", $tag, $match)) { 
//如果上一个标签没有闭合,并且上一个标签属于就近闭合类型 
//则闭合之,上一个标签出栈 
//如果标签未闭合 
if (false === $isClosed) { 
//就近闭合模式,直接就近闭合所有的标签 
if ('CLOSE' == $type) { 
$result .= '</' . end($tagStack) . '>'; 
array_pop($tagStack); 
} 
//默认的嵌套模式,就近闭合参数提供的标签 
else { 
if (in_array(end($tagStack), $tagArray)) { 
$result .= '</' . end($tagStack) . '>'; 
array_pop($tagStack); 
} 
} 
} 
//如果参数 $lowerTag 为 TRUE 则将标签名转为小写 
$matchLower = $lowerTag == TRUE ? strtolower($match[1]) : $match[1]; 
$tag = str_replace('<' . $match[1], '<' . $matchLower, $tag); 
//开始新的标签组合 
$result .= $tag; 
array_push($tagStack, $matchLower); 
//如果属于约定的的单标签,则闭合之并出栈 
foreach ($singleTagArray as $singleTag) { 
if (stripos($tag, $singleTag) !== false) { 
if ($XHtmlFix == TRUE) { 
$tag = str_replace('>', ' />', $tag); 
} 
array_pop($tagStack); 
} 
} 
//就近闭合模式,状态变为未闭合 
if ('CLOSE' == $type) { 
$isClosed = false; 
} 
//默认的嵌套模式,如果标签位于提供的 $tagArray 里,状态改为未闭合 
else { 
if (in_array($matchLower, $tagArray)) { 
$isClosed = false; 
} 
} 
unset($matchLower); 
} 
//匹配闭合标签,如果合适则出栈 
else if (preg_match("/<\/(\w+)[^\/>]*?>/si", $tag, $match)) { 
//如果参数 $lowerTag 为 TRUE 则将标签名转为小写 
$matchLower = $lowerTag == TRUE ? strtolower($match[1]) : $match[1]; 
if (end($tagStack) == $matchLower) { 
$isClosed = true; //匹配完成,标签闭合 
$tag = str_replace('</' . $match[1], '</' . $matchLower, $tag); 
$result .= $tag; 
array_pop($tagStack); 
} 
unset($matchLower); 
} 
//匹配注释,直接连接 $result 
else if (preg_match("/<!--.*?-->/si", $tag)) { 
$result .= $tag; 
} 
//将字符串放入 $result ,顺便做下截断操作 
else { 
if (is_null($length) || $len + mb_strlen($tag) < $length) { 
$result .= $tag; 
$len += mb_strlen($tag); 
} else { 
$str = mb_substr($tag, 0, $length - $len + 1); 
$result .= $str; 
break; 
} 
} 
} 
//如果还有将栈内的未闭合的标签连接到 $result 
while (!empty($tagStack)) { 
$result .= '</' . array_pop($tagStack) . '>'; 
} 
return $result; 
}
PHP 相关文章推荐
PHP完整的日历类(CLASS)
Nov 27 PHP
php自动跳转中英文页面
Jul 29 PHP
php 字符过滤类,用于过滤各类用户输入的数据
May 27 PHP
第五章 php数组操作
Dec 30 PHP
php适配器模式介绍
Aug 14 PHP
CI框架源码阅读,系统常量文件constants.php的配置
Feb 28 PHP
php实现的Cookies操作类实例
Sep 24 PHP
Ubuntu下安装PHP的mongodb扩展操作命令
Jul 04 PHP
PHP的文件操作与算法实现的面试题示例
Aug 10 PHP
PHP面向对象编程之深入理解方法重载与方法覆盖(多态)
Dec 24 PHP
简单了解将WordPress中的工具栏移到底部的小技巧
Dec 31 PHP
Laravel 框架控制器 Controller原理与用法实例分析
Apr 14 PHP
PHP 文本文章分页代码 按标记或长度(不涉及数据库)
Jun 07 #PHP
PHP 查找字符串常用函数介绍
Jun 07 #PHP
php中通过curl smtp发送邮件
Jun 05 #PHP
Smarty的配置与高级缓存技术分享
Jun 05 #PHP
PHP Parse Error: syntax error, unexpected $end 错误的解决办法
Jun 05 #PHP
php中json_decode()和json_encode()的使用方法
Jun 04 #PHP
php安全之直接用$获取值而不$_GET 字符转义
Jun 03 #PHP
You might like
PHP If Else(elsefi) 语句
2013/04/07 PHP
超详细的php用户注册页面填写信息完整实例(附源码)
2015/11/17 PHP
thinkPHP框架动态配置用法实例分析
2018/06/14 PHP
PHP面向对象程序设计继承用法简单示例
2018/12/28 PHP
让GoogleCode的SVN下的HTML文件在FireFox下正常显示.
2009/05/25 Javascript
javascript实现促销倒计时+fixed固定在底部
2013/09/18 Javascript
jquery通过select列表选择框对表格数据进行过滤示例
2014/05/07 Javascript
js中的hasOwnProperty和isPrototypeOf方法使用实例
2014/06/06 Javascript
jQuery插件制作之全局函数用法实例
2015/06/01 Javascript
jQuery+Ajax+PHP弹出层异步登录效果(附源码下载)
2016/05/27 Javascript
Node.js中常规的文件操作总结
2016/10/13 Javascript
BootStrap3使用错误记录及解决办法
2016/12/22 Javascript
JavaScript之json_动力节点Java学院整理
2017/06/29 Javascript
canvas绘制爱心的几种方法总结(推荐)
2017/10/31 Javascript
three.js中3D视野的缩放实现代码
2017/11/16 Javascript
vue轮播图插件vue-awesome-swiper
2017/11/27 Javascript
在小程序中使用腾讯视频插件播放教程视频的方法
2018/07/10 Javascript
bootstrap select2插件用ajax来获取和显示数据的实例
2018/08/09 Javascript
详解从vue-loader源码分析CSS Scoped的实现
2019/09/23 Javascript
html-webpack-plugin修改页面的title的方法
2020/06/18 Javascript
解决vue里a标签值解析变量,跳转页面,前面加默认域名端口的问题
2020/07/22 Javascript
简单介绍使用Python解析并修改XML文档的方法
2015/10/15 Python
Python基础之函数的定义与使用示例
2019/03/23 Python
python 通过可变参数计算n个数的乘积方法
2019/06/13 Python
python2与python3爬虫中get与post对比解析
2019/09/18 Python
Python中的list与tuple集合区别解析
2019/10/12 Python
FFT快速傅里叶变换的python实现过程解析
2019/10/21 Python
详解使用HTML5的classList属性操作CSS类
2017/10/13 HTML / CSS
印度领先的在线时尚商店:Koovs
2016/08/28 全球购物
幼儿园元旦活动感言
2014/03/02 职场文书
精彩的广告词
2014/03/19 职场文书
新年爱情寄语
2014/04/08 职场文书
服务型党组织建设典型材料
2014/05/07 职场文书
2014党委书记四风问题对照检查材料思想汇报
2014/09/22 职场文书
2014年信用社工作总结
2014/11/25 职场文书
教你怎么用python selenium实现自动化测试
2021/05/27 Python