PHP项目如何实现评论审核功能?从零搭建安全高效的评论系统
目录导读
- 为什么需要评论审核?
- 评论审核的核心功能设计
- 数据库表结构设计(MySQL+Redis)
- PHP后端实现:提交→审核→展示流程
- 敏感词过滤与自动拦截机制
- 管理员审核后台实现
- 性能优化与防刷策略
- 常见问题解答(FAQ)
为什么需要评论审核?
在PHP项目(如博客、电商、论坛)中,评论功能是用户互动的重要环节,但未经审核的评论可能带来三大风险:

- 垃圾广告:机器人刷屏推广链接
- :政治敏感、色情、暴力词汇
- 恶意攻击:SQL注入、XSS跨站脚本
审核模式对比:
| 模式 | 优点 | 缺点 |
|---|---|---|
| 预审核(先审后发) | 安全可控 | 延迟用户体验 |
| 后审核(先发后审) | 用户反馈快 | 需快速响应 |
| 自动+人工混合 | 效率与安全平衡 | 技术实现复杂 |
推荐方案:采用自动过滤(敏感词/垃圾检测)+ 人工抽查的混合模式,既保证效率又降低风险。
评论审核的核心功能设计
一个完整的评论审核系统应包含:
1 用户端功能
- 提交评论(含昵称、邮箱、内容)
- 查看已通过审核的评论(分页展示)
- 评论状态提示(“审核中”/“已拒绝”/“已通过”)
2 管理端功能
- 待审核列表:按时间倒序排列
- 审核操作:通过/拒绝/标记垃圾
- 批量处理:全选操作
- 敏感词管理:增删改敏感词库
3 自动处理机制
- 敏感词匹配拦截(立即拒绝)
- 超链接检测(标记为可疑)
- 频率限制(同一IP/用户短时间重复提交)
- 第三方API检测(如百度AI内容审核)
数据库表结构设计(MySQL+Redis)
1 评论主表(comments)
CREATE TABLE `comments` ( `id` int(11) NOT NULL AUTO_INCREMENT, `post_id` int(11) NOT NULL COMMENT '文章ID', `user_name` varchar(50) NOT NULL COMMENT '评论者昵称', `email` varchar(100) DEFAULT NULL COMMENT '邮箱', `content` text NOT NULL COMMENT '评论内容', `status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '状态:0待审,1通过,2拒绝', `ip_address` varchar(45) DEFAULT NULL COMMENT '用户IP', `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_post_id` (`post_id`), KEY `idx_status` (`status`), KEY `idx_created_at` (`created_at`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
2 敏感词表(sensitive_words)
CREATE TABLE `sensitive_words` ( `id` int(11) NOT NULL AUTO_INCREMENT, `word` varchar(100) NOT NULL, `created_at` datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_word` (`word`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
3 Redis缓存设计
- 评论计数:
comment:count:{post_id}存储当前通过评论数 - 防重复提交:
comment:submit:{ip}:{post_id}设置60秒过期 - 最新评论:
comment:latest:{post_id}使用有序集合(ZSET)
PHP后端实现:提交→审核→展示流程
1 评论提交接口(需防XSS+SQL注入)
// submit_comment.php
public function submitComment($postId, $userName, $content) {
// 1. 输入净化
$userName = htmlspecialchars(strip_tags($userName), ENT_QUOTES, 'UTF-8');
$content = htmlspecialchars(strip_tags($content), ENT_QUOTES, 'UTF-8');
// 2. 防重复提交(Redis锁)
$ip = $_SERVER['REMOTE_ADDR'];
$lockKey = "comment:submit:{$ip}:{$postId}";
if ($redis->exists($lockKey)) {
return ['error' => '请勿频繁提交'];
}
$redis->setex($lockKey, 60, time());
// 3. 自动审核检测
$autoResult = $this->autoAudit($content);
$status = $autoResult['status']; // 0待审/1通过/2拒绝
// 4. 写入数据库
$sql = "INSERT INTO comments (post_id, user_name, content, status, ip_address) VALUES (?, ?, ?, ?, ?)";
$stmt = $db->prepare($sql);
$stmt->execute([$postId, $userName, $content, $status, $ip]);
return ['success' => true, 'comment_id' => $db->lastInsertId()];
}
2 评论列表展示(仅展示status=1)
public function getApprovedComments($postId, $page = 1, $perPage = 20) {
$offset = ($page - 1) * $perPage;
$sql = "SELECT id, user_name, content, created_at
FROM comments
WHERE post_id = ? AND status = 1
ORDER BY created_at DESC
LIMIT ? OFFSET ?";
$stmt = $db->prepare($sql);
$stmt->execute([$postId, $perPage, $offset]);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
敏感词过滤与自动拦截机制
1 Trie树敏感词检测(高性能方案)
class SensitiveFilter {
private $trie = [];
public function loadWords($words) {
foreach ($words as $word) {
$node = &$this->trie;
for ($i = 0; $i < mb_strlen($word); $i++) {
$char = mb_substr($word, $i, 1);
if (!isset($node[$char])) {
$node[$char] = [];
}
$node = &$node[$char];
}
$node['end'] = true; // 标记单词结束
}
}
public function check($text) {
$len = mb_strlen($text);
for ($i = 0; $i < $len; $i++) {
$node = $this->trie;
for ($j = $i; $j < $len; $j++) {
$char = mb_substr($text, $j, 1);
if (!isset($node[$char])) break;
$node = $node[$char];
if (isset($node['end'])) {
return true; // 发现敏感词
}
}
}
return false;
}
}
2 自动审核策略组合
private function autoAudit($content) {
// 策略1:敏感词检测
if ($this->sensitiveFilter->check($content)) {
return ['status' => 2]; // 直接拒绝
}
// 策略2:链接检测(超过2个链接标记为可疑)
preg_match_all('/https?:\/\/[^\s]+/', $content, $links);
if (count($links[0]) > 2) {
return ['status' => 0]; // 进入人工审核
}
// 策略3:重复提交检测(同一用户连续相同内容)
// ... 使用Redis比较上次内容
return ['status' => 1]; // 自动通过
}
管理员审核后台实现
1 待审核列表页面
// admin/comments.php
$sql = "SELECT c.*, p.title as post_title
FROM comments c
LEFT JOIN posts p ON c.post_id = p.id
WHERE c.status = 0
ORDER BY c.created_at DESC
LIMIT 50";
前端操作按钮:
- ✅ 通过:
update comments set status=1 where id=? - ❌ 拒绝:
update comments set status=2 where id=? - 🗑️ 删除:
delete from comments where id=?
2 批量操作优化
// 使用事务批量更新
$ids = explode(',', $_POST['ids']);
$db->beginTransaction();
$stmt = $db->prepare("UPDATE comments SET status = ? WHERE id = ?");
foreach ($ids as $id) {
$stmt->execute([$newStatus, intval($id)]);
}
$db->commit();
性能优化与防刷策略
1 数据库查询优化
- 为
post_id + status建立联合索引 - 使用
LIMIT分页,避免OFFSET过大(改用游标分页) - 评论计数缓存:每次审核通过后更新Redis计数
2 防刷机制
// 1. 时间限制:同一IP 60秒内只能评论一次
if ($redis->exists("comment:limit:ip:{$ip}")) {
die('操作频繁');
}
// 2. 内容长度限制
if (mb_strlen($content) < 2 || mb_strlen($content) > 2000) {
die('内容长度不合法');
}
// 3. 验证码(可选)
// 使用极验或Google reCAPTCHA
3 异步审核队列(高并发场景)
// 使用Redis列表作为队列
$redis->lpush('comment:audit_queue', json_encode([
'comment_id' => $id,
'content' => $content
]));
// 单独脚本处理审核
while ($data = $redis->brpop('comment:audit_queue', 5)) {
$this->autoAudit($data['content']);
// 更新状态
}
常见问题解答(FAQ)
Q1:评论审核需要每次查询数据库吗?
A:建议缓存已通过评论数量(Redis),列表页只查已通过的,待审核数量用 SELECT COUNT(*) WHERE status=0,但避免高频查询。
Q2:如何处理图片/附件评论的审核?
A:存储路径到数据库,审核时人工或调用图片鉴黄API(如腾讯云内容安全),通过后才显示图片链接。
Q3:用户能否修改已提交的评论?
A:建议不允许修改,或仅允许修改未审核的评论,修改后状态重置为待审核。
Q4:敏感词库如何更新?
A:建立后台管理页面,支持导入词典(txt文件,每行一个词),定期更新。
Q5:审核系统与现有评论插件(如Disqus)如何集成?
A:推荐自建,如果使用第三方,可以设置其内容同步到本地数据库后进行二次审核。
总结与最佳实践
实现PHP评论审核功能的关键点:
- 安全第一:防止XSS/SQL注入,使用预处理语句
- 分层审核:自动过滤(>80%)+ 人工审核(<20%)
- 缓存加速:Redis缓存评论数和最新评论
- 用户体验:提交后立即显示“审核中”,通过后异步刷新
- 日志记录:记录所有审核操作,方便回溯
通过以上实现,你可以构建一个既安全又高效的评论审核系统,既保护网站内容安全,又提升用户互动体验。