VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > PHP >
  • PHP实现支持SSL连接的SMTP邮件发送类

这篇文章主要介绍了PHP实现支持SSL连接的SMTP邮件发送类,实例分析了php实现smtp邮件发送类的原理与技巧,以及支持SSL连接的方法,需要的朋友可以参考下

本文实例讲述了PHP实现支持SSL连接的SMTP邮件发送类,分享给大家供大家参考。具体如下:

该实例代码测试过了gmail和QQ邮箱的SMTP,具体代码如下:

  1. <?php 
  2. /** 
  3. * 邮件发送类 
  4. * 支持发送纯文本邮件和HTML格式的邮件,可以多收件人,多抄送,多秘密抄送,带附件(单个或多个附件),支持到服务器的ssl连接 
  5. * 需要的php扩展:sockets、Fileinfo和openssl。 
  6. * 编码格式是UTF-8,传输编码格式是base64 
  7. * @example 
  8. * $mail = new MySendMail(); 
  9. * $mail->setServer("smtp@126.com", "XXXXX@126.com", "XXXXX"); //设置smtp服务器,普通连接方式 
  10. * $mail->setServer("smtp.gmail.com", "XXXXX@gmail.com", "XXXXX", 465, true); //设置smtp服务器,到服务器的SSL连接 
  11. * $mail->setFrom("XXXXX"); //设置发件人 
  12. * $mail->setReceiver("XXXXX"); //设置收件人,多个收件人,调用多次 
  13. * $mail->setCc("XXXX"); //设置抄送,多个抄送,调用多次 
  14. * $mail->setBcc("XXXXX"); //设置秘密抄送,多个秘密抄送,调用多次 
  15. * $mail->addAttachment("XXXX"); //添加附件,多个附件,调用多次 
  16. * $mail->setMail("test", "<b>test</b>"); //设置邮件主题、内容 
  17. * $mail->sendMail(); //发送 
  18. */ 
  19. class MySendMail { 
  20.     /** 
  21.     * @var string 邮件传输代理用户名 
  22.     * @access protected 
  23.     */ 
  24.     protected $_userName
  25.     /** 
  26.     * @var string 邮件传输代理密码 
  27.     * @access protected 
  28.     */ 
  29.     protected $_password
  30.     /** 
  31.     * @var string 邮件传输代理服务器地址 
  32.     * @access protected 
  33.     */ 
  34.     protected $_sendServer
  35.     /** 
  36.     * @var int 邮件传输代理服务器端口 
  37.     * @access protected 
  38.     */ 
  39.     protected $_port
  40.     /** 
  41.     * @var string 发件人 
  42.     * @access protected 
  43.     */ 
  44.     protected $_from
  45.     /** 
  46.     * @var array 收件人 
  47.     * @access protected 
  48.     */ 
  49.     protected $_to = array(); 
  50.     /** 
  51.     * @var array 抄送 
  52.     * @access protected 
  53.     */ 
  54.     protected $_cc = array(); 
  55.     /** 
  56.     * @var array 秘密抄送 
  57.     * @access protected 
  58.     */ 
  59.     protected $_bcc = array(); 
  60.     /** 
  61.     * @var string 主题 
  62.     * @access protected 
  63.     */ 
  64.     protected $_subject
  65.     /** 
  66.     * @var string 邮件正文 
  67.     * @access protected 
  68.     */ 
  69.     protected $_body
  70.     /** 
  71.     * @var array 附件 
  72.     * @access protected 
  73.     */ 
  74.     protected $_attachment = array(); 
  75.     /** 
  76.     * @var reource socket资源 
  77.     * @access protected 
  78.     */ 
  79.     protected $_socket
  80.     /** 
  81.     * @var reource 是否是安全连接 
  82.     * @access protected 
  83.     */ 
  84.     protected $_isSecurity
  85.     /** 
  86.     * @var string 错误信息 
  87.     * @access protected 
  88.     */ 
  89.     protected $_errorMessage
  90.     /** 
  91.     * 设置邮件传输代理,如果是可以匿名发送有邮件的服务器,只需传递代理服务器地址就行 
  92.     * @access public 
  93.     * @param string $server 代理服务器的ip或者域名 
  94.     * @param string $username 认证账号 
  95.     * @param string $password 认证密码 
  96.     * @param int $port 代理服务器的端口,smtp默认25号端口 
  97.     * @param boolean $isSecurity 到服务器的连接是否为安全连接,默认false 
  98.     * @return boolean 
  99.     */ 
  100.     public function setServer($server$username=""$password=""$port=25, $isSecurity=false) { 
  101.         $this->_sendServer = $server
  102.         $this->_port = $port
  103.         $this->_isSecurity = $isSecurity
  104.         $this->_userName = emptyempty($username) ? "" : base64_encode($username); 
  105.         $this->_password = emptyempty($password) ? "" : base64_encode($password); 
  106.         return true; 
  107.     } 
  108.     /** 
  109.     * 设置发件人 
  110.     * @access public 
  111.     * @param string $from 发件人地址 
  112.     * @return boolean 
  113.     */ 
  114.     public function setFrom($from) { 
  115.         $this->_from = $from
  116.         return true; 
  117.     } 
  118.     /** 
  119.     * 设置收件人,多个收件人,调用多次. 
  120.     * @access public 
  121.     * @param string $to 收件人地址 
  122.     * @return boolean 
  123.     */ 
  124.     public function setReceiver($to) { 
  125.         $this->_to[] = $to
  126.         return true; 
  127.     } 
  128.     /** 
  129.     * 设置抄送,多个抄送,调用多次. 
  130.     * @access public 
  131.     * @param string $cc 抄送地址 
  132.     * @return boolean 
  133.     */ 
  134.     public function setCc($cc) { 
  135.         $this->_cc[] = $cc
  136.         return true; 
  137.     } 
  138.     /** 
  139.     * 设置秘密抄送,多个秘密抄送,调用多次 
  140.     * @access public 
  141.     * @param string $bcc 秘密抄送地址 
  142.     * @return boolean 
  143.     */ 
  144.     public function setBcc($bcc) { 
  145.         $this->_bcc[] = $bcc
  146.         return true; 
  147.     } 
  148.     /** 
  149.     * 设置邮件附件,多个附件,调用多次 
  150.     * @access public 
  151.     * @param string $file 文件地址 
  152.     * @return boolean 
  153.     */ 
  154.     public function addAttachment($file) { 
  155.         if(!file_exists($file)) { 
  156.             $this->_errorMessage = "file " . $file . " does not exist."
  157.             return false; 
  158.         } 
  159.         $this->_attachment[] = $file
  160.         return true; 
  161.     } 
  162.     /** 
  163.     * 设置邮件信息 
  164.     * @access public 
  165.     * @param string $body 邮件主题 
  166.     * @param string $subject 邮件主体内容,可以是纯文本,也可是是HTML文本 
  167.     * @return boolean 
  168.     */ 
  169.     public function setMail($subject$body) { 
  170.         $this->_subject = base64_encode($subject); 
  171.         $this->_body = base64_encode($body); 
  172.         return true; 
  173.     } 
  174.     /** 
  175.     * 发送邮件 
  176.     * @access public 
  177.     * @return boolean 
  178.     */ 
  179.     public function sendMail() { 
  180.         $command = $this->getCommand(); 
  181.         $this->_isSecurity ? $this->socketSecurity() : $this->socket(); 
  182.         foreach ($command as $value) { 
  183.             $result = $this->_isSecurity ? $this->sendCommandSecurity($value[0], $value[1]) : $this->sendCommand($value[0], $value[1]); 
  184.             if($result) { 
  185.                 continue
  186.             } 
  187.             else
  188.                 return false; 
  189.             } 
  190.         } 
  191.         //其实这里也没必要关闭,smtp命令:QUIT发出之后,服务器就关闭了连接,本地的socket资源会自动释放 
  192.         $this->_isSecurity ? $this->closeSecutity() : $this->close(); 
  193.         return true; 
  194.     } 
  195.     /** 
  196.     * 返回错误信息 
  197.     * @return string 
  198.     */ 
  199.     public function error(){ 
  200.         if(!isset($this->_errorMessage)) { 
  201.             $this->_errorMessage = ""
  202.         } 
  203.         return $this->_errorMessage; 
  204.     } 
  205.     /** 
  206.     * 返回mail命令 
  207.     * @access protected 
  208.     * @return array 
  209.     */ 
  210.     protected function getCommand() { 
  211.         $separator = "----=_Part_" . md5($this->_from . time()) . uniqid(); //分隔符 
  212.         $command = array
  213.                 array("HELO sendmail\r\n", 250) 
  214.             ); 
  215.         if(!emptyempty($this->_userName)){ 
  216.             $command[] = array("AUTH LOGIN\r\n", 334); 
  217.             $command[] = array($this->_userName . "\r\n", 334); 
  218.             $command[] = array($this->_password . "\r\n", 235); 
  219.         } 
  220.         //设置发件人 
  221.         $command[] = array("MAIL FROM: <" . $this->_from . ">\r\n", 250); 
  222.         $header = "FROM: <" . $this->_from . ">\r\n"
  223.         //设置收件人 
  224.         if(!emptyempty($this->_to)) { 
  225.             $count = count($this->_to); 
  226.             if($count == 1){ 
  227.                 $command[] = array("RCPT TO: <" . $this->_to[0] . ">\r\n", 250); 
  228.                 $header .= "TO: <" . $this->_to[0] .">\r\n"
  229.             } 
  230.             else
  231.                 for($i=0; $i<$count$i++){ 
  232.                     $command[] = array("RCPT TO: <" . $this->_to[$i] . ">\r\n", 250); 
  233.                     if($i == 0){ 
  234.                         $header .= "TO: <" . $this->_to[$i] .">"
  235.                     } 
  236.                     elseif($i + 1 == $count){ 
  237.                         $header .= ",<" . $this->_to[$i] .">\r\n"
  238.                     } 
  239.                     else
  240.                         $header .= ",<" . $this->_to[$i] .">"
  241.                     } 
  242.                 } 
  243.             } 
  244.         } 
  245.         //设置抄送 
  246.         if(!emptyempty($this->_cc)) { 
  247.             $count = count($this->_cc); 
  248.             if($count == 1){ 
  249.                 $command[] = array("RCPT TO: <" . $this->_cc[0] . ">\r\n", 250); 
  250.                 $header .= "CC: <" . $this->_cc[0] .">\r\n"
  251.             } 
  252.             else
  253.                 for($i=0; $i<$count$i++){ 
  254.                     $command[] = array("RCPT TO: <" . $this->_cc[$i] . ">\r\n", 250); 
  255.                     if($i == 0){ 
  256.                     $header .= "CC: <" . $this->_cc[$i] .">"
  257.                     } 
  258.                     elseif($i + 1 == $count){ 
  259.                         $header .= ",<" . $this->_cc[$i] .">\r\n"
  260.                     } 
  261.                     else
  262.                         $header .= ",<" . $this->_cc[$i] .">"
  263.                     } 
  264.                 } 
  265.             } 
  266.         } 
  267.         //设置秘密抄送 
  268.         if(!emptyempty($this->_bcc)) { 
  269.             $count = count($this->_bcc); 
  270.             if($count == 1) { 
  271.                 $command[] = array("RCPT TO: <" . $this->_bcc[0] . ">\r\n", 250); 
  272.                 $header .= "BCC: <" . $this->_bcc[0] .">\r\n"
  273.             } 
  274.             else
  275.                 for($i=0; $i<$count$i++){ 
  276.                     $command[] = array("RCPT TO: <" . $this->_bcc[$i] . ">\r\n", 250); 
  277.                     if($i == 0){ 
  278.                     $header .= "BCC: <" . $this->_bcc[$i] .">"
  279.                     } 
  280.                     elseif($i + 1 == $count){ 
  281.                         $header .= ",<" . $this->_bcc[$i] .">\r\n"
  282.                     } 
  283.                     else
  284.                         $header .= ",<" . $this->_bcc[$i] .">"
  285.                     } 
  286.                 } 
  287.             } 
  288.         } 
  289.         //主题 
  290.         $header .= "Subject: =?UTF-8?B?" . $this->_subject ."?=\r\n"
  291.         if(isset($this->_attachment)) { 
  292.             //含有附件的邮件头需要声明成这个 
  293.             $header .= "Content-Type: multipart/mixed;\r\n"
  294.         } 
  295.         elseif(false){ 
  296.             //邮件体含有图片资源的,且包含的图片在邮件内部时声明成这个,如果是引用的远程图片,就不需要了 
  297.             $header .= "Content-Type: multipart/related;\r\n"
  298.         } 
  299.         else
  300.             //html或者纯文本的邮件声明成这个 
  301.             $header .= "Content-Type: multipart/alternative;\r\n"
  302.         } 
  303.         //邮件头分隔符 
  304.         $header .= "\t" . 'boundary="' . $separator . '"'
  305.         $header .= "\r\nMIME-Version: 1.0\r\n"
  306.         //这里开始是邮件的body部分,body部分分成几段发送 
  307.         $header .= "\r\n--" . $separator . "\r\n"
  308.         $header .= "Content-Type:text/html; charset=utf-8\r\n"
  309.         $header .= "Content-Transfer-Encoding: base64\r\n\r\n"
  310.         $header .= $this->_body . "\r\n"
  311.         $header .= "--" . $separator . "\r\n"
  312.         //加入附件 
  313.         if(!emptyempty($this->_attachment)){ 
  314.             $count = count($this->_attachment); 
  315.             for($i=0; $i<$count$i++){ 
  316.                 $header .= "\r\n--" . $separator . "\r\n"
  317.                 $header .= "Content-Type: " . $this->getMIMEType($this->_attachment[$i]) . '; name="=?UTF-8?B?' . base64_encodebasename($this->_attachment[$i]) ) . '?="' . "\r\n"
  318.                 $header .= "Content-Transfer-Encoding: base64\r\n"
  319.                 $header .= 'Content-Disposition: attachment; filename="=?UTF-8?B?' . base64_encodebasename($this->_attachment[$i]) ) . '?="' . "\r\n"
  320.                 $header .= "\r\n"
  321.                 $header .= $this->readFile($this->_attachment[$i]); 
  322.                 $header .= "\r\n--" . $separator . "\r\n"
  323.             } 
  324.         } 
  325.         //结束邮件数据发送 
  326.         $header .= "\r\n.\r\n"
  327.  
  328.         $command[] = array("DATA\r\n", 354); 
  329.         $command[] = array($header, 250); 
  330.         $command[] = array("QUIT\r\n", 221); 
  331.         return $command
  332.     } 
  333.     /** 
  334.     * 发送命令 
  335.     * @access protected 
  336.     * @param string $command 发送到服务器的smtp命令 
  337.     * @param int $code 期望服务器返回的响应吗 
  338.     * @return boolean 
  339.     */ 
  340.     protected function sendCommand($command$code) { 
  341.         echo 'Send command:' . $command . ',expected code:' . $code . '<br />'
  342.         //发送命令给服务器 
  343.         try{ 
  344.             if(socket_write($this->_socket, $commandstrlen($command))){ 
  345.                 //当邮件内容分多次发送时,没有$code,服务器没有返回 
  346.                 if(emptyempty($code))  { 
  347.                     return true; 
  348.                 } 
  349.                 //读取服务器返回 
  350.                 $data = trim(socket_read($this->_socket, 1024)); 
  351.                 echo 'response:' . $data . '<br /><br />'
  352.                 if($data) { 
  353.                     $pattern = "/^".$code."+?/"
  354.                     if(preg_match($pattern$data)) { 
  355.                         return true; 
  356.                     } 
  357.                     else
  358.                         $this->_errorMessage = "Error:" . $data . "|**| command:"
  359.                         return false; 
  360.                     } 
  361.                 } 
  362.                 else
  363.                     $this->_errorMessage = "Error:" . socket_strerror(socket_last_error()); 
  364.                     return false; 
  365.                 } 
  366.             } 
  367.             else
  368.                 $this->_errorMessage = "Error:" . socket_strerror(socket_last_error()); 
  369.                 return false; 
  370.             } 
  371.         }catch(Exception $e) { 
  372.             $this->_errorMessage = "Error:" . $e->getMessage(); 
  373.         } 
  374.     } 
  375.     /** 
  376.     * 安全连接发送命令 
  377.     * @access protected 
  378.     * @param string $command 发送到服务器的smtp命令 
  379.     * @param int $code 期望服务器返回的响应吗 
  380.     * @return boolean 
  381.     */ 
  382.     protected function sendCommandSecurity($command$code) { 
  383.         echo 'Send command:' . $command . ',expected code:' . $code . '<br />'
  384.         try { 
  385.             if(fwrite($this->_socket, $command)){ 
  386.                 //当邮件内容分多次发送时,没有$code,服务器没有返回 
  387.                 if(emptyempty($code))  { 
  388.                     return true; 
  389.                 } 
  390.                 //读取服务器返回 
  391.                 $data = trim(fread($this->_socket, 1024)); 
  392.                 echo 'response:' . $data . '<br /><br />'
  393.                 if($data) { 
  394.                     $pattern = "/^".$code."+?/"
  395.                     if(preg_match($pattern$data)) { 
  396.                         return true; 
  397.                     } 
  398.                     else
  399.                         $this->_errorMessage = "Error:" . $data . "|**| command:"
  400.                         return false; 
  401.                     } 
  402.                 } 
  403.                 else
  404.                     return false; 
  405.                 } 
  406.             } 
  407.             else
  408.                 $this->_errorMessage = "Error: " . $command . " send failed"
  409.                 return false; 
  410.             } 
  411.         }catch(Exception $e) { 
  412.             $this->_errorMessage = "Error:" . $e->getMessage(); 
  413.         } 
  414.     } 
  415.     /** 
  416.     * 读取附件文件内容,返回base64编码后的文件内容 
  417.     * @access protected 
  418.     * @param string $file 文件 
  419.     * @return mixed 
  420.     */ 
  421.     protected function readFile($file) { 
  422.         if(file_exists($file)) { 
  423.             $file_obj = file_get_contents($file); 
  424.             return base64_encode($file_obj); 
  425.         } 
  426.         else { 
  427.             $this->_errorMessage = "file " . $file . " dose not exist"
  428.             return false; 
  429.         } 
  430.     } 
  431.     /** 
  432.     * 获取附件MIME类型 
  433.     * @access protected 
  434.     * @param string $file 文件 
  435.     * @return mixed 
  436.     */ 
  437.     protected function getMIMEType($file) { 
  438.         if(file_exists($file)) { 
  439.             $mime = mime_content_type($file); 
  440.             /*if(! preg_match("/gif|jpg|png|jpeg/", $mime)){ 
  441.                 $mime = "application/octet-stream"; 
  442.             }*/ 
  443.             return $mime
  444.         } 
  445.         else { 
  446.             return false; 
  447.         } 
  448.     } 
  449.     /** 
  450.     * 建立到服务器的网络连接 
  451.     * @access protected 
  452.     * @return boolean 
  453.     */ 
  454.     protected function socket() { 
  455.         //创建socket资源 
  456.         $this->_socket = socket_create(AF_INET, SOCK_STREAM, getprotobyname('tcp')); 
  457.         if(!$this->_socket) { 
  458.             $this->_errorMessage = socket_strerror(socket_last_error()); 
  459.             return false; 
  460.         } 
  461.         socket_set_block($this->_socket);//设置阻塞模式 
  462.         //连接服务器 
  463.         if(!socket_connect($this->_socket, $this->_sendServer, $this->_port)) { 
  464.             $this->_errorMessage = socket_strerror(socket_last_error()); 
  465.             return false; 
  466.         } 
  467.         $str = socket_read($this->_socket, 1024); 
  468.         if(!preg_match("/220+?/"$str)){ 
  469.             $this->_errorMessage = $str
  470.             return false; 
  471.         } 
  472.         return true; 
  473.     } 
  474.     /** 
  475.     * 建立到服务器的SSL网络连接 
  476.     * @access protected 
  477.     * @return boolean 
  478.     */ 
  479.     protected function socketSecurity() { 
  480.         $remoteAddr = "tcp://" . $this->_sendServer . ":" . $this->_port; 
  481.         $this->_socket = stream_socket_client($remoteAddr$errno$errstr, 30); 
  482.         if(!$this->_socket){ 
  483.             $this->_errorMessage = $errstr
  484.             return false; 
  485.         } 
  486.         //设置加密连接,默认是ssl,如果需要tls连接,可以查看php手册stream_socket_enable_crypto函数的解释 
  487.         stream_socket_enable_crypto($this->_socket, true, STREAM_CRYPTO_METHOD_SSLv23_CLIENT); 
  488.         stream_set_blocking($this->_socket, 1); //设置阻塞模式 
  489.         $str = fread($this->_socket, 1024); 
  490.         if(!preg_match("/220+?/"$str)){ 
  491.             $this->_errorMessage = $str
  492.             return false; 
  493.         } 
  494.         return true; 
  495.     } 
  496.     /** 
  497.     * 关闭socket 
  498.     * @access protected 
  499.     * @return boolean 
  500.     */ 
  501.     protected function close() { 
  502.         if(isset($this->_socket) && is_object($this->_socket)) { 
  503.             $this->_socket->close(); 
  504.             return true; 
  505.         } 
  506.         $this->_errorMessage = "No resource can to be close"
  507.         return false; 
  508.     } 
  509.     /** 
  510.     * 关闭安全socket 
  511.     * @access protected 
  512.     * @return boolean 
  513.     */ 
  514.     protected function closeSecutity() { 
  515.         if(isset($this->_socket) && is_object($this->_socket)) { 
  516.             stream_socket_shutdown($this->_socket, STREAM_SHUT_WR); 
  517.             return true; 
  518.         } 
  519.         $this->_errorMessage = "No resource can to be close"
  520.         return false; 
  521.     } 
  522. }
  523.  

出处:http://www.phpfensi.com/php/20210515/15343.html


相关教程