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

PHP 實現支援 SSL 的 SMTP 郵件發送類詳解

     編輯:PHP 相關知識

前言

PHP 內建 mail() 函數功能有限:不支援 SMTP 認證、SSL/TLS 加密、附件、HTML 郵件等企業級需求。本文提供一個功能完整的 SMTP 郵件發送類,基於 Socket 直連郵件服務器,支援 SSL 加密、多收件人、抄送、密送、附件、HTML/文本混合格式,經測試兼容 Gmail、QQ 郵箱、163 等主流服務商。

核心特性

  • 雙連接模式:普通 Socket (25 端口) + SSL/TLS Socket (465/587 端口)
  • 完整 MIME 支援:multipart/mixed (附件)、multipart/alternative (HTML+文本)、Base64 編碼
  • 健壯的錯誤處理:SMTP 回應碼驗證、異常捕獲、詳細錯誤信息返回
  • 附件管理:自動 MIME 類型檢測、Base64 編碼、中文文件名支援

類結構與關鍵方法

<?php
/**
 * SMTP 郵件發送類
 * 依賴擴展:sockets、openssl、fileinfo
 * 編碼:UTF-8,傳輸編碼:Base64
 */
class MySendMail {
    // 屬性定義...
    protected $_userName;
    protected $_password;
    protected $_sendServer;
    protected $_port = 25;
    protected $_from;
    protected $_to = array();
    protected $_cc = array();
    protected $_bcc = array();
    protected $_subject;
    protected $_body;
    protected $_attachment = array();
    protected $_socket;
    protected $_isSecurity = false;
    protected $_errorMessage;

    /**
     * 設置 SMTP 服務器
     * @param string $server 服務器地址
     * @param string $username 登錄用戶名
     * @param string $password 登錄密碼/授權碼
     * @param int $port 端口 (25/465/587)
     * @param bool $isSecurity 是否啟用 SSL
     * @return bool
     */
    public function setServer($server, $username="", $password="", $port=25, $isSecurity=false) {
        $this->_sendServer = $server;
        $this->_port = $port;
        $this->_isSecurity = $isSecurity;
        // SMTP AUTH LOGIN 需要 Base64 編碼的用戶名密碼
        $this->_userName = empty($username) ? "" : base64_encode($username);
        $this->_password = empty($password) ? "" : base64_encode($password);
        return true;
    }

    // 設置發件人、收件人、抄送、密送、附件、主題正文等 setter 方法...
    // (為節省篇幅省略,完整代碼見下方)

    /**
     * 發送郵件主入口
     * @return bool
     */
    public function sendMail() {
        $command = $this->getCommand(); // 構建完整 SMTP 命令序列
        // 建立連接
        $this->_isSecurity ? $this->socketSecurity() : $this->socket();
        // 逐條發送命令並驗證回應碼
        foreach ($command as $value) {
            $result = $this->_isSecurity 
                ? $this->sendCommandSecurity($value[0], $value[1]) 
                : $this->sendCommand($value[0], $value[1]);
            if (!$result) return false;
        }
        $this->_isSecurity ? $this->closeSecutity() : $this->close();
        return true;
    }

    /**
     * 構建 SMTP 命令序列(核心邏輯)
     * @return array [[命令, 期望回應碼], ...]
     */
    protected function getCommand() {
        $separator = "----=_Part_" . md5($this->_from . time()) . uniqid();
        $command = array(array("HELO sendmail\r\n", 250));
        
        // 認證流程
        if (!empty($this->_userName)) {
            $command[] = array("AUTH LOGIN\r\n", 334);
            $command[] = array($this->_userName . "\r\n", 334);
            $command[] = array($this->_password . "\r\n", 235);
        }
        // MAIL FROM
        $command[] = array("MAIL FROM: <" . $this->_from . ">\r\n", 250);
        $header = "FROM: <" . $this->_from . ">\r\n";
        
        // RCPT TO (收件人/抄送/密送)
        // ... 循環構建 TO/CC/BCC 命令與 Header
        
        // 郵件頭構建
        $header .= "Subject: =?UTF-8?B?" . $this->_subject . "?=\r\n";
        // 根據是否有附件決定 Content-Type
        if (!empty($this->_attachment)) {
            $header .= "Content-Type: multipart/mixed;\r\n";
        } else {
            $header .= "Content-Type: multipart/alternative;\r\n";
        }
        $header .= "\tboundary=\"" . $separator . "\"\r\n";
        $header .= "MIME-Version: 1.0\r\n";
        
        // 郵件體:HTML 部分
        $header .= "\r\n--" . $separator . "\r\n";
        $header .= "Content-Type: text/html; charset=utf-8\r\n";
        $header .= "Content-Transfer-Encoding: base64\r\n\r\n";
        $header .= $this->_body . "\r\n"; // 已 Base64 編碼
        $header .= "--" . $separator . "\r\n";
        
        // 附件部分
        if (!empty($this->_attachment)) {
            foreach ($this->_attachment as $file) {
                $header .= "\r\n--" . $separator . "\r\n";
                $header .= "Content-Type: " . $this->getMIMEType($file) . '; name="=?UTF-8?B?' . base64_encode(basename($file)) . '?="' . "\r\n";
                $header .= "Content-Transfer-Encoding: base64\r\n";
                $header .= 'Content-Disposition: attachment; filename="=?UTF-8?B?' . base64_encode(basename($file)) . '?="' . "\r\n";
                $header .= "\r\n";
                $header .= $this->readFile($file); // 文件內容 Base64
                $header .= "\r\n--" . $separator . "\r\n";
            }
        }
        $header .= "\r\n.\r\n"; // 數據結束標誌
        
        $command[] = array("DATA\r\n", 354);
        $command[] = array($header, 250);
        $command[] = array("QUIT\r\n", 221);
        return $command;
    }

