PHP 發送郵件主要有兩條路:使用內建 mail() 函式,或引入 SMTP 協定類別(如 PHPMailer、Symfony Mailer)。本文深度比較兩者優缺點、適用場景與完整實作範例。
<?php
$to = 'user@example.com';
$subject = '測試郵件';
$message = '這是一封測試郵件內容。';
$headers = "From: sender@example.com\r\n" .
"Reply-To: reply@example.com\r\n" .
"Content-Type: text/plain; charset=UTF-8\r\n" .
"X-Mailer: PHP/" . phpversion();
mail($to, $subject, $message, $headers);
?>
mail() 只是將郵件交給本機 MTA(如 Sendmail、Postfix、Exim),不支援 SMTP 認證。因此:
; Linux/Unix (使用 sendmail) sendmail_path = "/usr/sbin/sendmail -t -i" ; Windows (需指定 SMTP 伺服器,但不支援認證) SMTP = localhost smtp_port = 25 sendmail_from = me@example.com
| 特性 | mail() | SMTP 類別 |
|---|---|---|
| SMTP 認證 | ❌ | ✅ |
| SSL/TLS 加密 | ❌ | ✅ |
| HTML 郵件 | 手動組裝 | ✅ 內建 |
| 附件支援 | 手動組裝 | ✅ 內建 |
| 嵌入圖片 | 極困難 | ✅ 內建 |
| 多收件人/抄送/密件副本 | 手動處理 | ✅ 內建 |
| 郵件佇列/批次發送 | ❌ | ✅ 支援 |
| 錯誤處理/除錯 | 極少 | ✅ 詳細 |
| 跨平台相容 | 差 | ✅ 純 PHP |
composer require phpmailer/phpmailer
<?php
require 'vendor/autoload.php';
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\SMTP;
use PHPMailer\PHPMailer\Exception;
$mail = new PHPMailer(true); // true = 啟用例外處理
try {
// === SMTP 伺服器設定 ===
$mail->isSMTP();
$mail->Host = 'smtp.gmail.com'; // SMTP 伺服器
$mail->SMTPAuth = true; // 啟用認證
$mail->Username = 'your@gmail.com'; // 帳號
$mail->Password = 'your_app_password'; // 應用程式密碼(非登入密碼)
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS; // TLS
$mail->Port = 587; // TLS 埠口
// $mail->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS; // SSL
// $mail->Port = 465; // SSL 埠口
// === 寄件人/收件人 ===
$mail->setFrom('your@gmail.com', '您的名稱');
$mail->addAddress('recipient@example.com', '收件人名稱');
$mail->addReplyTo('reply@example.com', '回覆地址');
$mail->addCC('cc@example.com');
$mail->addBCC('bcc@example.com');
// === 附件 ===
$mail->addAttachment('/path/to/file.pdf', 'rename.pdf');
$mail->addAttachment('/path/to/image.jpg'); // 檔名保持原狀
// === 內容 ===
$mail->isHTML(true); // 設定為 HTML 格式
$mail->Subject = 'PHPMailer 測試郵件';
$mail->Body = '<h1>HTML 內容</h1><p>支援 <b>粗體</b>、連結、圖片等。</p>';
$mail->AltBody = '純文字備用內容(不支援 HTML 的郵件客戶端)';
// === 嵌入圖片 (CID) ===
$mail->addEmbeddedImage('/path/to/logo.png', 'logo_id');
$mail->Body = '<img src="cid:logo_id" alt="Logo">' . $mail->Body;
$mail->send();
echo '郵件發送成功!';
} catch (Exception $e) {
echo "發送失敗: {$mail->ErrorInfo}";
}
?>
| 提供商 | SMTP 主機 | 埠口 | 加密 | 帳號/密碼 |
|---|---|---|---|---|
| Gmail | smtp.gmail.com | 587 (TLS) / 465 (SSL) | STARTTLS / SSL | 需開啟「應用程式密碼」 |
| Outlook/Hotmail | smtp.office365.com | 587 | STARTTLS | 完整信箱 / 登入密碼 |
| Yahoo | smtp.mail.yahoo.com | 465/587 | SSL/TLS | 需開啟「應用程式密碼」 |
| 企業 Exchange | smtp.公司域名.com | 587 | STARTTLS | 完整信箱 / AD 密碼 |
| SendGrid | smtp.sendgrid.net | 587/2525/465 | TLS/SSL | apikey / API Key |
| Mailgun | smtp.mailgun.org | 587/2525 | TLS | postmaster@域名 / API Key |
composer require symfony/mailer
<?php
use Symfony\Component\Mailer\Mailer;
use Symfony\Component\Mailer\Transport;
use Symfony\Component\Mime\Email;
$dsn = 'smtp://user:pass@smtp.gmail.com:587';
$transport = Transport::fromDsn($dsn);
$mailer = new Mailer($transport);
$email = (new Email())
->from('your@gmail.com')
->to('recipient@example.com')
->subject('Symfony Mailer 測試')
->html('<h1>HTML 內容</h1>')
->text('純文字內容')
->attachFromPath('/path/to/file.pdf');
$mailer->send($email);
?>
<?php
// PHPMailer 批次發送(重複使用 SMTP 連線)
$mail = new PHPMailer(true);
$mail->isSMTP();
$mail->Host = 'smtp.gmail.com';
// ... 設定 SMTP ...
$mail->setFrom('sender@example.com');
$mail->Subject = '批次通知';
$mail->Body = '通知內容...';
$recipients = [
['email' => 'a@example.com', 'name' => 'User A'],
['email' => 'b@example.com', 'name' => 'User B'],
// ...
];
// 保持連線開啟
$mail->SMTPKeepAlive = true;
foreach ($recipients as $rcpt) {
$mail->clearAddresses();
$mail->addAddress($rcpt['email'], $rcpt['name']);
try {
$mail->send();
echo "Sent to {$rcpt['email']}\n";
} catch (Exception $e) {
echo "Failed to {$rcpt['email']}: {$mail->ErrorInfo}\n";
}
}
// 記得關閉連線
$mail->smtpClose();
?>
exec("php send_mail.php > /dev/null 2>&1 &")(不推薦生產環境)| 場景 | 推薦方案 |
|---|---|
| 簡單通知、內部系統、已有 MTA | mail() 可接受 |
| 對外發送、需 HTML/附件、生產環境 | PHPMailer / Symfony Mailer |
| 大量行銷郵件、交易郵件 | SendGrid, Mailgun, Amazon SES + SDK |
| Laravel / Symfony 專案 | Symfony Mailer (內建整合) |
結論:現代 PHP 專案強烈建議直接使用 PHPMailer 或 Symfony Mailer,避開 mail() 的諸多限制與坑。