單純用 //s 移除 HTML 註解會誤傷條件註解 <!--[if IE]>、腳本內註解 <script><!-- ... --></script>。本文逐步構建精準正則,層層排除特殊情況,實現只移除真正的 HTML 註解。
$html = '<!-- 普通註解 -->內容<!-- 另一個 -->';
// 1. 貪婪匹配錯誤:吞掉中間內容
preg_replace('#<!--.*-->#', '', $html);
// 結果:'內容' (錯誤,第一個註解匹配到最後一個 -->)
// 2. 非貪婪修正:正確處理多個註解
preg_replace('#<!--.*?-->#', '', $html);
// 結果:'內容' (正確)
// 3. 條件註解問題:<!--[if IE]>...<![endif]-->
$html2 = '<!--[if IE 9]>IE9專用<![endif]-->正常內容';
preg_replace('#<!--.*?-->#', '', $html2);
// 結果:'正常內容' (條件註解被誤刪,可能破壞 IE 相容性)
// 4. 腳本內註解問題
$html3 = '<script><!-- 舊瀏覽器隱藏腳本 --> console.log(1); //--></script>';
preg_replace('#<!--.*?-->#', '', $html3);
// 結果:'<script> console.log(1); //--></script>' (腳本結構被破壞)
[ 或 !// 模式:<!-- 後面不接 [ 或 ! $pattern1 = '#<!--[^\[!].*?-->#s';
//-->// 模式:不包含 //--> 的註解內容 $pattern2 = '#<!--[^\[!].*?(?步驟 3:完整最終版
function stripHtmlComments(string $html): string { // 核心正則:移除標準 HTML 註解,保留條件註解、腳本內註解 $pattern = '#<!--(?![\[!])(?:(?!-->).)*?(?測試驗證
<?php $testCases = [ '基本' => '<!-- 註解 -->保留', '多個' => '<!-- A -->中間<!-- B -->尾', '條件註解' => '<!--[if IE]>IE內容<![endif]-->保留', '腳本註解' => '<script><!-- 隱藏 --> code(); //--></script>', '巢狀註解' => '<!-- 外層 <!-- 內層 --> 外層尾 -->', // HTML 不支援巢狀,瀏覽器以第一個 --> 為結束 '混合' => '<!-- 刪除 --><div>保留</div><!--[if IE]>保留<![endif]--><script><!--保留--></script>', ]; foreach ($testCases as $name => $html) { $result = stripHtmlComments($html); echo "=== {$name} ===\n"; echo "原始: {$html}\n"; echo "結果: {$result}\n\n"; } ?>輸出結果
=== 基本 === 原始: <!-- 註解 -->保留 結果: 保留 === 多個 === 原始: <!-- A -->中間<!-- B -->尾 結果: 中間尾 === 條件註解 === 原始: <!--[if IE]>IE內容<![endif]-->保留 結果: <!--[if IE]>IE內容<![endif]-->保留 ✓ 保留 === 腳本註解 === 原始: <script><!-- 隱藏 --> code(); //--></script> 結果: <script><!-- 隱藏 --> code(); //--></script> ✓ 保留 === 巢狀註解 === 原始: <!-- 外層 <!-- 內層 --> 外層尾 --> 結果: 外層尾 --> (符合瀏覽器行為:第一個 --> 結束) === 混合 === 原始: <!-- 刪除 --><div>保留</div><!--[if IE]>保留<![endif]--><script><!--保留--></script> 結果: <div>保留</div><!--[if IE]>保留<![endif]--><script><!--保留--></script> ✓ 完美正則元件速查
| 語法 | 名稱 | 作用 |
|---|---|---|
(?!...) | 負向前瞻 | 斷言後面不匹配... |
(? | 負向後顧 | 斷言前面不匹配... |
(?:...) | 非捕獲群組 | 分組不佔用反向引用編號 |
.*? | 非貪婪匹配 | 盡可能少匹配 |
s 修飾符 | PCRE_DOTALL | . 匹配換行符 |
function stripCommentsDom(string $html): string {
$dom = new DOMDocument();
@$dom->loadHTML('' . $html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
$xpath = new DOMXPath($dom);
$comments = $xpath->query('//comment()');
foreach ($comments as $comment) {
// 保留條件註解:包含 [if 或 [endif
$text = $comment->nodeValue;
if (strpos($text, '[if') !== false || strpos($text, '[endif') !== false) {
continue;
}
$comment->parentNode->removeChild($comment);
}
return $dom->saveHTML();
}
preg_replace 正則 : ~0.5 ms / 10KB HTML DOMDocument 解析 : ~3 ms / 10KB HTML (含解析開銷) HTMLPurifier : ~50 ms / 10KB HTML (完整 XSS 防護)
精準的正則利用負向前瞻/後顧斷言優雅解決 HTML 註解移除的邊界情況,效能優於 DOM 解析,適合高頻處理場景。