PHP 內建 mail() 功能有限,生產環境強烈建議使用 **PHPMailer** 透過 SMTP 發送,支援認證、加密、HTML/文字多格式、附件、郵件佇列。本文以 QQ 企業郵箱 (騰訊企業郵) 為例,提供完整可用程式碼與常見坑點解析。
noreply@yourdomain.com)composer require phpmailer/phpmailer
<?php
namespace App\Mail;
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
class QqMailer {
private PHPMailer $mailer;
private array $config;
public function __construct(array $config) {
$this->config = $config;
$this->mailer = new PHPMailer(true); // true = 啟用例外
// SMTP 設定
$this->mailer->isSMTP();
$this->mailer->Host = $config['host'] ?? 'smtp.exmail.qq.com';
$this->mailer->SMTPAuth = true;
$this->mailer->Username = $config['username'];
$this->mailer->Password = $config['password']; // 授權碼
$this->mailer->SMTPSecure = $config['secure'] ?? PHPMailer::ENCRYPTION_STARTTLS; // TLS
$this->mailer->Port = $config['port'] ?? 587;
$this->mailer->CharSet = 'UTF-8';
$this->mailer->Timeout = 30;
// 除錯模式 (僅開發環境)
if (($config['debug'] ?? false)) {
$this->mailer->SMTPDebug = 2; // 2 = 詳細輸出
$this->mailer->Debugoutput = function($str, $level) {
error_log("PHPMailer [$level]: $str");
};
}
}
/**
* 發送郵件
*
* @param string|array $to 收件者 [email => name] 或單一 email
* @param string $subject 主旨
* @param string $htmlBody HTML 內容
* @param string|null $textBody 純文字備選內容
* @param array $attachments 附件 ['path' => '/path/file.pdf', 'name' => 'file.pdf']
* @param array $options 額外選項 ['cc', 'bcc', 'replyTo']
* @return bool
* @throws Exception
*/
public function send(
$to,
string $subject,
string $htmlBody,
?string $textBody = null,
array $attachments = [],
array $options = []
): bool {
// 寄件者
$this->mailer->setFrom($this->config['username'], $this->config['from_name'] ?? '系統通知');
// 收件者
if (is_array($to)) {
foreach ($to as $email => $name) {
$this->mailer->addAddress($email, $name);
}
} else {
$this->mailer->addAddress($to);
}
// CC/BCC
foreach (($options['cc'] ?? []) as $email => $name) {
$this->mailer->addCC($email, $name);
}
foreach (($options['bcc'] ?? []) as $email => $name) {
$this->mailer->addBCC($email, $name);
}
// Reply-To
if (!empty($options['replyTo'])) {
$this->mailer->addReplyTo($options['replyTo'][0], $options['replyTo'][1] ?? '');
}
// 內容
$this->mailer->isHTML(true);
$this->mailer->Subject = $subject;
$this->mailer->Body = $htmlBody;
$this->mailer->AltBody = $textBody ?? strip_tags($htmlBody); // 重要:純文字備選
// 附件
foreach ($attachments as $att) {
$this->mailer->addAttachment($att['path'], $att['name'] ?? basename($att['path']));
}
// 發送
$this->mailer->send();
return true;
}
}
<?php
require_once __DIR__ . '/vendor/autoload.php';
use App\Mail\QqMailer;
$config = [
'host' => 'smtp.exmail.qq.com',
'username' => 'noreply@yourcompany.com',
'password' => 'your_auth_code_here', // 授權碼,非密碼
'port' => 587,
'secure' => PHPMailer::ENCRYPTION_STARTTLS,
'from_name' => '我的系統',
'debug' => true, // 開發時開啟
];
$mailer = new QqMailer($config);
try {
$result = $mailer->send(
to: 'user@example.com',
subject: '歡迎註冊',
htmlBody: '<h1>歡迎</h1><p>感謝加入我們!</p>',
textBody: '歡迎\n感謝加入我們!',
attachments: [
['path' => __DIR__ . '/assets/welcome.pdf', 'name' => 'welcome.pdf']
],
options: [
'replyTo' => ['support@yourcompany.com', '客服團隊']
]
);
echo "發送成功";
} catch (Exception $e) {
error_log("郵件發送失敗: " . $e->getMessage());
echo "發送失敗: " . $e->getMessage();
}
| 問題 | 原因 | 解決 |
|---|---|---|
| 535 Error: authentication failed | 使用登入密碼而非授權碼 | 必須在郵箱設定產生**授權碼**並填入 Password |
| Connection timed out (port 465/587) | 伺服器防火牆封鎖輸出連線 | 開放輸出 TCP 465 (SSL) 或 587 (STARTTLS);或改用 25 (無加密,不推薦) |
| Certificate verification failed | PHP 無 CA 憑證束 | php.ini 設定 openssl.cafile=/path/cacert.pem 或 curl.cainfo |
| 郵件進垃圾桶 | 缺少 SPF/DKIM/DMARC | 域名 DNS 加入騰訊提供的 SPF (include:spf.exmail.qq.com)、DKIM 記錄 |
| 發送大量郵件被限流 | 觸發頻率限制 | 使用佇列 (Redis + Worker) 限流,單 IP 每分鐘 < 60 封 |
將郵件任務推入佇列,背景 Worker 處理,避免阻塞 HTTP 請求:
<?php
// Laravel Job 範例
class SendWelcomeEmail implements ShouldQueue {
use Queueable, SerializesModels;
public $userId;
public function __construct(int $userId) { $this->userId = $userId; }
public function handle() {
$user = User::find($this->userId);
$mailer = new QqMailer(config('mail.qq'));
$mailer->send($user->email, '歡迎', view('emails.welcome', ['user' => $user])->render());
}
}
// 控制器
SendWelcomeEmail::dispatch($user->id);
mail()**,全面改用 PHPMailer + SMTP Auth + TLS/SSL