PHP项目中如何处理敏感词过滤?

wen PHP项目 2

本文目录导读:

PHP项目中如何处理敏感词过滤?

  1. 简单字符串替换(适用于少量敏感词)
  2. 正则表达式过滤(更灵活)
  3. 高效方案:Trie树(字典树)
  4. 使用扩展库(推荐生产环境)
  5. 完整的生产环境方案
  6. 最佳实践建议

在PHP项目中处理敏感词过滤,常见的方法有以下几种,从简单到高效:

简单字符串替换(适用于少量敏感词)

function simpleFilter($text, $sensitiveWords) {
    foreach ($sensitiveWords as $word) {
        $text = str_replace($word, '***', $text);
    }
    return $text;
}
// 使用示例
$words = ['敏感词1', '敏感词2'];
$text = '这是一段包含敏感词1的文本';
echo simpleFilter($text, $words);

正则表达式过滤(更灵活)

function regexFilter($text, $sensitiveWords) {
    $pattern = '/(' . implode('|', array_map('preg_quote', $sensitiveWords)) . ')/u';
    return preg_replace($pattern, '***', $text);
}
// 使用示例
$text = '包含敏感词1和敏感词2的文本';
echo regexFilter($text, $words);

高效方案:Trie树(字典树)

这是处理大量敏感词时的最佳方案,适合生产环境:

class SensitiveFilter {
    private $trie = [];
    // 构建Trie树
    public function addWords($words) {
        foreach ($words as $word) {
            $node = &$this->trie;
            $len = mb_strlen($word);
            for ($i = 0; $i < $len; $i++) {
                $char = mb_substr($word, $i, 1);
                if (!isset($node[$char])) {
                    $node[$char] = [];
                }
                $node = &$node[$char];
            }
            $node['end'] = true; // 标记结束
        }
    }
    // 过滤敏感词
    public function filter($text, $replaceChar = '*') {
        $result = '';
        $textLen = mb_strlen($text);
        $i = 0;
        while ($i < $textLen) {
            $match = $this->findMatch($text, $i);
            if ($match !== false) {
                $result .= str_repeat($replaceChar, mb_strlen($match));
                $i += mb_strlen($match);
            } else {
                $result .= mb_substr($text, $i, 1);
                $i++;
            }
        }
        return $result;
    }
    private function findMatch($text, $start) {
        $node = $this->trie;
        $match = '';
        for ($i = $start; $i < mb_strlen($text); $i++) {
            $char = mb_substr($text, $i, 1);
            if (!isset($node[$char])) {
                return $match ? $match : false;
            }
            $match .= $char;
            $node = $node[$char];
            if (isset($node['end'])) {
                return $match;
            }
        }
        return false;
    }
}
// 使用示例
$filter = new SensitiveFilter();
$filter->addWords(['敏感词1', '敏感词2', '敏感词']);
$text = '这是一段包含敏感词1和敏感词2的文本';
echo $filter->filter($text);

使用扩展库(推荐生产环境)

使用 swoole 扩展(性能最好)

// 安装:pecl install swoole
$filter = new \Swoole\Filter();
$filter->setDict(['敏感词1', '敏感词2']);
$result = $filter->exec('包含敏感词的文本');

使用 trie_filter 扩展

// 安装:pecl install trie_filter
$tree = trie_filter_new();
foreach ($words as $word) {
    trie_filter_store($tree, $word);
}
trie_filter_save($tree, 'sensitive.dic');
// 使用时加载
$tree = trie_filter_load('sensitive.dic');
$result = trie_filter_search($tree, '文本内容');

完整的生产环境方案

class SensitiveWordFilter {
    private $trie = [];
    private $filePath;
    public function __construct($filePath = null) {
        $this->filePath = $filePath ?: __DIR__ . '/sensitive_words.txt';
        $this->loadFromFile();
    }
    // 从文件加载敏感词
    private function loadFromFile() {
        if (file_exists($this->filePath)) {
            $words = file($this->filePath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
            $this->addWords($words);
        }
    }
    // 添加单个词
    public function addWord($word) {
        $word = trim($word);
        if (empty($word)) return;
        $node = &$this->trie;
        $len = mb_strlen($word);
        for ($i = 0; $i < $len; $i++) {
            $char = mb_substr($word, $i, 1);
            if (!isset($node[$char])) {
                $node[$char] = [];
            }
            $node = &$node[$char];
        }
        $node['end'] = true;
    }
    // 批量添加
    public function addWords($words) {
        foreach ($words as $word) {
            $this->addWord($word);
        }
    }
    // 检查是否包含敏感词
    public function containsSensitive($text) {
        $textLen = mb_strlen($text);
        for ($i = 0; $i < $textLen; $i++) {
            $match = $this->findMatch($text, $i);
            if ($match !== false) {
                return true;
            }
        }
        return false;
    }
    // MySQL 版本(适合大量数据)
    public function saveToDatabase($pdo) {
        $words = json_encode($this->trie);
        $sql = "INSERT INTO sensitive_config (`type`, `content`) VALUES ('trie', ?) 
                ON DUPLICATE KEY UPDATE `content` = ?";
        $stmt = $pdo->prepare($sql);
        $stmt->execute([$words, $words]);
    }
    // 从数据库加载
    public function loadFromDatabase($pdo) {
        $sql = "SELECT content FROM sensitive_config WHERE type = 'trie' LIMIT 1";
        $stmt = $pdo->query($sql);
        $row = $stmt->fetch(PDO::FETCH_ASSOC);
        if ($row) {
            $this->trie = json_decode($row['content'], true);
        }
    }
}
// 生产环境使用示例
$filter = new SensitiveWordFilter('/path/to/sensitive_words.txt');
$text = $_POST['content'];
if ($filter->containsSensitive($text)) {
    echo json_encode(['code' => 1, 'msg' => '包含敏感词']);
} else {
    echo json_encode(['code' => 0, 'msg' => $filter->filter($text)]);
}

最佳实践建议

存储方式

  • 小项目:保存在文本文件或数据库配置表中
  • 大项目:使用Redis等缓存,减少IO开销
  • 分布式:使用配置中心统一管理

性能优化

  1. 缓存Trie树:序列化后缓存到Redis
  2. 分片处理:大文本分段过滤
  3. 异步处理:使用消息队列异步过滤
  4. 静态化:生成过滤结果,减少实时计算

注意事项

  • Unicode处理:使用 mb_* 函数处理字符串
  • 性能监控:记录过滤耗时,监控异常
  • 更新机制:支持热更新敏感词列表
  • 多语言支持:处理中英文混合情况

更复杂的场景处理

// 处理变体(如使用特殊字符绕过)
function antiEvasion($text) {
    // 替换常见变体
    $replacements = [
        '/[1lI]/' => 'l',  // 1和l的替换
        '/[0oO]/' => 'o',  // 0和o的替换
        '/[@]/' => 'a',   // @替换a
        '/[\\x{3000}]/u' => '', // 全角空格
    ];
    return preg_replace(array_keys($replacements), array_values($replacements), $text);
}

根据项目规模和性能要求选择合适的方案,建议在开发环境测试后再部署到生产环境。

抱歉,评论功能暂时关闭!