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

基於 SMTP 協議的 PHP 郵件發送類實現

     編輯:PHP 綜合知識

設計目標

不依賴 PHP 內建 mail() 函數(受限於 sendmail 配置、無法指定 SMTP 服務器、無認證支援),直接以 Socket 連接 SMTP 服務器實現郵件發送,支援認證、多服務器預設、詳細日誌記錄。

核心類 ZSMailBox

<?php
class ZSMailBox {
    var $fpSocket;
    var $strLog;
    var $strSMTPServer;
    var $strSMTPPort = '25';
    var $strFrom;
    var $strTo;

    function ZSMailBox() {
        $this->strLog = '';
    }

    // 發送命令並驗證回應碼
    function DoCommand($strCommand, $strOKReply) {
        fputs($this->fpSocket, $strCommand);
        $strReply = fgets($this->fpSocket, 512);
        if (!ereg("^$strOKReply", $strReply)) return false;
        return true;
    }

    // 等待並驗證回應
    function WaitReply($strOKReply) {
        $strReply = fgets($this->fpSocket, 512);
        $this->strLog .= "Reply: $strReply\n";
        if (!ereg("^$strOKReply", $strReply)) return false;
        return true;
    }

    // 構造郵件數據
    function SendData($strSubject, $strContent) {
        $str  = "To: {$this->strTo}\r\n";
        $str .= "From: {$this->strFrom}\r\n";
        $str .= "Subject: $strSubject\r\n\r\n";
        $str .= $strContent . "\r\n.\r\n";
        fputs($this->fpSocket, $str);
        return true;
    }

    // 完整發送流程
    function SendMail($strSubject, $strContent) {
        if (!$this->VerifySMTPVariables()) {
            $this->strLog = "SMTP 變量無效\n";
            return false;
        }

        $this->fpSocket = fsockopen($this->strSMTPServer, $this->strSMTPPort);
        if (!$this->fpSocket) {
            $this->strLog .= "無法打開 Socket\n";
            return false;
        }
        $this->strLog .= "Socket 連接成功\n";

        // SMTP 握手序列
        if (!$this->WaitReply('220')) return $this->closeFail('220 回應失敗');
        if (!$this->DoCommand("HELO {$this->strSMTPServer}\r\n", '250')) return $this->closeFail('HELO 失敗');
        if (!$this->DoCommand("MAIL FROM: <{$this->strFrom}>\r\n", '250')) return $this->closeFail('MAIL FROM 失敗');
        if (!$this->DoCommand("RCPT TO: <{$this->strTo}>\r\n", '250')) return $this->closeFail('RCPT TO 失敗');
        if (!$this->DoCommand("DATA\r\n", '354')) return $this->closeFail('DATA 命令失敗');

        if (!$this->SendData($strSubject, $strContent)) return $this->closeFail('數據發送失敗');
        if (!$this->WaitReply('250')) return $this->closeFail('數據接收確認失敗');
        if (!$this->DoCommand("QUIT\r\n", '221')) return $this->closeFail('QUIT 失敗');

        fclose($this->fpSocket);
        return true;
    }

    function closeFail($msg) {
        $this->strLog .= "$msg\n";
        fclose($this->fpSocket);
        return false;
    }

    // 驗證郵箱格式
    function VerifyMailAddr($strMailAddr) {
        return eregi('^[_.0-9a-z-]+@([0-9a-z][0-9a-z-]+\.)+[a-z]{2,3}$', $strMailAddr);
    }

    function VerifyWebSiteAddr($strWebSiteAddr) {
        return eregi('^([_0-9a-z-]+\.)+[a-z]{2,3}$', $strWebSiteAddr);
    }

    function VerifySMTPVariables() {
        return $this->VerifyWebSiteAddr($this->strSMTPServer)
            && isset($this->strSMTPPort)
            && $this->VerifyMailAddr($this->strFrom)
            && $this->VerifyMailAddr($this->strTo);
    }

    function SetSMTPVariables($strServer, $strPort, $strFrom, $strTo) {
        if (isset($strServer)) $this->strSMTPServer = $strServer;
        if (isset($strPort)) $this->strSMTPPort = $strPort;
        if (isset($strFrom)) $this->strFrom = $strFrom;
        if (isset($strTo)) $this->strTo = $strTo;
    }
}
?>

常用服務器預設配置

<?php
// 示例:根據參數自動選擇服務器
if (isset($strTo) && $strTo != '') {
    if (isset($nServer)) {
        switch ($nServer) {
            case 1: $strSMTPServer='smtp.sina.com.cn'; $strSMTPPort='25'; $strTo.='@sina.com'; break;
            case 2: $strSMTPServer='smtp.163.net';   $strSMTPPort='25'; $strTo.='@163.net'; break;
            case 3: $strSMTPServer='smtp.yeah.net';  $strSMTPPort='25'; $strTo.='@yeah.net'; break;
            case 4: $strSMTPServer='smtp.netease.com';$strSMTPPort='25'; $strTo.='@netease.com'; break;
            case 5: $strSMTPServer='smtp.sohu.com';  $strSMTPPort='25'; $strTo.='@sohu.com'; break;
            case 6: $strSMTPServer='smtp.263.net';   $strSMTPPort='25'; $strTo.='@263.net'; break;
            default: $strSMTPServer='smtp.sina.com.cn'; $strSMTPPort='25'; $strTo='guestxyz@sina.com'; break;
        }
    }
    if (!isset($strFrom) || $strFrom=='') $strFrom = $strTo;
    if (!isset($strSubject) || $strSubject=='') $strSubject = 'No subject';
    if (!isset($strContent) || $strContent=='') $strContent = 'No content';

    $zsmb = new ZSMailBox;
    $zsmb->SetSMTPVariables($strSMTPServer, $strSMTPPort, $strFrom, $strTo);
    if ($zsmb->SendMail($strSubject, $strContent)) {
        echo 'OK';
    } else {
        echo $zsmb->strLog;
    }
}
?>

現代化改進方向

  1. 支援 SMTP AUTH:加入 AUTH LOGIN / AUTH PLAIN 流程(Base64 編碼用戶名密碼)
  2. SSL/TLS 加密:使用 fsockopen('ssl://smtp.gmail.com', 465) 或 stream_socket_client 配合 STREAM_CRYPTO_METHOD_TLS_CLIENT
  3. MIME 多部分郵件:支援 HTML 正文、附件、內嵌圖片(需構造 multipart/mixed、multipart/alternative 邊界)
  4. 替換 ereg 為 preg:正則函數已棄用
  5. 異步發送:寫入隊列(Redis/RabbitMQ),由 Worker 進程消費發送,避免阻塞 Web 請求
  6. 使用成熟庫:PHPMailer、SwiftMailer、Symfony Mailer 已完善處理上述所有細節
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved