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

如何在WordPress中用Cron和Action Scheduler异步执行wp_mail避免超时

WordPress发布自定义文章后异步发送订阅邮件的解决方案

原代码的核心问题

  1. 同步调用wp_mail,订阅用户较多时会阻塞文章发布流程,极易触发服务器超时
  2. 邮件头手动拼接存在格式错误(转义引号、换行符混用),可能导致邮件发送失败或被标记为垃圾邮件
  3. 邮件内容格式不规范,链接未转为可点击的HTML元素,用户体验差

推荐解决方案:使用WP Cron异步执行

WP Cron是WordPress内置的后台任务调度系统,能把邮件发送任务放到后台执行,完全不影响文章发布流程。

修改后的完整代码

// 注册自定义Cron事件的回调钩子
add_action('send_calendrier_subscriber_emails', 'send_calendrier_emails_callback', 10, 1);

// 监听文章状态切换,调度邮件发送任务
add_action('transition_post_status', 'schedule_mails_on_publish', 10, 3);
function schedule_mails_on_publish($new_status, $old_status, $post) {
    // 仅在首次发布「gestion_calendrier」类型文章时触发
    if (
        'publish' !== $new_status || 
        'publish' === $old_status || 
        'gestion_calendrier' !== get_post_type($post)
    ) {
        return;
    }

    // 避免重复调度,检查是否已有待执行的任务
    if (!wp_next_scheduled('send_calendrier_subscriber_emails', array($post->ID))) {
        // 立即执行任务,也可以设置延迟(比如 time() + 60 表示1分钟后执行)
        wp_schedule_single_event(time(), 'send_calendrier_subscriber_emails', array($post->ID));
    }
}

// 实际执行邮件发送的回调函数
function send_calendrier_emails_callback($post_id) {
    $post = get_post($post_id);
    // 容错:验证文章存在且类型正确
    if (!$post || 'gestion_calendrier' !== get_post_type($post)) {
        return;
    }

    // 用数组形式构建邮件头,避免手动拼接的格式错误
    $headers = array(
        'From' => 'Your Site <example@gmail.com>',
        'Reply-To' => 'example@gmail.com',
        'Content-Type' => 'text/html; charset=UTF-8',
        'MIME-Version' => '1.0',
        'Content-Transfer-Encoding' => '8bit'
    );

    // 获取所有订阅用户的邮箱
    $subscribers = get_users(array('role' => 'subscriber'));
    $emails = array_column($subscribers, 'user_email'); // 简化数组提取

    // 构建规范的HTML邮件内容,转义链接避免安全风险
    $post_link = esc_url(get_permalink($post));
    $body = sprintf(
        '<p>Hey, 有新的日程条目发布啦!</p>
        <p>点击查看:<a href="%s">%s</a></p>',
        $post_link,
        get_the_title($post)
    );

    // 只有邮箱列表非空时才发送邮件
    if (!empty($emails)) {
        wp_mail($emails, '新日程条目发布!', $body, $headers);
    }
}

方案优势

  • 异步无阻塞:邮件发送在后台执行,文章发布瞬间完成,不会超时
  • 格式规范:用数组传递邮件头,避免手动拼接的转义错误
  • 安全可靠:用esc_url处理链接,防止XSS风险;添加容错检查,避免无效执行
  • 原生支持:无需额外插件,依赖WordPress自带功能,兼容性好

替代方案:异步HTTP请求(适用于WP Cron失效场景)

如果服务器禁用了WP Cron,可以用PHP curl发起异步请求,调用后台AJAX端点发送邮件:

代码示例

// 注册后台AJAX端点,仅允许有权限的用户访问
add_action('wp_ajax_send_calendrier_mails', 'ajax_send_calendrier_mails');
function ajax_send_calendrier_mails() {
    if (!current_user_can('publish_posts')) {
        wp_die('无权限操作');
    }

    $post_id = isset($_POST['post_id']) ? intval($_POST['post_id']) : 0;
    $post = get_post($post_id);
    if (!$post || 'gestion_calendrier' !== get_post_type($post)) {
        wp_die('无效文章');
    }

    // 复用邮件发送逻辑
    $headers = array(
        'From' => 'Your Site <example@gmail.com>',
        'Reply-To' => 'example@gmail.com',
        'Content-Type' => 'text/html; charset=UTF-8'
    );

    $subscribers = get_users(array('role' => 'subscriber'));
    $emails = array_column($subscribers, 'user_email');

    $post_link = esc_url(get_permalink($post));
    $body = sprintf(
        '<p>Hey, 有新的日程条目发布啦!</p>
        <p>点击查看:<a href="%s">%s</a></p>',
        $post_link,
        get_the_title($post)
    );

    if (!empty($emails)) {
        wp_mail($emails, '新日程条目发布!', $body, $headers);
    }

    wp_die('发送成功');
}

// 文章发布时触发异步请求
add_action('transition_post_status', 'trigger_async_mail_request', 10, 3);
function trigger_async_mail_request($new_status, $old_status, $post) {
    if (
        'publish' !== $new_status || 
        'publish' === $old_status || 
        'gestion_calendrier' !== get_post_type($post)
    ) {
        return;
    }

    // 构建AJAX请求参数
    $url = admin_url('admin-ajax.php');
    $data = array(
        'action' => 'send_calendrier_mails',
        'post_id' => $post->ID,
        'nonce' => wp_create_nonce('send_calendrier_mails_nonce') // 安全验证
    );

    // 用curl发起异步请求,设置超时1秒立即返回
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, 1);
    curl_exec($ch);
    curl_close($ch);
}

注意事项

  • 若订阅用户数量极大,建议分批发送邮件(比如每次发50个),避免被邮件服务商判定为垃圾邮件
  • 可以用「WP Crontrol」插件检查WP Cron的运行状态,确保任务能正常触发
  • 测试阶段先用少量测试用户验证邮件发送效果,再推广到正式环境

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 17:40:29