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

PHPMailer批量邮件发送故障:循环中邮件失效但数据库更新正常

批量处理邮件发送失效排查与修复

问题背景

原本实现了单条数据点击按钮查询后发送邮件的功能,现在改造成多选批量处理:循环更新选中数据状态(数据库更新正常),但将邮件代码移入循环后,邮件完全发不出去。尝试调整邮件代码位置、移除主查询都没用,怀疑SELECT查询有问题但无法验证,需要排查原因。

引入文件

include(dirname(__DIR__).'/db.php');
include(dirname(__DIR__).'/phpmailer/src/Exception.php');
include(dirname(__DIR__).'/phpmailer/src/PHPMailer.php');
include(dirname(__DIR__).'/phpmailer/src/SMTP.php');

use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;

原有嵌套代码

else if ($category=="pend")
{
        if ($val==1){
            foreach ($all_id as $value) {
                $query = mysqli_query($db,"SELECT * FROM tbl_person where app_id='$value' and app_status='2';"); 
                while($row= mysqli_fetch_array($query)) {
                    $email = $row["app_email"];
                    $name = $row["app_fname"]." " . $row["app_mname"].". ".$row["app_lname"]; 
                }
                $update = "Update tbl_person set app_status=3 WHERE app_id='$value'";
                $mail = new PHPMailer(true);

                $mail->isSMTP();
                $mail->SMTPAuth = true;

                $mail->Host = 'smtp.office365.com';
                $mail->Port       = 587;
                $mail->SMTPSecure = 'tls';
                $mail->SMTPAuth   = true;
                $mail->Username = 'useremail'; //sample
                $mail->Password = 'emailpassword'; //sample
                $mail->SetFrom('useremail', 'username'); //sample
                $mail->addAddress($email, $name);
                $mail->IsHTML(true);

                $mail->Subject = 'Application Status';
                $mail->Body    = 'Message Body Here';
                $mail->AltBody = 'Message Alt Body Here';
                if (mysqli_query($db,$update)) 
                {
                    header('location:../application.php');
                }
            }
        }
        else if ($val==2){
            foreach ($all_id as $value) {
                $update = "Update tbl_person set app_status=4 WHERE app_id='$value'";
                if (mysqli_query($db,$update)) {
                    header('location:../application.php');
            }
            }
        }
        else{
            echo "Something went wrong";
        }
}

核心问题排查

  • 循环被提前跳转中断:每次更新成功后直接调用header跳转,循环只执行第一次就结束了,后面的邮件发送和数据更新都没机会执行。
  • 邮件根本没触发发送:代码里只配置了PHPMailer的参数,漏掉了最关键的$mail->send()调用,相当于只装了子弹没扣扳机。
  • SELECT查询的冗余与风险:app_id是主键的话,while循环完全多余;而且没处理查询失败的情况——如果查不到数据,$email和$name要么是空值,要么保留上一次循环的旧值,邮件自然发不出去。
  • 无错误捕获机制:PHPMailer开启了异常模式,但没加try/catch,就算邮件发送出错(比如SMTP连接失败、邮箱无效),也看不到任何报错信息,无法排查问题。
  • SQL注入隐患:直接把$value拼进SQL语句,不仅不安全,还可能因为特殊字符导致SQL语法错误。

修复后的代码

else if ($category=="pend")
{
    if ($val==1){
        $isAllSuccess = true;
        foreach ($all_id as $value) {
            // 用预编译语句查询用户信息,防注入+避免语法错误
            $stmt = mysqli_prepare($db, "SELECT app_email, app_fname, app_mname, app_lname FROM tbl_person WHERE app_id=? AND app_status='2'");
            mysqli_stmt_bind_param($stmt, "s", $value);
            mysqli_stmt_execute($stmt);
            $result = mysqli_stmt_get_result($stmt);
            
            // 确认查到有效数据再执行后续操作
            if ($row = mysqli_fetch_assoc($result)) {
                $email = $row["app_email"];
                $name = $row["app_fname"] . " " . $row["app_mname"] . ". " . $row["app_lname"];
                
                // 更新数据状态
                $updateStmt = mysqli_prepare($db, "UPDATE tbl_person SET app_status=3 WHERE app_id=?");
                mysqli_stmt_bind_param($updateStmt, "s", $value);
                if (!mysqli_stmt_execute($updateStmt)) {
                    $isAllSuccess = false;
                    error_log("更新状态失败,ID: $value,错误: " . mysqli_error($db));
                    continue;
                }
                
                // 发送邮件并捕获异常
                try {
                    $mail = new PHPMailer(true);
                    $mail->isSMTP();
                    $mail->SMTPAuth = true;
                    $mail->Host = 'smtp.office365.com';
                    $mail->Port = 587;
                    $mail->SMTPSecure = 'tls';
                    $mail->Username = 'useremail'; // 替换为实际发件邮箱
                    $mail->Password = 'emailpassword'; // 替换为实际密码
                    $mail->setFrom('useremail', 'username'); // 替换为实际发件人信息
                    $mail->addAddress($email, $name);
                    $mail->isHTML(true);
                    $mail->Subject = 'Application Status';
                    $mail->Body = 'Message Body Here';
                    $mail->AltBody = 'Message Alt Body Here';
                    
                    // 关键:执行邮件发送
                    $mail->send();
                } catch (Exception $e) {
                    $isAllSuccess = false;
                    error_log("邮件发送失败,ID: $value,错误信息: " . $mail->ErrorInfo);
                }
            } else {
                $isAllSuccess = false;
                error_log("未找到符合条件的数据,ID: $value");
            }
        }
        // 所有操作完成后统一跳转
        if ($isAllSuccess) {
            header('location:../application.php');
            exit;
        } else {
            echo "部分操作失败,请查看服务器日志排查";
        }
    }
    else if ($val==2){
        $isAllSuccess = true;
        foreach ($all_id as $value) {
            $updateStmt = mysqli_prepare($db, "UPDATE tbl_person SET app_status=4 WHERE app_id=?");
            mysqli_stmt_bind_param($updateStmt, "s", $value);
            if (!mysqli_stmt_execute($updateStmt)) {
                $isAllSuccess = false;
                error_log("更新状态失败,ID: $value,错误: " . mysqli_error($db));
                break;
            }
        }
        if ($isAllSuccess) {
            header('location:../application.php');
            exit;
        } else {
            echo "部分更新失败,请查看服务器日志";
        }
    }
    else{
        echo "参数错误";
    }
}

修复点说明

  1. 移除循环内跳转:等所有批量操作执行完毕后再统一跳转,确保每条数据都被处理。
  2. 添加$mail->send():补上邮件发送的核心执行步骤。
  3. 异常捕获:用try/catch捕获PHPMailer的错误,通过日志记录失败原因,方便后续排查。
  4. SQL预编译:避免SQL注入风险,同时解决特殊字符导致的SQL语法错误。
  5. 查询结果校验:确认SELECT查询到有效数据后再执行后续操作,避免空值导致的报错。
  6. 状态标记:用isAllSuccess变量记录整体操作结果,给用户明确的反馈信息。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 08:24:55