PHP 修複未正常關閉的HTML標籤實現代碼(支援嵌套和就近閉合)

來源:互聯網
上載者:User

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;
}

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.