Mail.php 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. <?php
  2. /**
  3. * 邮件
  4. *
  5. * @author mybsdc <[email protected]>
  6. * @date 2019/5/12
  7. * @time 16:38
  8. */
  9. namespace Luolongfei\Libs\MessageServices;
  10. use Luolongfei\App\Exceptions\LlfException;
  11. use Luolongfei\Libs\Log;
  12. use PHPMailer\PHPMailer\PHPMailer;
  13. use PHPMailer\PHPMailer\Exception as MailException;
  14. use Luolongfei\Libs\Connector\MessageGateway;
  15. class Mail extends MessageGateway
  16. {
  17. /**
  18. * @var string
  19. */
  20. private $language;
  21. /**
  22. * @var string
  23. */
  24. private $noticeTemplatePath;
  25. /**
  26. * @var string
  27. */
  28. private $successfulRenewalTemplatePath;
  29. /**
  30. * @var string
  31. */
  32. private $noRenewalRequiredTemplatePath;
  33. /**
  34. * @var PHPMailer
  35. */
  36. private $phpMailerInstance;
  37. /**
  38. * @throws MailException
  39. */
  40. public function __construct()
  41. {
  42. $this->language = config('custom_language', 'zh');
  43. $this->noticeTemplatePath = sprintf('%s/mail/%s/notice.html', RESOURCES_PATH, $this->language);
  44. $this->successfulRenewalTemplatePath = sprintf('%s/mail/%s/successful_renewal.html', RESOURCES_PATH, $this->language);
  45. $this->noRenewalRequiredTemplatePath = sprintf('%s/mail/%s/no_renewal_required.html', RESOURCES_PATH, $this->language);
  46. $this->phpMailerInstance = new PHPMailer(true);
  47. $this->init();
  48. }
  49. /**
  50. * 初始化邮箱配置
  51. *
  52. * @throws MailException
  53. */
  54. protected function init()
  55. {
  56. $username = config('message.mail.username');
  57. $password = config('message.mail.password');
  58. list($host, $secure, $port) = $this->getBasicMailConf($username);
  59. $this->phpMailerInstance->SMTPDebug = config('debug') ? 2 : 0; // Debug 0:关闭 1:客户端信息 2:客户端和服务端信息
  60. $this->phpMailerInstance->isSMTP(); // 告诉 PHPMailer 使用 SMTP
  61. $this->phpMailerInstance->Host = $host; // SMTP 服务器
  62. $this->phpMailerInstance->SMTPAuth = true; // 启用 SMTP 身份验证
  63. $this->phpMailerInstance->Username = $username; // 账号
  64. $this->phpMailerInstance->Password = $password; // 密码或授权码
  65. $this->phpMailerInstance->SMTPSecure = $secure; // 将加密系统设置为使用 - ssl(不建议使用)或 tls
  66. $this->phpMailerInstance->Port = $port; // 设置 SMTP 端口号 - tsl 使用 587 端口,ssl 使用 465 端口
  67. $this->phpMailerInstance->CharSet = 'UTF-8'; // 防止中文邮件乱码
  68. $this->phpMailerInstance->setLanguage('zh_cn', VENDOR_PATH . '/phpmailer/phpmailer/language/'); // 设置语言
  69. $this->phpMailerInstance->setFrom($username, 'Im robot'); // 发件人
  70. }
  71. /**
  72. * 获取邮箱基本配置
  73. *
  74. * @param string $username
  75. *
  76. * @return array
  77. * @throws MailException
  78. */
  79. public function getBasicMailConf(string $username)
  80. {
  81. if (stripos($username, '@gmail.com') !== false) {
  82. $host = 'smtp.gmail.com';
  83. $secure = 'tls';
  84. $port = 587;
  85. } else if (stripos($username, '@qq.com') !== false) {
  86. $host = 'smtp.qq.com';
  87. $secure = 'tls';
  88. $port = 587;
  89. } else if (stripos($username, '@163.com') !== false) {
  90. $host = 'smtp.163.com';
  91. $secure = 'ssl';
  92. $port = 465;
  93. } else if (stripos($username, '@vip.163.com') !== false) {
  94. $host = 'smtp.vip.163.com';
  95. $secure = 'ssl';
  96. $port = 465;
  97. } else if (stripos($username, '@outlook.com') !== false) {
  98. $host = 'smtp.office365.com';
  99. $secure = PHPMailer::ENCRYPTION_STARTTLS;
  100. $port = 587;
  101. } else {
  102. $host = config('message.mail.host');
  103. $secure = config('message.mail.encryption');
  104. $port = (int)config('message.mail.port');
  105. if (!($host && $secure && $port)) {
  106. throw new MailException(lang('100069'));
  107. }
  108. }
  109. return [$host, $secure, $port];
  110. }
  111. /**
  112. * 生成域名 html
  113. *
  114. * @param array $domains
  115. *
  116. * @return string
  117. */
  118. public function genDomainsHtml(array $domains)
  119. {
  120. $domainsHtml = '';
  121. foreach ($domains as $domain) {
  122. $domainsHtml .= sprintf('<a href="http://%s" rel="noopener" target="_blank">%s</a>', $domain, $domain);
  123. }
  124. return $domainsHtml;
  125. }
  126. /**
  127. * 生成域名状态 html
  128. *
  129. * @param array $domainStatus
  130. *
  131. * @return string
  132. */
  133. public function genDomainStatusHtml(array $domainStatus)
  134. {
  135. if (empty($domainStatus)) {
  136. return lang('100070');
  137. }
  138. $domainStatusHtml = '';
  139. foreach ($domainStatus as $domain => $daysLeft) {
  140. $domainStatusHtml .= sprintf(lang('100071'), $domain, $domain, $daysLeft);
  141. }
  142. $domainStatusHtml = rtrim(rtrim($domainStatusHtml, ' '), ',,') . lang('100072');
  143. return $domainStatusHtml;
  144. }
  145. /**
  146. * 送信
  147. *
  148. * @param string $content
  149. * @param string $subject
  150. * @param integer $type
  151. * @param array $data
  152. * @param string|null $recipient
  153. * @param mixed ...$params
  154. *
  155. * @return bool
  156. * @throws LlfException
  157. * @throws MailException
  158. */
  159. public function send(string $content, string $subject = '', int $type = 1, array $data = [], ?string $recipient = null, ...$params)
  160. {
  161. $recipient = $recipient ?: config('message.mail.to');
  162. if (!$recipient) {
  163. throw new LlfException(34520012);
  164. }
  165. $this->check($content, $data);
  166. $this->phpMailerInstance->addAddress($recipient, config('message.mail.recipient_name', lang('100073'))); // 添加收件人,参数2选填
  167. $this->phpMailerInstance->addReplyTo(config('message.mail.reply_to', '[email protected]'), config('message.mail.reply_to_name', lang('100074'))); // 备用回复地址,收到的回复的邮件将被发到此地址
  168. /**
  169. * 抄送和密送都是添加收件人,抄送方式下,被抄送者知道除被密送者外的所有的收件人,密送方式下,
  170. * 被密送者知道所有的被抄送者,但不知道其它的被密送者。
  171. * 抄送好比@,密送好比私信。
  172. */
  173. // $this->phpMailerInstance->addCC('[email protected]'); // 抄送
  174. // $this->phpMailerInstance->addBCC('[email protected]'); // 密送
  175. // 添加附件,参数2选填
  176. // $this->phpMailerInstance->addAttachment('README.md', '说明.txt');
  177. // 标题
  178. $subject = $subject === '' ? mb_substr($content, 0, 12) . '...' : $subject;
  179. $this->phpMailerInstance->Subject = $subject;
  180. // 页脚
  181. $footer = '';
  182. /**
  183. * 正文
  184. * 使用 html 文件内容作为正文,其中的图片将被 base64 编码,另确保 html 样式为内联形式,且某些样式可能需要 !important 方能正常显示,
  185. * msgHTML 方法的第二个参数指定 html 内容中图片的路径,在转换时会拼接 html 中图片的相对路径得到完整的路径,最右侧无需“/”,PHPMailer
  186. * 源码里有加。 css 中的背景图片不会被转换,这是 PHPMailer 已知问题,建议外链
  187. * 此处也可替换为:
  188. * $this->phpMailerInstance->isHTML(true); // 设为html格式
  189. * $this->phpMailerInstance->Body = '正文'; // 支持html
  190. * $this->phpMailerInstance->AltBody = 'This is an HTML-only message. To view it, activate HTML in your email application.'; // 纯文本消息正文。不支持html预览的邮件客户端将显示此预览消息,其它情况将显示正常的body
  191. */
  192. if ($type === 1) {
  193. $template = file_get_contents($this->noticeTemplatePath);
  194. $this->setCommonFooter($footer, '<br>', false);
  195. $message = $this->genMessageContent([
  196. $content,
  197. $footer
  198. ], $template);
  199. } else if ($type === 2) {
  200. $template = file_get_contents($this->successfulRenewalTemplatePath);
  201. $this->setCommonFooter($footer, '<br>', false);
  202. $realData = [
  203. $data['username'],
  204. $data['renewalSuccessArr'] ? sprintf(lang('100075'), $this->genDomainsHtml($data['renewalSuccessArr'])) : '',
  205. $data['renewalFailuresArr'] ? sprintf(lang('100076'), $this->genDomainsHtml($data['renewalFailuresArr'])) : '',
  206. $this->genDomainStatusHtml($data['domainStatusArr']),
  207. $footer
  208. ];
  209. $message = $this->genMessageContent($realData, $template);
  210. } else if ($type === 3) {
  211. $template = file_get_contents($this->noRenewalRequiredTemplatePath);
  212. $this->setCommonFooter($footer, '<br>');
  213. $realData = [
  214. $data['username'],
  215. $this->genDomainStatusHtml($data['domainStatusArr']),
  216. $footer
  217. ];
  218. $message = $this->genMessageContent($realData, $template);
  219. } else if ($type === 4) {
  220. $template = file_get_contents($this->noticeTemplatePath);
  221. $this->setCommonFooter($footer, '<br>', false);
  222. $message = $this->genMessageContent([
  223. $this->newLine2Br($content),
  224. $footer
  225. ], $template);
  226. } else {
  227. throw new \Exception(lang('100003'));
  228. }
  229. $this->phpMailerInstance->msgHTML($message, RESOURCES_PATH . '/mail/' . $this->language);
  230. try {
  231. if (!$this->phpMailerInstance->send()) {
  232. throw new MailException($this->phpMailerInstance->ErrorInfo);
  233. }
  234. return true;
  235. } catch (\Exception $e) {
  236. system_log(lang('100077') . $e->getMessage());
  237. return false;
  238. }
  239. }
  240. }