首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP Heredoc/Nowdoc 語法與郵件發送完整實現指南

     編輯:PHP 綜合知識

PHP 提供 Heredoc (`<<<`) 和 Nowdoc 語法優雅處理大段字符串(如 HTML 模板、SQL 語句、郵件內容),避免繁瑣的引號轉義與拼接。本文詳解語法規則、變量解析差異、縮進處理,並實現一個生產級郵件發送類(支援 HTML/純文本、附件、內嵌圖片、SMTP 認證)。

Heredoc vs Nowdoc

特性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()}";
}

原生 mail() 函數簡易封裝 (無依賴)

<?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));
}

郵件發送最佳實踐清單

  1. 使用專業庫:PHPMailer / Symfony Mailer / Laravel Mail,處理編碼、MIME、SMTP 細節
  2. SPF/DKIM/DMARC:域名配置郵件認證,提高送達率
  3. 異步發送:放入隊列 (Redis/RabbitMQ),Worker 消費,避免阻塞 HTTP 響應
  4. 退訂鏈接:營銷郵件必須包含 List-Unsubscribe 頭與可見退訂鏈接
  5. 內容規範:HTML + 純文本雙版本、響應式設計、圖文比例合理、避免敏感詞
  6. 發送頻率控制:IP/域名信譽管理,新 IP 預熱,監控退信率、投訴率
  7. 測試驗證:Mailtrap/Ethmail 測試、Litmus/Email on Acid 跨客戶端測試

總結

Heredoc/Nowdoc 是 PHP 處理多行字符串的利器,配合變量插值極適合構建郵件模板。郵件發送強烈建議使用 PHPMailer 等成熟庫,而非原生 mail(),後者缺乏 SMTP 認證、MIME 組裝、異常處理等關鍵能力。架構上採用「控制器 → 隊列 → Worker → SMTP」異步模式,保障高併發下的穩定性與用戶體驗。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved