sendServer = $server;
$this->port = $port;
$this->isSecurity = $isSecurity ? true : false;
$this->debug = $debug;
$this->userName = empty($username) ? "" : base64_encode($username);
$this->password = empty($password) ? "" : base64_encode($password);
$this->from = $username;
} else {
$smtp = Config::get();
$this->sendServer = $smtp['smtp_server'];
$this->port = $smtp['smtp_port'];
$this->isSecurity = $smtp['smtp_ssl'] ? true : false;
$this->debug = $debug;
$this->userName = base64_encode($smtp['smtp_username']);
$this->password = base64_encode($smtp['smtp_password']);
$this->from = $smtp['smtp_username'];
}
return true;
}
/**
* 设置收件人,多个收件人,调用多次或用逗号隔开.
*
* @param string $to
* 收件人地址
* @return boolean
*/
public function setReceiver($to)
{
if (strpos($to, ',')) {
$this->to = explode(',', $to);
} else {
$this->to[] = $to;
}
return true;
}
/**
* 设置抄送,多个抄送,调用多次或用逗号隔开.
*
* @param string $cc
* 抄送地址
* @return boolean
*/
public function setCc($cc)
{
if (strpos($cc, ',')) {
$this->cc = explode(',', $cc);
} else {
$this->cc[] = $cc;
}
return true;
}
/**
* 设置秘密抄送,多个秘密抄送,调用多次或用逗号隔开.
*
* @param string $bcc
* 秘密抄送地址
* @return boolean
*/
public function setBcc($bcc)
{
if (strpos($bcc, ',')) {
$this->bcc = explode(',', $bcc);
} else {
$this->bcc[] = $bcc;
}
return true;
}
/**
* 设置邮件附件,多个附件,调用多次
*
* @param string $file
* 文件地址
* @return boolean
*/
public function addAttachment($file)
{
if (! file_exists($file)) {
$this->errorMessage = "file " . $file . " does not exist.";
return false;
}
$this->attachment[] = $file;
return true;
}
/**
* 设置邮件信息
*
* @param string $body
* 邮件主题
* @param string $subject
* 邮件主体内容,可以是纯文本,也可是是HTML文本
* @return boolean
*/
public function setMail($subject, $body)
{
$this->subject = base64_encode($subject);
$this->body = base64_encode($body);
return true;
}
/**
* 发送邮件
*
* @return boolean
*/
public function sendMail($to = '', $subject = '', $body = '')
{
if ($to) {
$this->setReceiver($to);
$this->setMail($subject, $body);
}
$command = $this->getCommand();
if (! $this->socket($this->isSecurity)) {
return false;
}
foreach ($command as $value) {
$result = $this->sendCommand($value[0], $value[1]);
if ($result) {
continue;
} else {
return false;
}
}
// 关闭连接
$this->close();
return true;
}
/**
* 返回错误信息
*
* @return string
*/
public function error()
{
if (! isset($this->errorMessage)) {
$this->errorMessage = "";
}
return $this->errorMessage;
}
/**
* 返回mail命令
*
* @access protected
* @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
);
}
// 设置发件人
$command[] = array(
"MAIL FROM: <" . $this->from . ">\r\n",
250
);
$header = "FROM: <" . $this->from . ">\r\n";
// 设置收件人
if (! empty($this->to)) {
$count = count($this->to);
if ($count == 1) {
$command[] = array(
"RCPT TO: <" . $this->to[0] . ">\r\n",
250
);
$header .= "TO: <" . $this->to[0] . ">\r\n";
} else {
for ($i = 0; $i < $count; $i ++) {
$command[] = array(
"RCPT TO: <" . $this->to[$i] . ">\r\n",
250
);
if ($i == 0) {
$header .= "TO: <" . $this->to[$i] . ">";
} elseif ($i + 1 == $count) {
$header .= ",<" . $this->to[$i] . ">\r\n";
} else {
$header .= ",<" . $this->to[$i] . ">";
}
}
}
}
// 设置抄送
if (! empty($this->cc)) {
$count = count($this->cc);
if ($count == 1) {
$command[] = array(
"RCPT TO: <" . $this->cc[0] . ">\r\n",
250
);
$header .= "CC: <" . $this->cc[0] . ">\r\n";
} else {
for ($i = 0; $i < $count; $i ++) {
$command[] = array(
"RCPT TO: <" . $this->cc[$i] . ">\r\n",
250
);
if ($i == 0) {
$header .= "CC: <" . $this->cc[$i] . ">";
} elseif ($i + 1 == $count) {
$header .= ",<" . $this->cc[$i] . ">\r\n";
} else {
$header .= ",<" . $this->cc[$i] . ">";
}
}
}
}
// 设置秘密抄送
if (! empty($this->bcc)) {
$count = count($this->bcc);
if ($count == 1) {
$command[] = array(
"RCPT TO: <" . $this->bcc[0] . ">\r\n",
250
);
$header .= "BCC: <" . $this->bcc[0] . ">\r\n";
} else {
for ($i = 0; $i < $count; $i ++) {
$command[] = array(
"RCPT TO: <" . $this->bcc[$i] . ">\r\n",
250
);
if ($i == 0) {
$header .= "BCC: <" . $this->bcc[$i] . ">";
} elseif ($i + 1 == $count) {
$header .= ",<" . $this->bcc[$i] . ">\r\n";
} else {
$header .= ",<" . $this->bcc[$i] . ">";
}
}
}
}
// 主题
$header .= "Subject: =?UTF-8?B?" . $this->subject . "?=\r\n";
if (isset($this->attachment)) {
// 含有附件的邮件头需要声明成这个
$header .= "Content-Type: multipart/mixed;\r\n";
} elseif (false) {
// 邮件体含有图片资源的,且包含的图片在邮件内部时声明成这个,如果是引用的远程图片,就不需要了
$header .= "Content-Type: multipart/related;\r\n";
} else {
// html或者纯文本的邮件声明成这个
$header .= "Content-Type: multipart/alternative;\r\n";
}
// 邮件头分隔符
$header .= "\t" . 'boundary="' . $separator . '"';
$header .= "\r\nMIME-Version: 1.0\r\n";
// 这里开始是邮件的body部分,body部分分成几段发送
$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";
$header .= "--" . $separator . "\r\n";
// 加入附件
if (! empty($this->attachment)) {
$count = count($this->attachment);
for ($i = 0; $i < $count; $i ++) {
$header .= "\r\n--" . $separator . "\r\n";
$header .= "Content-Type: " . $this->getMIMEType($this->attachment[$i]) . '; name="=?UTF-8?B?' . base64_encode(basename($this->attachment[$i])) . '?="' . "\r\n";
$header .= "Content-Transfer-Encoding: base64\r\n";
$header .= 'Content-Disposition: attachment; filename="=?UTF-8?B?' . base64_encode(basename($this->attachment[$i])) . '?="' . "\r\n";
$header .= "\r\n";
$header .= $this->readFile($this->attachment[$i]);
$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;
}
/**
* 发送命令
*
* @param string $command
* 发送到服务器的smtp命令
* @param int $code
* 期望服务器返回的响应吗
* @return boolean
*/
protected function sendCommand($command, $code)
{
if ($this->debug) {
echo 'Send command:' . $command . ',expected code:' . $code . '
';
}
try {
if (fwrite($this->socket, $command)) {
// 当邮件内容分多次发送时,没有$code,服务器没有返回
if (empty($code)) {
return true;
}
// 读取服务器返回
$data = trim(fread($this->socket, 1024));
if (! mb_check_encoding($data, 'utf-8')) {
$data = iconv('gbk', 'utf-8', $data);
}
if ($this->debug) {
echo 'response:' . $data . '
';
}
if ($data) {
$pattern = "/^" . $code . "+?/";
if (preg_match($pattern, $data)) {
return true;
} else {
$this->errorMessage = $data;
return false;
}
} else {
return false;
}
} else {
$this->errorMessage = "Error: " . $command . " send failed";
return false;
}
} catch (\Exception $e) {
$this->errorMessage = "Error:" . $e->getMessage();
}
}
/**
* 读取附件文件内容,返回base64编码后的文件内容
*
* @param string $file
* 文件
* @return mixed
*/
protected function readFile($file)
{
if (file_exists($file)) {
$file_obj = file_get_contents($file);
return base64_encode($file_obj);
} else {
$this->errorMessage = "file " . $file . " dose not exist";
return false;
}
}
/**
* 获取附件MIME类型
*
* @param string $file
* 文件
* @return mixed
*/
protected function getMIMEType($file)
{
if (file_exists($file)) {
$mime = mime_content_type($file);
return $mime;
} else {
return false;
}
}
/**
* 建立到服务器的网络连接
*
* @return boolean
*/
protected function socket($ssl = true)
{
if ($ssl && ! extension_loaded('openssl')) {
$this->errorMessage = '服务器未启用openssl扩展,无法使用加密方式发送邮件!';
return false;
}
if (function_exists('stream_socket_client')) {
if (! function_exists('stream_socket_enable_crypto')) {
$this->errorMessage = '服务器已经禁用stream_socket_enable_crypto函数,无法发送邮件!';
return false;
}
if (! function_exists('stream_set_blocking')) {
$this->errorMessage = '服务器已经禁用stream_set_blocking函数,无法发送邮件!';
return false;
}
// 建立连接
$remoteAddr = "tcp://" . $this->sendServer . ":" . $this->port;
$this->socket = stream_socket_client($remoteAddr, $errno, $errstr, 30);
if (! $this->socket) {
$this->errorMessage = $errstr;
return false;
}
// 设置加密方式
$crypto_method = STREAM_CRYPTO_METHOD_TLS_CLIENT;
if (defined('STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT')) {
$crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT;
$crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT;
}
stream_socket_enable_crypto($this->socket, $ssl, $crypto_method);
} elseif (function_exists('fsockopen')) {
if ($ssl) {
$remoteAddr = "ssl://" . $this->sendServer;
} else {
$remoteAddr = "tcp://" . $this->sendServer;
}
$this->socket = fsockopen($remoteAddr, $this->port, $errno, $errstr, 30);
if (! $this->socket) {
$this->errorMessage = $errstr;
return false;
}
} else {
$this->errorMessage = '服务器已经禁用stream_socket_client和fsockopen函数,请至少开启一个才能发送邮件!';
return false;
}
stream_set_blocking($this->socket, 1); // 设置阻塞模式
$str = fread($this->socket, 1024);
if (! preg_match("/220+?/", $str)) {
$this->errorMessage = $str;
return false;
}
return true;
}
/**
* 关闭安全socket
*
* @return boolean
*/
protected function close()
{
if (isset($this->socket) && is_object($this->socket)) {
stream_socket_shutdown($this->socket, STREAM_SHUT_WR);
return true;
}
$this->errorMessage = "No resource can to be close";
return false;
}
}