首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 高性能去除 HTML 標簽獲取純文本(支持巢狀標簽)

     編輯:PHP 相關知識

為什麼不建議用正則表達式?

正則處理 HTML 存在天然缺陷:無法正確匹配巢狀結構、屬性值包含 > 時誤判、CDATA/腳本/樣式標簽內容處理困難。雖然 strip_tags() 可用,但無法自定義保留/移除邏輯,且對編碼敏感。

基於狀態機的流式解析器實現

<?php
/**
 * HTML 標簽移除器 - 支持巢狀標簽、屬性值保護、實體解碼
 */
class HtmlTagStripper
{
    private string $html;
    private int $length;
    private int $pos = 0;
    private int $depth = 0;
    private string $result = '';
    private bool $inTag = false;
    private bool $inAttr = false;
    private bool $inScriptOrStyle = false;
    private string $tagName = '';
    
    public function __construct(string $html)
    {
        $this->html = $html;
        $this->length = strlen($html);
    }
    
    public function strip(bool $decodeEntities = true): string
    {
        while ($this->pos < $this->length) {
            $char = $this->html[$this->pos];
            
            if (!$this->inTag) {
                if ($char === '<') {
                    $this->enterTag();
                } elseif (!$this->inScriptOrStyle) {
                    $this->result .= $char;
                }
            } else {
                $this->processTagChar($char);
            }
            $this->pos++;
        }
        
        $output = $this->result;
        if ($decodeEntities) {
            $output = html_entity_decode($output, ENT_QUOTES | ENT_HTML5, 'UTF-8');
        }
        return trim(preg_replace('/\s+/', ' ', $output));
    }
    
    private function enterTag(): void
    {
        $this->inTag = true;
        $this->tagName = '';
        // 預讀標簽名
        $nextPos = $this->pos + 1;
        if ($nextPos < $this->length && $this->html[$nextPos] === '/') {
            $nextPos++;
        }
        $start = $nextPos;
        while ($nextPos < $this->length) {
            $c = $this->html[$nextPos];
            if (ctype_space($c) || $c === '>' || $c === '/') break;
            $nextPos++;
        }
        $this->tagName = strtolower(substr($this->html, $start, $nextPos - $start));
        $this->inScriptOrStyle = in_array($this->tagName, ['script', 'style', 'noscript']);
    }
    
    private function processTagChar(string $char): void
    {
        if ($char === '"' || $char === "'") {
            $this->inAttr = !$this->inAttr;
            return;
        }
        
        if (!$this->inAttr && $char === '>') {
            $this->exitTag();
        }
    }
    
    private function exitTag(): void
    {
        $this->inTag = false;
        $this->inAttr = false;
        if (in_array($this->tagName, ['script', 'style', 'noscript'])) {
            $this->inScriptOrStyle = false;
        }
    }
}

// 使用示例
$html = '<div class="box"><p>Hello <span>World</span></p><script>alert(1)</script></div>';
$stripper = new HtmlTagStripper($html);
echo $stripper->strip(); // 輸出: Hello World

對比主流方案

進階擴展建議

  • 白名單模式:增加 $allowedTags 參數,僅保留安全標簽(如 <b><i><a>)
  • 屬性過濾:解析標簽屬性,僅保留 href、src、alt 等安全屬性
  • 流式處理大文件:改為 Generator 逐塊輸出,避免大字符串內存壓力
  • 編碼自動檢測:集成 mb_detect_encoding 處理非 UTF-8 輸入

掌握狀態機解析思想,不僅能解決 HTML 去標簽,更能應對 XML、Markdown、模板引擎等結構化文本處理場景。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
方案巢狀支持屬性值保護腳本/樣式過濾實體解碼性能
strip_tags()✓✗✗✗★★★★★
正則替換✗✗✗✗★★★☆☆
DOMDocument✓✓✓✓★★☆☆☆
本文狀態機✓✓✓可選★★★★☆