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 preg_filter执行一个正则表达式搜索和替换
Feb 27 PHP
PHP模板引擎Smarty的缓存使用总结
Apr 24 PHP
php清除和销毁session的方法分析
Mar 19 PHP
正确的PHP匹配UTF-8中文的正则表达式
May 13 PHP
PHP图像处理类库MagickWand用法实例分析
May 21 PHP
PHP处理数组和XML之间的互相转换
Jun 02 PHP
利用PHP实现一个简单的用户登记表示例
Apr 25 PHP
ThinkPHP开发--使用七牛云储存
Sep 14 PHP
CentOS7编译安装php7.1的教程详解
Apr 18 PHP
tp5递归 无限级分类详解
Oct 18 PHP
PHP pthreads v3下的Volatile简介与使用方法示例
Feb 21 PHP
thinkphp诸多限制条件下如何getshell详解
Dec 09 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 获得汉字拼音首字母的函数
2009/08/01 PHP
php实现的简易扫雷游戏实例
2015/07/09 PHP
php基于环形链表解决约瑟夫环问题示例
2017/11/07 PHP
ThinkPHP5.1框架页面跳转及修改跳转页面模版示例
2019/05/06 PHP
表单提交时自动复制内容到剪贴板的js代码
2007/03/16 Javascript
asp.net和asp下ACCESS的参数化查询
2008/06/11 Javascript
一组JS创建和操作表格的函数集合
2009/05/07 Javascript
JS在IE和FF下attachEvent,addEventListener学习笔记
2009/11/26 Javascript
js利用与或运算符优先级实现if else条件判断表达式
2010/04/15 Javascript
jQuery技巧总结
2011/01/01 Javascript
Javascript 中的 call 和 apply使用介绍
2012/02/22 Javascript
Ext4.2的Ext.grid.plugin.RowExpander无法触发事件解决办法
2014/08/15 Javascript
重写document.write实现无阻塞加载js广告(补充)
2014/12/12 Javascript
PHP+mysql+Highcharts生成饼状图
2015/05/04 Javascript
在JavaScript中模拟类(class)及类的继承关系
2016/05/20 Javascript
bootstrap table小案例
2016/10/21 Javascript
JS轮播图实现简单代码
2021/02/19 Javascript
详解使用PM2管理nodejs进程
2017/10/24 NodeJs
用Electron写个带界面的nodejs爬虫的实现方法
2019/01/29 NodeJs
javascript+HTML5 canvas绘制时钟功能示例
2019/05/15 Javascript
[06:15]2016国际邀请赛中国区预选赛单车采访:我顶WINGS
2016/06/27 DOTA
简单介绍利用TK在Python下进行GUI编程的教程
2015/04/13 Python
Python中MySQLdb和torndb模块对MySQL的断连问题处理
2015/11/09 Python
详解python3中zipfile模块用法
2018/06/18 Python
在numpy矩阵中令小于0的元素改为0的实例
2019/01/26 Python
Python实现定时自动关闭的tkinter窗口方法
2019/02/16 Python
使用python获取邮箱邮件的设置方法
2019/09/20 Python
Python迭代器iterator生成器generator使用解析
2019/10/24 Python
浅谈Python中的生成器和迭代器
2020/06/19 Python
Python生成器generator原理及用法解析
2020/07/20 Python
Python描述数据结构学习之哈夫曼树篇
2020/09/07 Python
VSCode中autopep8无法运行问题解决方案(提示Error: Command failed,usage)
2021/03/02 Python
公益广告语集锦
2014/03/13 职场文书
“六查”、“三学”、“三干”查摆问题整改措施
2014/09/27 职场文书
Python网络编程之ZeroMQ知识总结
2021/04/25 Python
浅谈Golang 嵌套 interface 的赋值问题
2021/04/29 Golang