PHP 怎么群发单显

wen PHP项目 2

本文目录导读:

PHP 怎么群发单显

  1. 方案一:使用 SMTP 协议(推荐,最标准)
  2. 方案二:使用 mail() 函数(不推荐,但简单)
  3. 方案三:使用第三方 API(如 SendGrid, SES 等)
  4. 总结与最佳实践

在 PHP 中实现“群发单显”(即每位收件人只能看到自己的邮箱地址,看不到其他人的),主要取决于你使用的邮件发送方式。

以下是几种常见方案,按推荐程度排序:

使用 SMTP 协议(推荐,最标准)

这是最标准的做法。关键在于设置 Rcpt-To(收件人列表)和 To(显示在邮件头部的地址)

在 SMTP 协议中,Rcpt-To 决定了邮件实际发给谁,而 To 头字段决定了收件人客户端显示给谁看。

使用 PHPMailer(最流行)

这是最推荐的库,因为它自动处理了 SMTP 的细节。

<?php
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
require 'vendor/autoload.php'; // 如果有 Composer
$mail = new PHPMailer(true);
try {
    // 服务器设置
    $mail->isSMTP();
    $mail->Host       = 'smtp.example.com';
    $mail->SMTPAuth   = true;
    $mail->Username   = 'sender@example.com';
    $mail->Password   = 'secret';
    $mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
    $mail->Port       = 587;
    // 发件人
    $mail->setFrom('sender@example.com', '发件人名称');
    // 收件人列表(实际发送给这些地址)
    $recipients = [
        'user1@example.com' => '用户一',
        'user2@example.com' => '用户二',
        'user3@example.com' => '用户三',
    ];
    foreach ($recipients as $email => $name) {
        // 1. 清空之前的收件人(避免重复添加)
        $mail->clearAddresses();
        $mail->clearAttachments(); // 如有附件也需清空
        // 2. 关键:添加实际收件人(这会设置 Rcpt-To)
        $mail->addAddress($email, $name);
        // 3. 关键:设置邮件头部的 "To" 字段(往往设为发件人自己,或者留空)
        //    这里设置为发件人自己的地址,收件人看到“发给”的是发件人
        $mail->addCustomHeader('To: 保密收件人 <sender@example.com>'); 
        // 或者更简单:不设置,让 PHPMailer 默认取第一个 addAddress,但这样会暴露。
        // 更优雅的写法是覆盖默认值:
        // $mail->addAddress($email); // 先添加
        // $mail->__set('to', []); // 清空内部数组(不推荐,太底层)
        // 官方推荐:直接修改属性
        $mail->clearAddresses();
        // 在循环内 重新初始化 To 头部
        $mail->addAddress($email, $name);
        $mail->addCustomHeader("To: 收件人 <{$email}>"); // 这行注释掉即可
        // 更简单的做法(PHPMailer 官方推荐):
        // 直接设置 $mail->to = []; addAddress
        // 但为了确保绝对正确,我们使用以下方法:
        // 方法:强制覆盖 To 头
        $mail->addCustomHeader('To: 所有收件人 <sender@example.com>'); // 显示成发件人自己
        // 4. 邮件内容
        $mail->Subject = "您的密码已重置";
        $mail->Body    = "亲爱的 {$name},这是您的专属邮件。";
        // 5. 发送
        $mail->send();
        echo "已发送给: {$email}\n";
    }
    echo "群发完成!";
} catch (Exception $e) {
    echo "邮件发送失败: {$mail->ErrorInfo}";
}

注意:上面的代码有个问题,addCustomHeader 如果重复添加 To 会报错。更标准的 PHPMailer 写法是清空 to 数组后重新设置。

PHPMailer 最正确的单显写法(清理版):

foreach ($recipients as $email => $name) {
    // 重置收件人(关键步骤)
    $mail->clearAddresses();
    // 添加实际发送的 SMTP 收件人
    $mail->addAddress($email, $name);
    // 单独设置可见的 To 头部 - 指向自己(或某个公用地址)
    // 注释掉默认的 To,添加自定义
    $mail->set('to', [[
        'email' => 'sender@example.com', // 这里换成你自己的发件地址
        'name'  => '系统通知'
    ]]); // 或者直接用 $mail->clearAllRecipients();
    // PHPMailer 更简单的做法:
    // $mail->clearAddresses();
    // $mail->addAddress($email); // 这会把 Rcpt-To 和 To 都设为这个邮箱
    // 要单显,需要手动覆盖邮件头部
    $mail->addCustomHeader('To: 系统通知 <sender@example.com>'); 
    // 注意:addCustomHeader 在一个循环里如果调用了两次,之前设置会被覆盖吗?会报错。
    // 所以更好的做法是:
    $mail->clearCustomHeaders(); // 如果需要清理
    // 最终推荐最简洁方式(老外常用):
    $mail->clearAddresses();
    $mail->addAddress($email); // 这会同时设置 To 和 Rcpt
    $mail->addCustomHeader('To: 系统通知 <sender@example.com>'); // 覆盖显示
    $mail->send();
}