    // Socket 連接、SSL 連接、命令發送、文件讀取、MIME 類型獲取等 protected 方法...
    // (完整代碼較長,關鍵點見下方說明)
}

關鍵技術點解析

1. SSL/TLS 安全連接實現

使用 PHP Stream 套接字而非傳統 Socket,支援現代 TLS 協議:

protected function socketSecurity() {
    $remoteAddr = "tcp://" . $this->_sendServer . ":" . $this->_port;
    $this->_socket = stream_socket_client($remoteAddr, $errno, $errstr, 30);
    if (!$this->_socket) { $this->_errorMessage = $errstr; return false; }
    
    // 關鍵:啟用 TLS/SSL 加密,STREAM_CRYPTO_METHOD_TLS_CLIENT 更安全
    stream_socket_enable_crypto($this->_socket, true, STREAM_CRYPTO_METHOD_TLS_CLIENT);
    stream_set_blocking($this->_socket, 1);
    
    $str = fread($this->_socket, 1024);
    if (!preg_match("/220+/", $str)) { $this->_errorMessage = $str; return false; }
    return true;
}

2. 中文附件名與郵件主題編碼

採用 RFC 2047 標準:=?UTF-8?B?Base64編碼內容?=,確保跨客戶端顯示正常。

3. SMTP 回應碼驗證機制

每條命令發送後讀取服務器回應,通過正則匹配期望代碼(如 250、334、354),失敗即拋出錯誤並終止流程。

使用示例

<?php
require_once 'MySendMail.class.php';

$mail = new MySendMail();

// 配置:QQ 郵箱 SMTP (SSL 465 端口)
$mail->setServer("smtp.qq.com", "your_qq@qq.com", "授權碼", 465, true);
// 或 Gmail
// $mail->setServer("smtp.gmail.com", "your@gmail.com", "應用專用密碼", 465, true);

$mail->setFrom("your_qq@qq.com");           // 發件人
$mail->setReceiver("recipient1@example.com"); // 收件人
$mail->setCc("cc@example.com");             // 抄送
$mail->setBcc("bcc@example.com");           // 密送
$mail->addAttachment("/path/to/report.pdf"); // 附件
$mail->addAttachment("/path/to/image.png");
$mail->setMail("項目週報", "<h3>本週進度</h3><p>詳細內容...</p>"); // 主題、HTML 正文

if ($mail->sendMail()) {
    echo "郵件發送成功!";
} else {
    echo "發送失敗: " . $mail->error();
}

常見問題與避坑指南

問題原因解決方案
認證失敗 (535)密碼錯誤/未開啟 SMTP/使用登錄密碼而非授權碼郵箱後台開啟 SMTP,獲取專用授權碼/應用密碼
連接超時端口被封/防火牆/服務器地址錯誤telnet 測試端口連通性,確認 465/587/25 端口
附件亂碼/無法打開文件讀取編碼錯誤/MIME 類型不匹配使用 file_get_contents + base64_encode,fileinfo 擴展獲取 MIME
進入垃圾箱無 SPF/DKIM/DMARC、內容敏感、IP 信譽差配置域名解析記錄、優化內容、使用可信發信服務商
SSL 握手失敗PHP OpenSSL 版本過低/協議不匹配升級 PHP,嘗試 STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT

總結

本類通過底層 Socket 實現了完整的 SMTP 協議交互,無需依賴第三方庫(如 PHPMailer),適合學習協議細節或輕量級項目。生產環境高併發場景建議:使用異步隊列(如 Redis + Worker)剝離發送邏輯、接入專業發信服務(SendGrid、Mailgun、阿里云邮件推送)提升送達率、實現退訂/彈回/打開追蹤等高級功能。

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