PHP 修复未正常关闭的HTML标签实现代码(支持嵌套和就近闭合)
程序员文章站
2022-04-28 15:46:01
fixhtmltag version 0.2 这个版本解决了上次遗留的问题,即就近闭合和嵌套闭合问题。具体可以看代码的注释。 复制代码 代码如下:
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;
}
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;
}
上一篇: python3 员工信息表
推荐阅读
-
PHP 修复未正常关闭的HTML标签实现代码(支持嵌套和就近闭合)
-
PHP 修复未正常关闭的HTML标签实现代码(支持嵌套和就近闭合)_PHP教程
-
PHP 修复未正常关闭的HTML标签实现代码(支持嵌套和就近闭合)_PHP
-
PHP 修复未正常关闭的HTML标签实现代码(支持嵌套和就近闭合)_php实例
-
PHP 修复未正常关闭的HTML标签实现代码(支持嵌套和就近闭合)_php实例
-
修复未正常关闭的HTML标签(支持嵌套和就近闭合)的代码
-
PHP 修复未正常关闭的HTML标签实现代码(支持嵌套和就近闭合)_PHP教程
-
PHP 修复未正常关闭的HTML标签实现代码(支持嵌套和就近闭合)
-
PHP 修复未正常关闭的HTML标签实现代码(支持嵌套和就近闭合)
-
PHP 修复未正常关闭的HTML标签实现代码(支持嵌套和就近闭合)