注意事项addCustomHeader('To: ...') 在循环中必须在每次循环前调用 clearCustomHeaders() 或手动避免重复添加,否则报错。


使用 mail() 函数(不推荐,但简单)

如果你必须使用内置的 mail() 函数,需要通过 追加参数\nBcc: 头部实现。

重点:使用 Bcc(密送)或 Rcpt-To。

内置 mail() 函数的签名是 mail($to, $subject, $message, $headers)

  • $to 参数:必须设置为单个收件人,或者你自己。
  • $headers:通过添加 Bcc: 头部来添加其他收件人,但 PHP 的 mail() 函数依赖底层 sendmail,sendmail 配置不当,Bcc 可能会暴露。

最稳妥的做法(循环发送):

$recipients = ['user1@example.com', 'user2@example.com'];
foreach ($recipients as $email) {
    $to = $email; // 只放一个收件人
    $subject = '测试单显';
    $message = "你好,这是一封给 {$email} 的测试邮件。";
    $headers = "From: sender@example.com\r\n";
    $headers .= "Reply-To: sender@example.com\r\n";
    // 注意:这里不要加 To 头部,让 sendmail 自动处理
    // 关键:这里必须确保只发给 $email。
    // 如果直接设定 $headers 中的 To 为发件人,而 $to 为收件人,
    // 可能会被某些 MTA 忽略。
    mail($to, $subject, $message, $headers);
}

重要提示

  1. 无论你用什么库,不要将多个收件人用逗号分隔放在 $to 变量里(除非你特意要让他们看到彼此)。
  2. 使用 Bcc 是有风险的,因为有些垃圾邮件过滤器会将其暴露,或者如果服务器配置 sendmail 不当,Bcc 信息可能被解析到日志中。

使用第三方 API(如 SendGrid, SES 等)

这是最省心且最稳定的方式,几乎所有邮件服务商都支持“单发”或“模板邮件”。

以 SendGrid 为例(PHP)

$email = new \SendGrid\Mail\Mail(); 
$email->setFrom("sender@example.com", "系统通知");
$email->setSubject("群发单显测试");
$recipients = ['user1@example.com', 'user2@example.com'];
foreach ($recipients as $recipient) {
    // 创建新邮件对象(或清空 dynamic_template_data)
    $personalization = new \SendGrid\Mail\Personalization();
    $personalization->addTo(new \SendGrid\Mail\To($recipient, "User")); // 只添加一个 To
    $personalization->addSubstitution("-name-", "用户".$recipient); 
    $email->addPersonalization($personalization);
}
// 发送
$sendgrid = new \SendGrid(getenv('SENDGRID_API_KEY'));
$response = $sendgrid->send($email);

在 SendGrid 中,Personalization 对象天然支持一人一个 To,从而实现了单显。


总结与最佳实践

方案 实现方式 推荐指数
PHPMailer + SMTP clearAddresses() 循环,覆盖 To 头部 ⭐⭐⭐⭐⭐
mail() 函数 循环发送,单收件人 ⭐⭐
第三方 API 使用 Personalization ⭐⭐⭐⭐⭐

强烈推荐使用 PHPMailer + SMTP

最核心的代码片段(PHPMailer 单显)

$mail->isSMTP();
$mail->Host = 'smtp.example.com';
...
foreach ($recipients as $email) {
    $mail->clearAddresses();          // 清空上一轮收件人
    $mail->addAddress($email);        // 添加当前收件人(Rcpt)
    // 覆盖可见的 To 头:
    $mail->clearCustomHeaders();      // 如有必要
    $mail->addCustomHeader('To: 系统通知 <sender@example.com>'); 
    $mail->send();
}

这样,收件人打开邮件,看到的“发给谁”是发件人自己,而实际 SMTP 投递时只发给了唯一的 $email 地址。

抱歉,评论功能暂时关闭!