WordPress无需插件 添加 评论区数学计算题 验证码的 新手详细教程
最近,我把评论区放出来了,为了防止恶意评论,我想添加个验证码,不想用插件,仅靠代码实现。
经过一番搜索,代码如下:
修改模版里的functions.php,添加以下代码:
/**
* 1-9 加减乘除算术验证码 - 输出HTML
*/
function math_captcha_show()
{
if (current_user_can('edit_posts')) return;
$ops = ['+', '-', '*', '/'];
$op = $ops[rand(0, 3)];
$num1 = rand(1, 9);
$num2 = rand(1, 9);
$result = 0;
// 运算规则
switch ($op) {
case '-':
if ($num1 < $num2) list($num1, $num2) = [$num2, $num1];
$result = $num1 - $num2;
break;
case '/':
while ($num2 === 0 || $num1 % $num2 !== 0) {
$num1 = rand(1, 9);
$num2 = rand(1, 9);
}
$result = (int)($num1 / $num2);
break;
case '*':
$result = $num1 * $num2;
break;
default:
$result = $num1 + $num2;
break;
}
// 生成唯一令牌,有效期5分钟
$token = md5(uniqid(mt_rand(), true));
set_transient('math_captcha_' . $token, $result, 300);
// 输出验证码HTML
echo '<div class="math-captcha-wrap">';
echo esc_html($num1) . ' ' . esc_html($op) . ' ' . esc_html($num2) . ' = ';
echo '<input type="text" name="math_captcha" class="math-captcha-input" autocomplete="off" required>';
echo '<input type="hidden" name="captcha_token" value="' . esc_attr($token) . '">';
echo '</div>';
}
/**
* 评论验证码校验 - 放在函数外部(正确写法)
* 优先级10,在中文校验(5)之后执行
*/
add_filter('preprocess_comment', function ($commentdata) {
if (current_user_can('edit_posts')) return $commentdata;
$input = isset($_POST['math_captcha']) ? trim($_POST['math_captcha']) : '';
$token = isset($_POST['captcha_token']) ? sanitize_text_field($_POST['captcha_token']) : '';
if (empty($input) || empty($token)) {
wp_die('Please fill in the calculation result.', 'Failed', ['response' => 403]);
}
$right_ans = get_transient('math_captcha_' . $token);
if ($right_ans === false) {
wp_die('Verification expired, please refresh page.', 'Failed', ['response' => 403]);
}
if (intval($input) !== intval($right_ans)) {
wp_die('Wrong answer, please try again.', 'Failed', ['response' => 403]);
}
delete_transient('math_captcha_' . $token);
return $commentdata;
}, 10);
2026.08.01更新:我觉得这些验证码5分钟写入一个,文章有访问就生成一个,搞的wp_options越来越臃肿,我想了下,还是单独建个表放验证码吧,可以随便删除。
第一步,先用phpmyadmin登录你的数据库,创建一个单独的表,比如yanzhengma,用phpmyadmin里的SQL语句创建就行了。
CREATE TABLE `yanzhengma` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增ID', `captcha_key` varchar(64) NOT NULL COMMENT '验证码唯一标识token', `answer` int(11) NOT NULL COMMENT '算术验证码正确答案', `expire` int(10) unsigned NOT NULL COMMENT '过期时间戳', `createtime` int(10) unsigned NOT NULL COMMENT '创建时间戳', PRIMARY KEY (`id`), UNIQUE KEY `idx_captcha_key` (`captcha_key`), KEY `idx_expire` (`expire`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='算术验证码独立存储表';
字段说明:
token:前端隐藏域传递的唯一标识,唯一索引快速查询
answer:算式正确结果
expire:时间戳,5 分钟有效期
used:验证成功标记为已使用,杜绝同一个 token 反复刷评论
idx_expire索引:定时清理过期数据更快
下面的代码放入你的functions.php里就行了,就替换上面的代码:
// 算术验证码【独立数据表版本】
if (!defined('MATH_CAPTCHA_TABLE')) {
define('MATH_CAPTCHA_TABLE', 'yanzhengma');
}
function math_captcha_show()
{
if (current_user_can('edit_posts')) return;
global $wpdb;
$now = time();
// 5%概率清理过期数据,限制单次删除行数,防止锁表
if (mt_rand(1, 20) <= 1) {
$wpdb->query($wpdb->prepare("DELETE FROM " . MATH_CAPTCHA_TABLE . " WHERE expire < %d LIMIT 100", $now));
}
$ops = ['+', '-', '*', '/'];
$op = $ops[rand(0, 3)];
$num1 = rand(1, 9);
$num2 = rand(1, 9);
$result = 0;
switch ($op) {
case '-':
if ($num1 < $num2) list($num1, $num2) = [$num2, $num1];
$result = $num1 - $num2;
break;
case '/':
while ($num2 === 0 || $num1 % $num2 !== 0) {
$num1 = rand(1, 9);
$num2 = rand(1, 9);
}
$result = (int)($num1 / $num2);
break;
case '*':
$result = $num1 * $num2;
break;
default:
$result = $num1 + $num2;
break;
}
$token = bin2hex(random_bytes(16));
$expire = $now + 300; //5分钟
$wpdb->insert(
MATH_CAPTCHA_TABLE,
[
'captcha_key' => $token,
'answer' => $result,
'expire' => $expire,
'createtime' => $now
],
['%s','%d','%d','%d']
);
echo '<div class="math-captcha-wrap">';
echo esc_html($num1) . ' ' . esc_html($op) . ' ' . esc_html($num2) . ' = ';
echo '<input type="text" name="math_captcha" class="math-captcha-input" autocomplete="off" required>';
echo '<input type="hidden" name="captcha_token" value="' . esc_attr($token) . '">';
echo '</div>';
}
// 评论验证码校验【适配独立数据表 + AJAX】
add_filter('preprocess_comment', function ($commentdata) {
if (current_user_can('edit_posts')) return $commentdata;
global $wpdb;
$now = time();
$input = isset($_POST['math_captcha']) ? trim($_POST['math_captcha']) : '';
$token = isset($_POST['captcha_token']) ? sanitize_text_field($_POST['captcha_token']) : '';
if (empty($input) || empty($token)) {
if(wp_doing_ajax()){
wp_send_json_error('<strong>错误</strong>:请填写计算结果。');
}
wp_die('<strong>错误</strong>:请填写计算结果。', '验证失败', ['response' => 403]);
}
$row = $wpdb->get_row($wpdb->prepare(
"SELECT answer FROM " . MATH_CAPTCHA_TABLE . " WHERE captcha_key = %s AND expire > %d LIMIT 1",
$token, $now
));
if (!$row) {
if(wp_doing_ajax()){
wp_send_json_error('<strong>错误</strong>:验证码已过期,请刷新页面。');
}
wp_die('<strong>错误</strong>:验证码已过期,请刷新页面。', '验证失败', ['response' => 403]);
}
if ((int)$input !== (int)$row->answer) {
if(wp_doing_ajax()){
wp_send_json_error('<strong>错误</strong>:答案错误,请重试。');
}
wp_die('<strong>错误</strong>:答案错误,请重试。', '验证失败', ['response' => 403]);
}
//验证通过直接删除,一次性token
$wpdb->delete(MATH_CAPTCHA_TABLE, ['captcha_key' => $token], ['%s']);
return $commentdata;
}, 8);
1.彻底移除 set_transient / get_transient / delete_transient,不再读写wp_options
2.独立表 yanzhengma 单独存放验证码,不和站点配置混杂
30增加 used=1 核销机制:验证成功后标记,同一个 token 无法重复提交评论(原生 transient 无法天然实现)
4.定时任务 SQL 修改:不再清理 options 表,直接删除表内过期记录
5.写入时自动清理当前 IP 旧验证码,减少无效数据堆积
如果你想立刻手动清理过期数据,直接执行 SQL:
TRUNCATE TABLE yanzhengma;
如果你和我一样,把上面的PHP代码想保存在单独的文件里,不放入functions.php里,使用下面的代码
<?php
/**
* 算术验证码独立文件
* 独立数据表 yanzhengma
*/
if (!defined('ABSPATH')) {
exit;
}
// 定义验证码数据表名
if (!defined('MATH_CAPTCHA_TABLE')) {
define('MATH_CAPTCHA_TABLE', 'yanzhengma');
}
/**
* 1-9 加减乘除算术验证码 - 输出HTML
*/
function math_captcha_show()
{
if (current_user_can('edit_posts')) return;
global $wpdb;
$now = time();
// 5%概率清理过期数据,限制单次删除行数,防止锁表
if (mt_rand(1, 500) <= 1) {
$wpdb->query($wpdb->prepare("DELETE FROM " . MATH_CAPTCHA_TABLE . " WHERE expire < %d LIMIT 100", $now));
}
$ops = ['+', '-', '*', '/'];
$op = $ops[rand(0, 3)];
$num1 = rand(1, 9);
$num2 = rand(1, 9);
$result = 0;
switch ($op) {
case '-':
if ($num1 < $num2) list($num1, $num2) = [$num2, $num1];
$result = $num1 - $num2;
break;
case '/':
while ($num2 === 0 || $num1 % $num2 !== 0) {
$num1 = rand(1, 9);
$num2 = rand(1, 9);
}
$result = (int)($num1 / $num2);
break;
case '*':
$result = $num1 * $num2;
break;
default:
$result = $num1 + $num2;
break;
}
$token = bin2hex(random_bytes(16));
$expire = $now + 300; //5分钟有效期
$wpdb->insert(
MATH_CAPTCHA_TABLE,
[
'captcha_key' => $token,
'answer' => $result,
'expire' => $expire,
'createtime' => $now
],
['%s','%d','%d','%d']
);
echo '<div class="math-captcha-wrap">';
echo esc_html($num1) . ' ' . esc_html($op) . ' ' . esc_html($num2) . ' = ';
echo '<input type="text" name="math_captcha" class="math-captcha-input" autocomplete="off" required>';
echo '<input type="hidden" name="captcha_token" value="' . esc_attr($token) . '">';
echo '</div>';
}
/**
* 评论验证码校验【适配独立数据表 + AJAX评论】
*/
add_filter('preprocess_comment', function ($commentdata) {
if (current_user_can('edit_posts')) return $commentdata;
global $wpdb;
$now = time();
$input = isset($_POST['math_captcha']) ? trim($_POST['math_captcha']) : '';
$token = isset($_POST['captcha_token']) ? sanitize_text_field($_POST['captcha_token']) : '';
if (empty($input) || empty($token)) {
if(wp_doing_ajax()){
wp_send_json_error('<strong>错误</strong>:请填写计算结果。');
}
wp_die('<strong>错误</strong>:请填写计算结果。', '验证失败', ['response' => 403]);
}
$row = $wpdb->get_row($wpdb->prepare(
"SELECT answer FROM " . MATH_CAPTCHA_TABLE . " WHERE captcha_key = %s AND expire > %d LIMIT 1",
$token, $now
));
if (!$row) {
if(wp_doing_ajax()){
wp_send_json_error('<strong>错误</strong>:验证码已过期,请刷新页面。');
}
wp_die('<strong>错误</strong>:验证码已过期,请刷新页面。', '验证失败', ['response' => 403]);
}
if ((int)$input !== (int)$row->answer) {
if(wp_doing_ajax()){
wp_send_json_error('<strong>错误</strong>:答案错误,请重试。');
}
wp_die('<strong>错误</strong>:答案错误,请重试。', '验证失败', ['response' => 403]);
}
//验证成功立刻销毁token,一次性使用
$wpdb->delete(MATH_CAPTCHA_TABLE, ['captcha_key' => $token], ['%s']);
return $commentdata;
}, 8);
可选优化(高并发场景)
增加 MySQL 事件自动清理,脱离 WP 定时任务(WP 定时任务依赖访客访问触发)
CREATE EVENT IF NOT EXISTS evt_clear_yanzhengma ON SCHEDULE EVERY 6 HOUR STARTS CURRENT_TIMESTAMP DO DELETE FROM yanzhengma WHERE expire < UNIX_TIMESTAMP();
最后还是
修改comments.php,在合适的位置添加调用代码:
<?php math_captcha_show(); ?>
这样就可以了。

写的很好,学习一下
我发现一个现象, 就是 AI 出来以后像极验的滑块几乎都被破解了, 5 分钱一次打码, 通过率还接近 100%, 而好多老站采用的都是自己写的验证, 图片和文字暴露出来却一点事都没有, 理应更容易遭 Span 的, 看来还是老一辈的技术强