PHP手动实现SMTP邮件发送

2025-04-20T15:18:00

完整代码实现:

<?php

/********************** 邮箱配置参数 **********************/
// SMTP服务器配置(以腾讯企业邮箱为例)
$smtpHost = 'ssl://smtp.exmail.qq.com'; // SMTP服务器地址
$smtpPort = 465;                        // SMTP端口号(SSL加密端口)
$username = 'admin@admin.com';          // 发件邮箱账号
$password = '75yEBasMUFVfs5h9';         // 邮箱授权码(非登录密码)
$fromName = '青竹小轩博客';              // 发件人显示名称

/********************** 发送内容参数 **********************/
$to = 'TEST@qq.com';               // 收件人邮箱地址
$subject = 'SMTP测试';           // 邮件主题
$message = file_get_contents('mailpage.html'); // 从HTML文件读取邮件正文内容

/**
 * SMTP协议邮件发送函数
 * @param string $to         收件人邮箱
 * @param string $subject    邮件主题
 * @param string $message    邮件正文内容
 * @param string $smtpHost   SMTP服务器地址
 * @param int    $smtpPort   SMTP端口号
 * @param string $username   发件邮箱账号
 * @param string $password   邮箱授权码
 * @param string $fromName   发件人显示名称
 * @return bool              发送结果
 */
function sendSMTPMail($to, $subject, $message, $smtpHost, $smtpPort, $username, $password, $fromName) {
    
    /************** 建立SMTP连接 **************/
    // 创建SSL加密的套接字连接
    $socket = stream_socket_client("$smtpHost:$smtpPort", $errno, $errstr, 30);
    if (!$socket) die("连接失败: $errstr ($errno)");

    // 读取服务器欢迎响应(220表示服务就绪)
    $response = fgets($socket);
    if (substr($response, 0, 3) != '220') die("连接响应错误: $response");

    /************** SMTP协议交互流程 **************/
    // 定义命令序列及期望的响应状态码
    $commands = [
        // EHLO命令开启会话,250表示请求成功
        ["EHLO localhost\r\n", 250],  
        
        // AUTH LOGIN开始认证,334表示等待输入
        ["AUTH LOGIN\r\n", 334],
        
        // 发送Base64编码的用户名
        [base64_encode($username) . "\r\n", 334],
        
        // 发送Base64编码的密码,235表示认证成功
        [base64_encode($password) . "\r\n", 235],
        
        // 设置发件人地址,250表示接受
        ["MAIL FROM: <{$username}>\r\n", 250],
        
        // 设置收件人地址,250表示接受
        ["RCPT TO: <{$to}>\r\n", 250],
        
        // 准备发送数据,354表示开始输入
        ["DATA\r\n", 354]
    ];

    // 循环执行每个SMTP命令
    foreach ($commands as $cmd) {
        list($command, $expectedCode) = $cmd;
        
        // 发送当前命令
        fwrite($socket, $command);
        
        // 特殊处理EHLO的多行响应(可能返回多行250)
        if ($expectedCode == 250 && $command == "EHLO localhost\r\n") {
            $response = '';
            // 持续读取直到遇到以"250 "开头的结束行
            while ($line = fgets($socket)) {
                $response .= $line;
                if (substr($line, 0, 4) === "250 ") break;
            }
        } else {
            // 读取单行响应
            $response = fgets($socket);
        }
        
        // 验证响应状态码是否正确
        if (substr($response, 0, 3) != $expectedCode) {
            die("命令失败: " . trim($command) . " | 预期: $expectedCode | 实际响应: " . trim($response));
        }
    }

    /************** 构建邮件内容 **************/
    $headers = [
        "From: {$fromName} <{$username}>",  // 发件人信息
        "To: {$to}",                       // 收件人地址
        "Subject: {$subject}",             // 邮件主题
        "Date: " . date('r'),              // 日期(RFC2822格式)
        "MIME-Version: 1.0",               // MIME版本
        "Content-Type: text/html; charset=utf-8" // HTML内容类型
    ];

    // 发送邮件头+空行+正文内容
    fwrite($socket, implode("\r\n", $headers) . "\r\n\r\n{$message}\r\n.\r\n");
    
    // 验证数据发送响应(250表示成功接受)
    $response = fgets($socket);
    if (substr($response, 0, 3) != 250) {
        die("邮件发送失败: $response");
    }

    /************** 结束会话 **************/
    fwrite($socket, "QUIT\r\n");  // 发送退出命令
    fclose($socket);              // 关闭连接
    return true;
}

/********************** 执行发送 **********************/
try {
    $result = sendSMTPMail(
        $to,
        $subject,
        $message,
        $smtpHost,
        $smtpPort,
        $username,
        $password,
        $fromName
    );
    echo $result ? '邮件发送成功!' : '发送失败';
} catch (Exception $e) {
    echo "错误: " . $e->getMessage();
}
?>
关键功能说明:
  1. 协议交互流程

    • 建立加密连接后,按顺序执行SMTP协议要求的命令序列
    • 每个命令都验证服务器返回的状态码(如250=成功,334=需要认证信息)
    • 特殊处理EHLO命令的多行响应
  2. 安全特性

    • 使用SSL加密传输
    • 用户名密码使用Base64编码(注意:建议配合SSL使用)
    • 严格的响应状态码验证
  3. 邮件内容构建

    • 自动生成符合RFC标准的邮件头
    • 支持HTML格式邮件内容
    • 包含完整的MIME头信息
使用注意事项:
  1. 确保服务器已启用openssl扩展
  2. mailpage.html文件需存在且内容有效
  3. 腾讯企业邮箱需要使用授权码而非登录密码
  4. 生产环境建议:

    • 将配置参数移到单独配置文件
    • 添加附件支持
    • 实现邮件队列机制
    • 添加发送频率限制
当前页面是本站的「Baidu MIP」版。发表评论请点击:完整版 »