You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用wp_mail与wp cron后台发批量邮件遇问题求助

WordPress批量邮件与WP Cron问题排查与修复

问题描述

尝试通过wp_mail和WP Cron后台发送批量邮件,代码可正常发送邮件,但存在两个核心问题:

  • 当邮箱数组包含900+条数据时,页面会持续加载直至任务完成,无法先返回成功提示再在后台执行,不符合WP Cron后台运行的预期。
  • 使用wp_schedule_single_event创建的定时任务,无法在Advanced Cron Manager插件的Scheduled Actions中查看,看似函数已执行,但定时任务并未正确创建。

原始代码:

set_time_limit(0);
function send_newsletter()
{
    $i = 0;
    foreach ($emails as $email)
    {
        $i++;

        if ($i < count($emails))
        {
            sleep(60);
            wp_mail($email, $subject, $msg, $headers);
            continue;

         }else{
            break;
         }
    }

}

$args = array( false );

if (!wp_next_scheduled('subscribers_newsletter')) {

    wp_schedule_single_event( time() + 10, 'subscribers_newsletter', $args );

    add_action( 'subscribers_newsletter', 'send_newsletter' );
    do_action( 'subscribers_newsletter' );

}else{
     echo "Wait until the previous job finished";
}

问题1:页面持续加载,未后台执行

原因

代码中直接调用了do_action('subscribers_newsletter'),这会立即同步执行任务函数,而非交给WP Cron在后台触发。WP Cron本质是伪定时任务,依赖后续站点访问触发任务,手动触发action会让任务在当前请求内同步跑完,导致页面卡死。此外,单次循环900+邮件并加上sleep(60),会远超服务器和WP Cron的超时限制,进一步加剧页面阻塞。

解决方案

  1. 移除do_action('subscribers_newsletter')调用,仅保留任务调度逻辑,让WP Cron在指定时间自动触发。
  2. 拆分任务:每次只发送小批量邮件(比如10封),完成后调度下一次任务,直到所有邮件发送完毕,避免单次执行时间过长。

问题2:定时任务在插件中无法查看

原因

  1. add_action放在了调度条件判断内部,仅第一次调度时才绑定钩子,后续无法正确触发;且钩子绑定应在WP初始化阶段完成,而非请求时临时绑定。
  2. 手动调用do_action会直接执行任务,导致调度的任务被标记为已完成,插件中自然无法看到。
  3. 传递的$args参数未被函数接收,虽不致命,但可能导致钩子匹配异常。

解决方案

  1. 将add_action放在全局位置(比如主题functions.php或插件主文件开头),确保WP初始化时完成钩子绑定。
  2. 移除do_action调用,让任务完全由WP Cron调度触发。
  3. 按需传递参数,若无需参数可省略$args。

修正后的代码示例

// 全局绑定钩子,确保WP初始化时加载
add_action('subscribers_newsletter', 'send_newsletter_batch');

// 分批发送邮件的核心函数
function send_newsletter_batch() {
    // 从WP选项读取待发送数据(避免变量作用域问题)
    $pending_emails = get_option('newsletter_pending_emails', []);
    $subject = get_option('newsletter_subject', '');
    $msg = get_option('newsletter_message', '');
    $headers = get_option('newsletter_headers', ['Content-Type: text/html; charset=UTF-8']);

    if (empty($pending_emails)) {
        // 无待发邮件,清理存储的内容
        delete_option('newsletter_pending_emails');
        delete_option('newsletter_subject');
        delete_option('newsletter_message');
        delete_option('newsletter_headers');
        return;
    }

    // 单次发送10封,可根据服务器配置调整
    $batch_size = 10;
    $current_batch = array_slice($pending_emails, 0, $batch_size);
    $remaining_emails = array_slice($pending_emails, $batch_size);

    // 发送当前批次邮件
    foreach ($current_batch as $email) {
        wp_mail($email, $subject, $msg, $headers);
        sleep(2); // 短间隔避免触发邮件服务商限制
    }

    // 若有剩余邮件,调度下一次任务(5分钟后执行)
    if (!empty($remaining_emails)) {
        update_option('newsletter_pending_emails', $remaining_emails);
        if (!wp_next_scheduled('subscribers_newsletter')) {
            wp_schedule_single_event(time() + 300, 'subscribers_newsletter');
        }
    }
}

// 触发任务的入口(可放在后台页面或短代码中)
function trigger_newsletter() {
    // 替换为你的900+邮箱数组、主题、内容
    $all_emails = ['email1@example.com', 'email2@example.com', ...];
    $subject = '订阅更新通知';
    $msg = '这是你的批量邮件内容';

    // 检查是否已有任务在运行
    if (wp_next_scheduled('subscribers_newsletter')) {
        echo '已有邮件发送任务在进行,请等待完成';
        return;
    }

    // 将待发送数据存储到WP选项
    update_option('newsletter_pending_emails', $all_emails);
    update_option('newsletter_subject', $subject);
    update_option('newsletter_message', $msg);

    // 调度第一次任务(10秒后执行)
    wp_schedule_single_event(time() + 10, 'subscribers_newsletter');

    echo '邮件发送任务已启动,将在后台分批发送';
}

额外提示

  • 若站点访问量低,建议配置服务器真实CronJob触发wp-cron.php,确保任务按时执行(避免依赖站点访问触发的延迟)。
  • 调整$batch_size和调度间隔时,需参考服务器超时设置和邮件服务商的发送限制,避免被封禁。

内容的提问来源于stack exchange,提问作者Pixie

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 10:17:22