截取html
需求
从一段html 中截取指定长度的内容,
要求:
- 长度为不包括html标签的内容长度,即 innerTEXT 长度
- 保留相关标签,并进行闭合
代码
简单版代码
/**
* 截取html, 标签不计入长度,自动闭合标签
* @param string $html
* @param int $length
* @param string $endWith
* @bug 本方法缺陷: 未进行严格标签判断 例如 < <gg data="<a>"
* @example ::substr('<p>1111<div/>111<br>111<i class="444">11</i>111 55555</p>', 12)
* @return string
*/
public static function substr(string $html, int $length, string $endWith = '...'): string {
if ($length < 1) {
return $endWith;
}
$maxLength = mb_strlen($html);
if ($maxLength < $length) {
return $html;
}
$result = '';
$n = 0;
$unClosedTags = [];
$isCode = false; // 是不是HTML代码
$isHTML = false; // 是不是HTML特殊字符,如
$notClosedTags = ['area', 'base', 'basefont', 'br', 'col', 'frame', 'hr', 'img', 'input', 'link', 'meta', 'param', 'embed', 'command', 'keygen', 'source', 'track', 'wbr'];
$tag = '';
for ($i = 0; $i < $maxLength; $i++) {
$char = mb_substr($html, $i, 1);
if ($char == '<') {
// 进入标签
$isCode = true;
$tag = '';
}
else if ($char == '&') {
$isHTML = true;
}
else if ($char == '>' && $isCode) {
$n = $n - 1;
$isCode = false;
$tag = explode(' ', $tag, 2)[0];
if (substr($tag, 0, 1) === '/') {
// 判断是否时结束标签, 倒序找到邻近开始标签,进行移除
for ($j = count($unClosedTags) - 1; $j >= 0; $j --) {
if ($tag === $unClosedTags[$j]) {
$unClosedTags = array_splice($unClosedTags, 0, $j - 1);
break;
}
}
$tag = '';
}
if (!empty($tag) &&
substr($tag, strlen($tag) - 1, 1) !== '/'
&& !in_array(strtolower($tag), $notClosedTags)) {
// 不是结束标签且不是自闭合且不是无需闭合把标签加入
$unClosedTags[] = $tag;
}
$tag = '';
}
else if ($char == ';' && $isHTML) {
$isHTML = false;
}
if ($isCode && ($tag !== '' || $char !== '<')) {
$tag .= $char;
}
if (!$isCode && !$isHTML && $char !== ' ') {
$n = $n + 1;
}
$result .= $char;
if ($n >= $length) {
break;
}
}
$result .= $endWith;
for ($j = count($unClosedTags) - 1; $j >= 0; $j --) {
$result .= sprintf('</%s>', $unClosedTags[$j]);
}
return $result;
}
此代码存在的问题
- 未对 < 进行验证是否真的为标签开始
- 未对标签属性值中可能出现的标记 进行过滤
相关需求
- 指定行的截取
转载请保留原文链接: https://zodream.cn/blog/id/91.html