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

PHPMailer发送邮件重复问题及批量发件安全浏览器咨询

邮件重复发送排查与批量发送方案建议

问题描述

我用PHPMailer编写的邮件脚本偶尔会出现重复发送的情况(并非每次都发生)。试过在无扩展的Chrome中运行,问题依旧。邮件主题包含随机数,可以确认是同一封邮件被多次送达。另外想咨询:有没有适合通过PHPMailer进行批量邮件发送的安全浏览器?

以下是我的脚本代码:

if(!isset($_SESSION[$reciever]))
{
    $_SESSION[$reciever] = 1;

require "mail_inhoud.php";                          // <== IMPORTANT INCLUDE

$maand = date('Ym'); // 202103
$datum = date('Ymd'); // '20210406'
$datum_tijd = date('Ymd H:i'); // '20210406'

$maand_ENG = 'March'; 
$teller = 1;

$raw_results = mysql_query("SELECT EMAIL FROM EMAILS_TESTBANK") or die(mysql_error());       

//Create a new PHPMailer instance
$mail = new PHPMailer();

$mail->SMTPKeepAlive = true; // SMTP connection will not close after each email sent, reduces SMTP overhead

$mail->CharSet = 'UTF-8'; 
//$mail->isSMTP(); 
$mail->Mailer = 'sendmail'; 
$mail->SMTPAuth   = true;
$mail->Host = 'xxxxxx';
$mail->Port = 587;
$mail->Username   = "xxxxx" ;  
$mail->Password   = "xxxxxx"; 
$mail->SMTPSecure = "tls";
$mail->setFrom('lucas.tessens@mers.be', 'MERS Antique Books Antwerp');
$mail->Sender = 'bounced@mers.be'; 
$mail->addReplyTo('lucas.tessens@mers.be', 'MERS Antique Books Antwerp'); 

$mail->Subject = 'Test Mail ' . rand();

$ONDERWERP =  "Catalogue for $maand_ENG";
$mail->Encoding = 'base64';

$today = date('Ymd'); 

if(mysql_num_rows($raw_results) > 0){
    while($row = mysql_fetch_array($raw_results)){
        $email = $row['EMAIL'];
        $email_user = $row['EMAIL'];

        /* We stellen de INHOUD OP */
        $message = "";
        $message .= "<html>";
        $message .= "<body>";

        $message .= "<table width='100%' border='0' cellspacing='0' cellpadding='0'>";
        $message .= "<tr><td align='center' style='width:600px'>";

        $htmlContent = file_get_contents("$maand/INTERNATIONAAL.html");
        $message .= $htmlContent;
        $message .= "<br><br>";
        $message .= "<div style='text-align:center'>";
        $message .= "<span>This email was sent to $email</span> | <a href='https://www.mers.be/unsubscribe.php?EMAIL=$email' target='new' style='color:#666; text-decoration:none'>Unsubscribe</a>";
        $message .= "<br><br>";
        $message .= "<div style='visibility:hidden !important; opacity:0 !important; display:none !important' >";
        $message .= "<img style='visibility:hidden !important' src='https://www.mers.be/backend/MAILING_NEW/mailcheck.php?user=$email&subject=$ONDERWERP'>";
        $message .= "</div>";
        $message .= "</div>";

        $message .= "</td></tr>";
        $message .= "</table>";
        //$message .= $teller;

        $message .= "</body>";
        $message .= "</html>";
        
        $body = $message; 
        
        $mail->msgHTML($body);
        $mail->AltBody = 'To view the message, please use an HTML compatible email viewer!'; 

        $raw_check = mysql_query("SELECT EMAIL FROM EMA_CONTROLE WHERE EMAIL = '$email_user' AND DATE = '$today'") or die(mysql_error()); 
        
        if(mysql_num_rows($raw_check) > 0){
            // DO NOTHING, EMAIL WAS ALREADY SENT
            echo 'EMAIL ALREADY SENT<br>';
        }else{
            echo 'NOT YET SEND<br>';
            $mail->addAddress($email, 'Receiver name');
            $mail->send(); // SEND EMAIL
            $mail->clearAddresses();
            $mail->clearAttachments();

            echo 'Message sent to :  (' . htmlspecialchars($row['EMAIL']) . ')<br>';
            //Mark it as sent in the DB
            $OND = "NEW ARRIVALS MARCH DEEL 1";

            mysql_query('SET NAMES UTF8'); 
            mysql_query("INSERT INTO `MAILING_VERZONDEN` 
            (`DATUM`, `EMAIL`, WELKE_MAILING, `TAAL`, `RECEIVER`) VALUES 
            ('$datum_tijd', '$email','$OND', 'INTERNATIONAAL', 'KLANT' )") or die(mysql_error());

            mysql_query("INSERT INTO `EMA_CONTROLE` ( `EMAIL`, `DATE`) VALUES ('$email','$today')") or die(mysql_error());
        }

echo $teller; echo '<br>';
$teller++;

}
}

}
else{
    echo "doing it more than once";
}

$conn = null;
$con = null;
exit;

问题排查与修复方案

1. 会话控制逻辑漏洞

你当前的会话判断$_SESSION[$reciever]存在问题:如果$reciever变量未定义,会变成$_SESSION[''],等于没有做有效拦截。用户刷新页面或重复请求时,这个判断会失效,导致脚本重复执行。

  • 修复:给$reciever设置固定唯一标识,确保会话变量有效:
    $reciever = 'mail_batch_send_lock';
    if(!isset($_SESSION[$reciever])) {
        $_SESSION[$reciever] = 1;
        // 后续执行代码
    } else {
        echo "脚本正在执行,请勿重复触发";
        exit;
    }
    

2. 数据库操作的原子性问题

当前逻辑是先查询EMA_CONTROLE表,再发送邮件,最后插入记录——这个过程不是原子操作。如果脚本被重复触发,第二次请求可能在第一次插入记录前就通过了查询判断,导致重复发送。

  • 修复:
    • 给EMA_CONTROLE表的EMAIL和DATE字段添加联合唯一索引,这样插入重复记录时会直接报错,从数据库层面阻止重复发送。
    • 把发送邮件和插入数据库的操作放在事务中,确保要么全部成功,要么全部回滚。

3. PHPMailer配置冲突

你同时设置了$mail->Mailer = 'sendmail';和SMTP相关参数(SMTPAuth、Host等),这会导致配置混乱:sendmail模式不需要SMTP参数,SMTP模式需要启用$mail->isSMTP();。

  • 修复:二选一调整配置:
    • 用SMTP模式:注释掉$mail->Mailer = 'sendmail';,打开$mail->isSMTP();
    • 用sendmail模式:删除所有SMTP相关配置(SMTPAuth、Host、Port、Username、Password、SMTPSecure)

4. 脚本超时与重复触发

批量发送邮件耗时较长,容易触发浏览器超时,用户可能会刷新页面;或者PHP执行时间限制不足,导致脚本中断后被重新触发。

  • 修复:
    • 在脚本开头添加set_time_limit(0);,取消执行时间限制(或设置足够大的数值)
    • 不要通过浏览器运行批量发送脚本,改用命令行执行(php your_script.php),或者服务器定时任务(如Crontab),这是更稳定的方式

关于批量发送的安全浏览器建议

批量发送邮件不建议通过浏览器运行,因为浏览器容易出现超时、重复请求的问题。如果一定要用,Chrome、Firefox、Edge等主流安全浏览器都可以,但要注意:

  • 关闭浏览器的自动刷新、重试功能
  • 脚本执行期间不要操作页面
  • 优先选择命令行或服务器定时任务的方式,这才是批量发送邮件的安全稳定方案

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 05:13:11