PHP 提供 Heredoc (`<<<`) 和 Nowdoc 語法優雅處理大段字符串(如 HTML 模板、SQL 語句、郵件內容),避免繁瑣的引號轉義與拼接。本文詳解語法規則、變量解析差異、縮進處理,並實現一個生產級郵件發送類(支援 HTML/純文本、附件、內嵌圖片、SMTP 認證)。
| 特性 | Heredoc (`<<| Nowdoc (`<<<'EOT'`) | |
|---|---|---|
| 變量解析 | 是 (雙引號行為) | 否 (單引號行為) |
| 轉義序列 | 支援 (\n, \t, \$ 等) | 僅支援 \\ 和 \' |
| 結束標記 | 須獨佔行首,無縮進 | 同 |
| 適用場景 | 模板、SQL、含變量文本 | 正則、純文本、無需解析 |
<?php
$name = 'World';
$version = '8.2';
// Heredoc:解析變量
$html = << '2024-01-01'
SQL;
// PHP 7.3+ 允許結束標記縮進,且不需獨佔行 (若無縮進內容)
$indented = <<
<?php
namespace App\Mail;
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
use PHPMailer\PHPMailer\SMTP;
class Mailer {
private PHPMailer $mailer;
private array $config;
public function __construct(array $config) {
$this->config = $config;
$this->mailer = new PHPMailer(true); // 啟用異常
$this->configure();
}
private function configure(): void {
$m = $this->mailer;
$c = $this->config;
// SMTP 設置
$m->isSMTP();
$m->Host = $c['smtp_host'];
$m->SMTPAuth = true;
$m->Username = $c['smtp_user'];
$m->Password = $c['smtp_pass'];
$m->SMTPSecure = $c['smtp_secure'] ?? PHPMailer::ENCRYPTION_STARTTLS; // 'ssl' 或 'tls'
$m->Port = $c['smtp_port'] ?? 587;
$m->Timeout = $c['timeout'] ?? 30;
// 字符集與編碼
$m->CharSet = 'UTF-8';
$m->Encoding = 'base64';
// 發件人
$m->setFrom($c['from_email'], $c['from_name']);
// 開發環境調試
if (($c['debug'] ?? false)) {
$m->SMTPDebug = SMTP::DEBUG_SERVER;
$m->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'=>, 'name'=>, 'mime'=>]]
* @param array $inlineImages 內嵌圖片 [cid => path]
* @param array $cc 抄送
* @param array $bcc 密送
* @return bool
* @throws Exception
*/
public function send(
$to,
string $subject,
string $htmlBody,
?string $textBody = null,
array $attachments = [],
array $inlineImages = [],
array $cc = [],
array $bcc = []
): bool {
$m = $this->mailer;
// 清除上次收件人
$m->clearAddresses();
$m->clearAttachments();
$m->clearCustomHeaders();
// 收件人
$recipients = is_array($to) ? $to : [$to => ''];
foreach ($recipients as $email => $name) {
$m->addAddress($email, $name);
}
// 抄送/密送
foreach ($cc as $email => $name) $m->addCC($email, $name);
foreach ($bcc as $email => $name) $m->addBCC($email, $name);
// 內容
$m->Subject = $subject;
$m->msgHTML($htmlBody, __DIR__ . '/../templates/mail'); // 基礎路徑用於相對圖片
if ($textBody) {
$m->AltBody = $textBody;
} else {
$m->AltBody = strip_tags($htmlBody);
}
// 附件
foreach ($attachments as $att) {
$m->addAttachment(
$att['path'],
$att['name'] ?? basename($att['path']),
$att['encoding'] ?? 'base64',
$att['mime'] ?? 'application/octet-stream'
);
}
// 內嵌圖片 (HTML 中用
)
foreach ($inlineImages as $cid => $path) {
$m->addEmbeddedImage($path, $cid);
}
// 發送
return $m->send();
}
}
<?php
// config/mail.php
return [
'smtp_host' => 'smtp.exmail.qq.com',
'smtp_user' => 'noreply@example.com',
'smtp_pass' => 'your_app_password',
'smtp_secure' => 'tls', // ssl/tls
'smtp_port' => 587,
'from_email' => 'noreply@example.com',
'from_name' => 'Example System',
'debug' => (getenv('APP_ENV') !== 'production'),
];
// 使用
require 'vendor/autoload.php';
$config = require 'config/mail.php';
$mailer = new App\Mail\Mailer($config);
// Heredoc 構建 HTML 郵件
$user = ['name' => '張三', 'verify_url' => 'https://example.com/verify?token=abc123'];
$html = <<
{$user['verify_url']}
</p>
</div>
</body>
</html>
HTML;
$text = <<send(
$to: ['user@example.com' => $user['name']],
subject: '歡迎註冊 - 請驗證您的郵箱',
htmlBody: $html,
textBody: $text,
inlineImages: ['logo' => __DIR__ . '/assets/logo.png'],
);
echo $result ? '發送成功' : '發送失敗';
} catch (Exception $e) {
echo "郵件發送異常: {$e->getMessage()}";
}
<?php
function sendMail(
string $to,
string $subject,
string $htmlBody,
string $fromEmail,
string $fromName,
string $replyTo = '',
array $attachments = []
): bool {
$boundary = '----' . md5(uniqid(time()));
$headers = [];
$headers[] = "From: {$fromName} <{$fromEmail}>";
if ($replyTo) $headers[] = "Reply-To: {$replyTo}";
$headers[] = "MIME-Version: 1.0";
$headers[] = "Content-Type: multipart/mixed; boundary=\"{$boundary}\"";
$message = "--{$boundary}\n";
$message .= "Content-Type: text/html; charset=UTF-8\n";
$message .= "Content-Transfer-Encoding: base64\n\n";
$message .= chunk_split(base64_encode($htmlBody)) . "\n";
foreach ($attachments as $att) {
$content = file_get_contents($att['path']);
$message .= "--{$boundary}\n";
$message .= "Content-Type: {$att['mime']}; name=\"{$att['name']}\"\n";
$message .= "Content-Transfer-Encoding: base64\n";
$message .= "Content-Disposition: attachment; filename=\"{$att['name']}\"\n\n";
$message .= chunk_split(base64_encode($content)) . "\n";
}
$message .= "--{$boundary}--\n";
return mail($to, $subject, $message, implode("\r\n", $headers));
}
Heredoc/Nowdoc 是 PHP 處理多行字符串的利器,配合變量插值極適合構建郵件模板。郵件發送強烈建議使用 PHPMailer 等成熟庫,而非原生 mail(),後者缺乏 SMTP 認證、MIME 組裝、異常處理等關鍵能力。架構上採用「控制器 → 隊列 → Worker → SMTP」異步模式,保障高併發下的穩定性與用戶體驗。