PHP手动实现SMTP邮件发送
完整代码实现:
<?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();
}
?>
关键功能说明:
协议交互流程:
- 建立加密连接后,按顺序执行SMTP协议要求的命令序列
- 每个命令都验证服务器返回的状态码(如250=成功,334=需要认证信息)
- 特殊处理EHLO命令的多行响应
安全特性:
- 使用SSL加密传输
- 用户名密码使用Base64编码(注意:建议配合SSL使用)
- 严格的响应状态码验证
邮件内容构建:
- 自动生成符合RFC标准的邮件头
- 支持HTML格式邮件内容
- 包含完整的MIME头信息
使用注意事项:
- 确保服务器已启用
openssl
扩展 mailpage.html
文件需存在且内容有效- 腾讯企业邮箱需要使用授权码而非登录密码
生产环境建议:
- 将配置参数移到单独配置文件
- 添加附件支持
- 实现邮件队列机制
- 添加发送频率限制