本文目录导读:

在PHP项目中实现敏感词过滤,通常有以下几种常见方案,按推荐程度排序:
Trie树(前缀树)方案(推荐)
这是性能最好的方案,适合大规模敏感词库:
<?php
class SensitiveWordFilter {
private $trieTree = [];
/**
* 构建敏感词树
*/
public function loadWords(array $words) {
foreach ($words as $word) {
$this->addWord($word);
}
}
/**
* 添加单个敏感词到Trie树
*/
private function addWord($word) {
$node = &$this->trieTree;
$len = mb_strlen($word, 'UTF-8');
for ($i = 0; $i < $len; $i++) {
$char = mb_substr($word, $i, 1, 'UTF-8');
if (!isset($node[$char])) {
$node[$char] = [];
}
$node = &$node[$char];
}
// 标记敏感词结束
$node['is_end'] = true;
}
/**
* 过滤敏感词(替换为指定字符)
*/
public function filter($text, $replaceChar = '*') {
$result = '';
$len = mb_strlen($text, 'UTF-8');
$i = 0;
while ($i < $len) {
$node = $this->trieTree;
$matched = false;
$endPos = $i;
for ($j = $i; $j < $len; $j++) {
$char = mb_substr($text, $j, 1, 'UTF-8');
if (!isset($node[$char])) {
break;
}
$node = $node[$char];
if (isset($node['is_end'])) {
$endPos = $j;
$matched = true;
}
}
if ($matched) {
// 替换敏感词
$wordLen = $endPos - $i + 1;
$result .= str_repeat($replaceChar, $wordLen);
$i = $endPos + 1;
} else {
// 保留原字符
$result .= mb_substr($text, $i, 1, 'UTF-8');
$i++;
}
}
return $result;
}
/**
* 检查是否包含敏感词
*/
public function hasSensitiveWord($text) {
$len = mb_strlen($text, 'UTF-8');
for ($i = 0; $i < $len; $i++) {
$node = $this->trieTree;
for ($j = $i; $j < $len; $j++) {
$char = mb_substr($text, $j, 1, 'UTF-8');
if (!isset($node[$char])) {
break;
}
$node = $node[$char];
if (isset($node['is_end'])) {
return true;
}
}
}
return false;
}
}
// 使用示例
$filter = new SensitiveWordFilter();
$filter->loadWords(['敏感词1', '敏感词2', '测试']);
$text = '这是一段包含敏感词1和测试的文本';
$result = $filter->filter($text, '*');
echo $result; // 输出: 这是一段包含***和**的文本
if ($filter->hasSensitiveWord($text)) {
echo '包含敏感词';
}
DFA算法(确定有限自动机)
与Trie树类似,但有更灵活的匹配规则:
<?php
class DFAFilter {
private $wordTree = [];
public function loadWords(array $words) {
foreach ($words as $word) {
$this->addWord($word);
}
}
private function addWord($word) {
$len = mb_strlen($word, 'UTF-8');
$node = &$this->wordTree;
for ($i = 0; $i < $len; $i++) {
$char = mb_substr($word, $i, 1, 'UTF-8');
if (!isset($node[$char])) {
$node[$char] = [
'is_end' => false,
'child' => []
];
}
if ($i == $len - 1) {
$node[$char]['is_end'] = true;
}
$node = &$node[$char]['child'];
}
}
public function filter($text, $replace = '***') {
$result = '';
$textLen = mb_strlen($text, 'UTF-8');
for ($start = 0; $start < $textLen; $start++) {
$node = $this->wordTree;
$matchLen = 0;
$tempResult = '';
for ($i = $start; $i < $textLen; $i++) {
$char = mb_substr($text, $i, 1, 'UTF-8');
if (!isset($node[$char])) {
break;
}
$tempResult .= $char;
$matchLen++;
if ($node[$char]['is_end']) {
$result .= $replace;
$start = $i;
$matchLen = 0;
break;
}
$node = &$node[$char]['child'];
}
if ($matchLen == 0) {
$result .= mb_substr($text, $start, 1, 'UTF-8');
}
}
return $result;
}
}
正则表达式方案(简单但性能差)
<?php
class RegexFilter {
private $pattern = '';
public function loadWords(array $words) {
// 转义特殊字符
$escaped = array_map(function($word) {
return preg_quote($word, '/');
}, $words);
$this->pattern = '/(' . implode('|', $escaped) . ')/u';
}
public function filter($text, $replace = '*') {
return preg_replace($this->pattern, $replace, $text);
}
public function hasSensitiveWord($text) {
return preg_match($this->pattern, $text) === 1;
}
}
// 使用示例
$filter = new RegexFilter();
$filter->loadWords(['敏感词1', '敏感词2']);
$result = $filter->filter('这是一段包含敏感词1的文本', '***');
使用第三方库(推荐生产环境)
COS-AC自动机库
composer require lustre/multibyte-cjk-regular
use Multibyte\CJKRegular;
$filter = new CJKRegular();
$filter->addDictionary(['敏感词1', '敏感词2']);
$filter->addFromFile('/path/to/words.txt');
$result = $filter->replace('这是敏感词1文本', '***');
Redis+布隆过滤器(大规模高并发)
<?php
class BloomFilter {
private $redis;
private $key = 'sensitive_words_bloom';
public function __construct($redis) {
$this->redis = $redis;
}
public function addWord($word) {
$positions = $this->getHashPositions($word);
foreach ($positions as $position) {
$this->redis->setBit($this->key, $position, 1);
}
}
public function exists($word) {
$positions = $this->getHashPositions($word);
foreach ($positions as $position) {
if ($this->redis->getBit($this->key, $position) == 0) {
return false;
}
}
return true; // 有误判率
}
private function getHashPositions($str) {
// 多个哈希函数
$positions = [];
$positions[] = crc32($str) % 100000;
$positions[] = md5($str) % 100000;
$positions[] = sha1($str) % 100000;
return $positions;
}
}
实践建议
敏感词库管理
// 从文件加载
$words = file('sensitive_words.txt', FILE_IGNORE_NEW_LINES);
// 或者从数据库加载
$words = $db->query("SELECT word FROM sensitive_words")->fetchAll(PDO::FETCH_COLUMN);
性能优化
// 使用单例模式
class FilterManager {
private static $instance;
private $filter;
private function __construct() {
$this->filter = new SensitiveWordFilter();
$this->filter->loadWords($this->getWords());
}
public static function getInstance() {
if (!self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
}
完整过滤器类
<?php
class SensitiveFilter {
use SingletonTrait; // 假设有单例trait
private $filter;
private $cacheTime = 3600; // 缓存1小时
public function __construct() {
$this->filter = new DFAFilter();
$this->loadWords();
}
private function loadWords() {
// 优先从缓存加载
$words = Cache::get('sensitive_words');
if (!$words) {
$words = $this->fetchWordsFromDB();
Cache::set('sensitive_words', $words, $this->cacheTime);
}
$this->filter->loadWords($words);
}
public function filter($text) {
return $this->filter->filter($text);
}
public function hasSensitive($text) {
return $this->filter->hasSensitiveWord($text);
}
}
推荐方案
- 小型项目:Trie树方案(自己实现)
- 中型项目:第三方库如
lustre/multibyte-cjk-regular - 大型项目:Redis + Trie树 + Bloom Filter 组合方案
- 实时处理:AC自动机(Aho-Corasick)
选择方案时要考虑:敏感词库大小、文本长度、并发量、允许的误判率等因素。
版权声明:足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播