* @date 2019/5/12 * @time 16:38 */ namespace Luolongfei\Libs\MessageServices; use Luolongfei\App\Exceptions\LlfException; use Luolongfei\Libs\Log; use PHPMailer\PHPMailer\PHPMailer; use PHPMailer\PHPMailer\Exception as MailException; use Luolongfei\Libs\Connector\MessageGateway; class Mail extends MessageGateway { /** * @var PHPMailer */ private $phpMailerInstance; /** * @throws MailException */ public function __construct() { $this->phpMailerInstance = new PHPMailer(true); $this->init(); } /** * 初始化邮箱配置 * * @throws MailException */ protected function init() { $username = config('message.mail.username'); $password = config('message.mail.password'); list($host, $secure, $port) = $this->getBasicMailConf($username); $this->phpMailerInstance->SMTPDebug = config('debug') ? 2 : 0; // Debug 0:关闭 1:客户端信息 2:客户端和服务端信息 $this->phpMailerInstance->isSMTP(); // 告诉 PHPMailer 使用 SMTP $this->phpMailerInstance->Host = $host; // SMTP 服务器 $this->phpMailerInstance->SMTPAuth = true; // 启用 SMTP 身份验证 $this->phpMailerInstance->Username = $username; // 账号 $this->phpMailerInstance->Password = $password; // 密码或授权码 $this->phpMailerInstance->SMTPSecure = $secure; // 将加密系统设置为使用 - ssl(不建议使用)或 tls $this->phpMailerInstance->Port = $port; // 设置 SMTP 端口号 - tsl 使用 587 端口,ssl 使用 465 端口 $this->phpMailerInstance->CharSet = 'UTF-8'; // 防止中文邮件乱码 $this->phpMailerInstance->setLanguage('zh_cn', VENDOR_PATH . '/phpmailer/phpmailer/language/'); // 设置语言 $this->phpMailerInstance->setFrom($username, 'Im robot'); // 发件人 } /** * 获取邮箱基本配置 * * @param string $username * * @return array * @throws MailException */ public function getBasicMailConf(string $username) { if (stripos($username, '@gmail.com') !== false) { $host = 'smtp.gmail.com'; $secure = 'tls'; $port = 587; } else if (stripos($username, '@qq.com') !== false) { $host = 'smtp.qq.com'; $secure = 'tls'; $port = 587; } else if (stripos($username, '@163.com') !== false) { $host = 'smtp.163.com'; $secure = 'ssl'; $port = 465; } else if (stripos($username, '@vip.163.com') !== false) { $host = 'smtp.vip.163.com'; $secure = 'ssl'; $port = 465; } else if (stripos($username, '@outlook.com') !== false) { $host = 'smtp.office365.com'; $secure = 'starttls'; $port = 587; } else { $host = config('message.mail.host'); $secure = config('message.mail.encryption'); $port = (int)config('message.mail.port'); if (!($host && $secure && $port)) { throw new MailException('目前支持Gmail、QQ邮箱、163邮箱以及Outlook邮箱自动识别配置,其它类型的邮箱或者自建邮箱,' . '请在 .env 文件中追加“自定义邮箱配置”的所有相关项,否则无法使用邮件服务。'); } } return [$host, $secure, $port]; } /** * 生成域名 html * * @param array $domains * * @return string */ public function genDomainsHtml(array $domains) { $domainsHtml = ''; foreach ($domains as $domain) { $domainsHtml .= sprintf('%s', $domain, $domain); } return $domainsHtml; } /** * 生成域名状态 html * * @param array $domainStatus * * @return string */ public function genDomainStatusHtml(array $domainStatus) { $domainStatusHtml = ''; foreach ($domainStatus as $domain => $daysLeft) { $domainStatusHtml .= sprintf('%s还有 %d 天到期,', $domain, $domain, $daysLeft); } $domainStatusHtml = rtrim($domainStatusHtml, ',') . "。"; return $domainStatusHtml; } /** * 送信 * * @param string $content * @param string $subject * @param integer $type * @param array $data * @param string|null $recipient * @param mixed ...$params * * @return bool * @throws LlfException * @throws MailException */ public function send(string $content, string $subject = '', int $type = 1, array $data = [], ?string $recipient = null, ...$params) { $recipient = $recipient ?: config('message.mail.to'); if (!$recipient) { throw new LlfException(34520012); } if ($content === '' && empty($data)) { throw new \Exception(lang('error_msg.100002')); } if ($content !== '' && $data) { throw new \Exception(lang('error_msg.100004')); } $this->phpMailerInstance->addAddress($recipient, config('message.mail.recipient_name', '主人')); // 添加收件人,参数2选填 $this->phpMailerInstance->addReplyTo(config('message.mail.reply_to', 'mybsdc@qq.com'), config('message.mail.reply_to_name', '作者')); // 备用回复地址,收到的回复的邮件将被发到此地址 /** * 抄送和密送都是添加收件人,抄送方式下,被抄送者知道除被密送者外的所有的收件人,密送方式下, * 被密送者知道所有的被抄送者,但不知道其它的被密送者。 * 抄送好比@,密送好比私信。 */ // $this->phpMailerInstance->addCC('cc@example.com'); // 抄送 // $this->phpMailerInstance->addBCC('bcc@example.com'); // 密送 // 添加附件,参数2选填 // $this->phpMailerInstance->addAttachment('README.md', '说明.txt'); // 标题 $subject = $subject === '' ? mb_substr($content, 0, 12) . '...' : $subject; $this->phpMailerInstance->Subject = $subject; /** * 正文 * 使用 html 文件内容作为正文,其中的图片将被 base64 编码,另确保 html 样式为内联形式,且某些样式可能需要 !important 方能正常显示, * msgHTML 方法的第二个参数指定 html 内容中图片的路径,在转换时会拼接 html 中图片的相对路径得到完整的路径,最右侧无需“/”,PHPMailer * 源码里有加。 css 中的背景图片不会被转换,这是 PHPMailer 已知问题,建议外链 * 此处也可替换为: * $this->phpMailerInstance->isHTML(true); // 设为html格式 * $this->phpMailerInstance->Body = '正文'; // 支持html * $this->phpMailerInstance->AltBody = 'This is an HTML-only message. To view it, activate HTML in your email application.'; // 纯文本消息正文。不支持html预览的邮件客户端将显示此预览消息,其它情况将显示正常的body */ if ($type === 1) { $template = file_get_contents(RESOURCES_PATH . '/mail/notice.html'); $message = sprintf($template, $content); } else if ($type === 2) { $template = file_get_contents(RESOURCES_PATH . '/mail/successful_renewal.html'); $realData = [ $data['username'], $data['renewalSuccessArr'] ? sprintf('续期成功:%s
', $this->genDomainsHtml($data['renewalSuccessArr'])) : '', $data['renewalFailuresArr'] ? sprintf('续期出错:%s
', $this->genDomainsHtml($data['renewalFailuresArr'])) : '', $this->genDomainStatusHtml($data['domainStatusArr']) ]; $message = $this->genMessageContent($realData, $template); } else if ($type === 3) { $template = file_get_contents(RESOURCES_PATH . '/mail/no_renewal_required.html'); $realData = [ $data['username'], $this->genDomainStatusHtml($data['domainStatusArr']) ]; $message = $this->genMessageContent($realData, $template); } else { throw new \Exception(lang('error_msg.100003')); } $this->phpMailerInstance->msgHTML($message, APP_PATH . '/mail'); try { if (!$this->phpMailerInstance->send()) { throw new MailException($this->phpMailerInstance->ErrorInfo); } return true; } catch (\Exception $e) { system_log('邮件发送失败:' . $e->getMessage()); return false; } } }