chengkun
2025-05-22 3b321a2882db082c68aaf8771e0f55daa58a63d3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
<?php
 
 
class sensitive
    
    private static $instance = null;
    /**
     * 替换符号
     * @var string
     */
    private static $replaceSymbol = "*";
    /**
     * 敏感词树
     * @var array
     */
    private static $sensitiveWordTree = [];
    private function __construct(){}
    /**
     * 获取实例
     */
    public static function getInstance()
    {
        if (!(self::$instance instanceof Sensitive)) {
            return self::$instance = new self;
        }
        return self::$instance;
    }
    
    /**
     * @desc 添加敏感词,组成树结构。
     * @param $file_path:敏感词库文件路径
     */
    public static function addSensitiveWords($file_path)
    {
        $file_path  =  (string)$file_path;
        
        foreach (self::readFile($file_path) as $words) {
     
            $len = mb_strlen($words);
            $treeArr = &self::$sensitiveWordTree;
            
            for ($i = 0; $i < $len; $i++) {
                $word = mb_substr($words, $i, 1);
                //敏感词树结尾记录状态为false;
                $treeArr = &$treeArr[$word];
                if(!isset($treeArr)){
                    $treeArr  =  false;
                }
            }
        }
    }
    
    /**
     * 执行过滤
     * @param string $txt
     * @return string
     */
    public static function execFilter($txt) 
    {
        $txt  =  (string)$txt;
        $wordList = self::searchWords($txt);
        
        
        if(empty($wordList)){
            return $txt;
        }else{
            return strtr($txt,$wordList);
        }
    }
    
    /**
     * 搜索敏感词
     * @param string $txt
     * @return array
     */
    private static function searchWords($txt)
    {
        $txt  =  (string)$txt;
        $txtLength  = mb_strlen($txt);
        $wordList   = array();
        
        for($i = 0; $i < $txtLength; $i++){
            //检查字符是否存在敏感词树内,传入检查文本、搜索开始位置、文本长度
            $len = self::checkWordTree($txt,$i,$txtLength);
            //存在敏感词,进行字符替换。
            if($len > 0){
                //搜索出来的敏感词
                $word = mb_substr($txt,$i,$len);
                $wordList[$word] = str_repeat(self::$replaceSymbol,$len);
            }
        }
        return $wordList;
    }
    
    /**
     * 检查敏感词树是否合法
     * @param string $txt 检查文本
     * @param int $index 搜索文本位置索引
     * @param int $txtLength 文本长度
     * @return int 返回不合法字符个数
     */
    private static function checkWordTree($txt, $index, $txtLength) 
    {
        $txt       =  (string)$txt;
        $index     = (int)$index;
        $txtLength = (int)$txtLength;
        
        $treeArr = &self::$sensitiveWordTree;
        
        $wordLength = 0;//敏感字符个数
        $flag = false;
        
        for($i = $index; $i < $txtLength; $i++){
            
            $txtWord = mb_substr($txt,$i,1); //截取需要检测的文本,和词库进行比对
            
            //如果搜索字不存在词库中直接停止循环。
            if(!isset($treeArr[$txtWord])) break;
            
            if($treeArr[$txtWord] !== false){//检测还未到底
                $treeArr = &$treeArr[$txtWord]; //继续搜索下一层tree
            }else{
                $flag = true;
            }
            $wordLength++;
        }
        //没有检测到敏感词,初始化字符长度
        $flag ?: $wordLength = 0;
        return $wordLength;
    }
    
    
    /**
     * 读取文件内容
     * @param string $file_path
     * @return Generator
     */
    private static function readFile($file_path) 
    {
        $file_path  =  (string)$file_path;
        $handle = fopen($file_path, 'r');
         
        while (!feof($handle)) {
            yield trim(fgets($handle));
        }
        fclose($handle);
    }
    
    private function __clone()
    {
        throw new \Exception("clone instance failed!");
    }
    
    private function __wakeup()
    {
        throw new \Exception("unserialize instance failed!");
    }
 
}
?>