| 指令 | Windows (SMTP) | Linux (sendmail) | 說明 |
|---|---|---|---|
SMTP | smtp.example.com | (無效) | SMTP 伺服器主機名/IP |
smtp_port | 25 / 587 / 465 | (無效) | SMTP 埠號 |
sendmail_from | noreply@example.com | (無效) | 預設寄件人(Windows 必填) |
sendmail_path | (無效) | /usr/sbin/sendmail -t -i | sendmail 執行檔路徑與參數 |
; php.ini
[mail function]
SMTP = smtp.gmail.com
smtp_port = 587
sendmail_from = noreply@example.com
; 注意:Windows 內建 mail() 不支援 SMTP 驗證(AUTH LOGIN)、SSL/TLS
; 實務上 Windows 生產環境**強烈建議改用 PHPMailer/Symfony Mailer** 直連 SMTP
; php.ini
[mail function]
sendmail_path = "/usr/sbin/sendmail -t -i -f noreply@example.com"
; -t 從訊息標頭解析收件人、-i 單獨處理點號行、-f 設定信封寄件人(影響 SPF/DKIM)
確保系統已安裝並啟用 Postfix/Exim/Sendmail:systemctl enable --now postfix。
function sendMail(
string $to,
string $subject,
string $htmlBody,
string $fromEmail = 'noreply@example.com',
string $fromName = '系統通知',
array $attachments = [] // ['path' => '/path/file.pdf', 'name' => 'file.pdf', 'mime' => 'application/pdf']
): bool {
// 1. 標頭編碼:Subject、From 需 MIME encoded-word
$subject = '=?UTF-8?B?' . base64_encode($subject) . '?=';
$from = '=?UTF-8?B?' . base64_encode($fromName) . '?= <' . $fromEmail . '>';
// 2. 邊界字串(混合 HTML + 附件)
$boundary = '==MULTIPART_BOUNDARY_' . md5(uniqid(microtime(true), true)) . '==';
// 3. 標頭
$headers = [
"From: $from",
"Reply-To: $fromEmail",
"MIME-Version: 1.0",
"Content-Type: multipart/mixed; boundary=\"$boundary\"",
"X-Mailer: PHP/" . PHP_VERSION,
];
// 4. 訊息主體
$message = "--$boundary\r\n";
$message .= "Content-Type: text/html; charset=UTF-8\r\n";
$message .= "Content-Transfer-Encoding: base64\r\n\r\n";
$message .= base64_encode($htmlBody) . "\r\n";
// 5. 附件
foreach ($attachments as $att) {
if (!file_exists($att['path'])) continue;
$content = base64_encode(file_get_contents($att['path']));
$message .= "--$boundary\r\n";
$message .= "Content-Type: " . ($att['mime'] ?? 'application/octet-stream') . '; name="' . $att['name'] . "\"\r\n";
$message .= "Content-Transfer-Encoding: base64\r\n";
$message .= 'Content-Disposition: attachment; filename="' . $att['name'] . "\"\r\n\r\n";
$message .= chunk_split($content) . "\r\n";
}
$message .= "--$boundary--\r\n";
// 6. 發送
$result = @mail($to, $subject, $message, implode("\r\n", $headers));
if (!$result) {
error_log("mail() failed: to=$to, subject=$subject");
}
return $result;
}
| 現象 | 可能原因 | 排查步驟 |
|---|---|---|
| mail() 回傳 true 但未收到信 | 進入垃圾箱、對方伺服器靜默丟棄、SPF/DKIM 失敗 | 檢查垃圾箱、查看郵件原始碼 Authentication-Results、設定 SPF/DKIM/DMARC |
| mail() 回傳 false | sendmail_path 錯誤、SMTP 連線失敗、埠號被防火牆阻擋 | CLI 測試 echo "test" | sendmail -t、telnet SMTP 埠、查看 mail.log |
| 中文亂碼 | Subject/From 未 MIME 編碼、Body 未指定 charset | Subject 用 =?UTF-8?B?...?=、Body Content-Type: text/html; charset=UTF-8 |
| 附件損壞/無法開啟 | Base64 編碼未分行、邊界字串衝突、二進位讀取錯誤 | 用 chunk_split(base64_encode($data))、唯一邊界、file_get_contents 讀取 |
| 大量發送極慢 | mail() 同步阻塞、每封建立新 SMTP 連線 | 改用 PHPMailer + SMTP keep-alive、或佇列異步發送 |
PHP mail() 適合簡單測試、低頻通知、無法安裝擴展的受限環境。**任何正式專案請直接使用 PHPMailer 或 Symfony Mailer**,配合專業郵件服務商(SendGrid、Mailgun、Amazon SES、Postmark、自建 Postfix + OpenDKIM),確保送達率、安全性、可觀測性與維護性。