22.2 功能设计和实现
本节开始实现用PHP发送邮件的程序。该程序的基本功能是,通过PHP程序,向SMTP发起请求,待SMTP服务器验证通过后,向PHP程序指定的邮箱发送邮件。使用mail函数也可以发送邮件,但其功能比较弱,不便于调试,而且在Windows下需要配置SMTP服务器,所以本章将通过Socket实现发送邮件的PHP程序。本节将设计一个发送邮件的PHP类,通过这个类完成向SMTP服务器发起请求以及发送邮件的功能。
22.2.1 设计一个发送邮件的类
有了22.1节的理论基础,实现发送邮件的类并不是一件困难的事。首先,确定该类应该有以下成员变量。
·$lastmessage:改变量记录服务器最后返回的响应信息。
·$lastact:最近一次要执行的操作,即是HELLO还是MAIL FROM请求等。
·$welcome:用在HELLO命令后面的字符串。
·$debug:是否显示调试信息。
·$smtp:smtp服务器。
·$port:smtp端口号。
·$fp:socket句柄。
需要实现的函数包括类的构造函数,显示调试信息的函数,处理Socket的函数以及发送邮件的函数。其中发送邮件的函数其实就是用来向Socket发送执行SMTP请求的命令。代码22-1是这个类的完整实现。
代码22-1 发送邮件的PHP类mail.php
01 <?php 02 class smtp_mail 03 { 04 var $lastmessage; // 记录最后返回的响应信息 05 var $lastact; // 最近一次要执行的操作 06 var $welcome; // 用在HELLO 命令后面的字符串 07 var $debug; // 是否显示调试信息 08 var $smtp; //smtp 服务器 09 var $port; //smtp 端口号 10 var $fp; //socket 句柄 11 12 function smtp_mail($smtp, $welcome="", $debug=false) 13 { 14 if(empty($smtp)) die("SMTP cann't be NULL!"); 15 16 $this->smtp=$smtp; 17 if(empty($welcome)) 18 { 19 $this->welcome=gethostbyaddr("localhost"); 20 } 21 else 22 $this->welcome=$welcome; 23 $this->debug=$debug; 24 25 $this->lastmessage=""; 26 $this->lastact=""; 27 $this->port="25"; 28 } 29 30 function show_debug($message, $inout) 31 { 32 $b = false; 33 if ($this->debug) 34 { 35 if($inout=="in") // 响应信息 36 { 37 $m='<< '; 38 $b = true; 39 } 40 else // 请求指令 41 $m='>> '; 42 if(!ereg("\n$", $message)) 43 $message .= ""; 44 $message=nl2br($message); 45 if($b) 46 echo "<b>${m}${message}</b>"; 47 else 48 echo "${m}${message}"; 49 } 50 } 51 52 function do_command($command, $code) // 处理Socket 53 { 54 $this->lastact=$command; 55 $this->show_debug($this->lastact, "out"); 56 fputs ( $this->fp, $this->lastact ); 57 58 $this->lastmessage = fgets ( $this->fp, 512 ); 59 $this->show_debug($this->lastmessage, "in"); 60 61 if(!ereg("^$code", $this->lastmessage)) 62 { 63 return false; 64 } 65 else 66 return true; 67 } 68 69 function send($from,$to,$subject,$message) // 发送邮件 70 { 71 $this->show_debug("Connect to SMTP server : ".$this->smtp."\n", "out"); 72 $this->fp = fsockopen ( $this->smtp, $this->port ); 73 if ( $this->fp ) 74 { 75 set_socket_blocking( $this->fp, true ); 76 $this->lastmessage=fgets($this->fp,512); 77 $this->show_debug($this->lastmessage, "in"); 78 79 if (! ereg ( "^220", $this->lastmessage ) ) 80 { 81 return false; 82 } 83 else 84 { 85 $this->lastact="HELLO " . $this->welcome . "\n"; 86 if(!$this->do_command($this->lastact, "250")) 87 { 88 fclose($this->fp); 89 return false; 90 } 91 92 $this->lastact="MAIL FROM:<$from>". "\n"; 93 if(!$this->do_command($this->lastact, "250")) 94 { 95 fclose($this->fp); 96 return false; 97 } 98 99 $this->lastact="RCPT TO:<$to>" . "\n"; 100 if(!$this->do_command($this->lastact, "250")) 101 { 102 fclose($this->fp); 103 return false; 104 } 105 106 $this->lastact="DATA\n"; // 发送正文 107 if(!$this->do_command($this->lastact, "354")) 108 { 109 fclose($this->fp); 110 return false; 111 } 112 113 $head="Subject: $subject\n"; // 处理Subject 114 if(!empty($subject) && !ereg($head, $message)) 115 { 116 $message = $head.$message; 117 } 118 119 120 $head="From: $from\n"; // 处理From 121 if(!empty($from) && !ereg($head, $message)) 122 { 123 $message = $head.$message; 124 } 125 126 $head="To: $to\n"; // 处理To 127 if(!empty($to) && !ereg($head, $message)) 128 { 129 $message = $head.$message; 130 } 131 132 if(!ereg("\n\.\n", $message)) // 加上结束串 133 $message .= "\n.\n"; 134 $this->show_debug($message, "out"); 135 fputs($this->fp, $message); 136 137 $this->lastact="QUIT\n"; 138 if(!$this->do_command($this->lastact, "250")) 139 { 140 fclose($this->fp); 141 return false; 142 } 143 } 144 return true; 145 } 146 else 147 { 148 $this->show_debug("Connect failed!", "in"); 149 return false; 150 } 151 } 152 } 153 ?>
【代码解析】构造函数smtp_mail()完成一些初始值的判定及设置。参数$welcome用于HELLO指令中,告诉服务器用户的名字或者直接填写服务器的名称,表示向服务器打招呼。如果用户没有给出$welcome,则自动查找本地的机器名,如代码第19行所示。
函数show_debug()用来显示调试信息,可以在参数$inout中指定是请求指令还是返回的响应。如果为请求指令,则使用“out”;如果为返回的响应,则使用“in”。代码第35行判断是否为响应信息,如果是,将信息的前面加上“<<”来区别响应指令,加上“>>”来区别请求指令。同时将响应信息的字符串字体加粗显示,如代码第46行和第48行所示。
函数do_command()用来处理Socket部分,通过Socket处理参数$command所表示的指令,对于HELLO、MAIL FROM、RCPT TO、QUIT和DATA指令,都要求根据是否显示调试信息将相关内容显示出来,并且根据参数$code所表示的服务器响应码,决定是否中断处理。代码第56行使用fputs向服务器发送请求指令。该函数一方面完成指令及信息的发送、显示功能,另一方面根据返回的响应判断是否成功。
函数send()用来发送邮件的函数,它共有4个参数,分别是:$from是发信邮件地址;$to是收信人邮件地址;$subject是邮件的主题;$message是邮件体,也就是邮件正文。如果处理成功,该函数返回true,否则返回false。其中的代码$this->fp=fsockopen($this->smtp,$this->port),作用是使用函数fsockopen()打开一个网络链接,其第一个参数是表示此socket链接的标识符,第2个参数标识在哪个端口打开此链接。
22.2.2 测试发送邮件功能
22.2.1小节实现了发送邮件的PHP类,本小节对这个类的功能加以测试。首先编写一个测试程序,代码22-2是一个测试程序。
代码22-2 测试smtp_mail类的PHP程序testmail.php
01 <?php 02 include "mail.php"; 03 echo "<b>start....</b><br/>"; 04 05 $email="Hello, this is a test mail!"; 06 $sendmail=new smtp_mail("smtp.sina.com", "sina", true);// 实例化一个smtp_mail 类的对象 07 if($sendmail->send("macbooks@domain", " macbooks@domain", "test", $email)) // 发送邮件 08 { 09 echo " 发送成功!"; 10 } 11 else 12 { 13 echo " 发送失败!"; 14 } 15 16 echo "<br/><b>end....</b><br/>"; 17 ?>
【代码解析】这是一个很简单的测试程序。代码第02行将smtp_mail类的定义包含进该程序,然后在第05行定义了一个字符串变量$email表示邮件的正文。在第06行实例化一个smtp_mail类的对象,第07行就是调用该对象的send()方法发送邮件了,其中该函数的发送地址参数和接收地址参数都为macbooks@sina.com,读者调试时,需要使用自己真实的地址。第05行初始化对象时,所使用的SMTP服务器地址smtp.sina.com也仅仅是个示例,读者可以根据实际情况替换成其他的SMTP服务器地址。设定好SMTP服务器地址、发件人地址和收件人地址,执行程序testmail.php,如果一切正常,会看到类似图22-1所示的结果;如果有错误,也可以看到相关的服务器响应信息和错误代码。
22.2.3 发送邮件的表单及程序实现
有了可以发送电子邮件的PHP类之后,最后制作一个发送邮件的简易界面和执行发送的PHP程序。
(1)发送电子邮件的用户界面使用HTML编写,可以参考代码22-3。
代码22-3 发送邮件的用户界面user.htm
01 <html> 02 <head> 03 <meta http-equiv="Content-Type" content="text/html; charset=gb2312" /> 04 <title> 使用PHP 发送电子邮件</title> 05 <style type="text/css"> 06 <!-- 07 .STYLE1 {font-size: 12px} 08 .STYLE2 { 09 font-size: 24px; 10 font-weight: bold; 11 } 12 --> 13 </style> 14 </head> 15 16 <body> 17 <p class="STYLE2"> 使用PHP 发电子邮件</p> 18 <form name="form1" method="post" action="send_mail.php"> 19 <table width="444" height="347" border="0" > 20 <tr> 21 <td width="71" height="23" bgcolor="#D6B1E9"> 22 <div class="STYLE1"> 23 <div > 收件人</div> 24 </div> 25 </td> 26 <td width="363"> 27 <label> <input type="text" name="sendto"></label> 28 </td> 29 </tr> 30 <tr> 31 <td height="27" bgcolor="#D6B1E9"> 32 <div class="STYLE1"> 33 <div > 邮件标题</div> 34 </div></td> 35 <td> 36 <label> <input type="text" name="subject"></label> 37 </td> 38 </tr> 39 <tr> 40 <td height="23" colspan="2" bgcolor="#D6B1E9"> 41 <div class="STYLE1"> 42 <div > 邮件正文</div> 43 </div> 44 </td> 45 </tr> 46 <tr> 47 <td colspan="2" bgcolor="#D6B1E9"> 48 <div > 49 <label> 50 <div > 51 <textarea name="emailcontent" cols="60" rows="18"></textarea> 52 </div> 53 </label> 54 </div> 55 <div ></div> 56 <div ></div> 57 </td> 58 </tr> 59 <tr> 60 <td colspan="2"> 61 <label><input type="submit" name="Submit" value=" 提交"></label> 62 </td> 63 </tr> 64 </table> 65 </form> 66 67 </body> 68 </html>
【代码解析】 这是一个常见的邮件发送界面,包含收件人、标题和邮件内容,使用了<table>标签实现界面对齐。
(2)该HTML页面会将收件人、邮件标题等信息提交至程序send_mail.php做处理。该页面显示效果如图22-2所示。
图22-1 发送邮件测试程序的执行结果
图22-2 发送邮件的简单界面
(3)接下来完成发送电子邮件的PHP程序,该程序将使用22.2.1小节创建的smtp_mail类完成发送邮件的功能。这个程序并不复杂,只需对22.2.2小节编写的testmail.php稍做修改即可,如代码22-4所示。
代码22-4 发送邮件的程序send_mail.php
01 <?php 02 include "mail.php"; // 将指定的文件包含进来 03 04 if(isset($_POST['sendto'])) // 判断收信人 05 $sendto = $_POST['sendto']; 06 else 07 $sendto = ''; 08 09 if(isset($_POST['subject'])) // 判断主题 10 $subject = $_POST['subject']; 11 else 12 $subject = ''; 13 14 if(isset($_POST['emailcontent'])) // 判断邮件内容 15 $emailcontent = $_POST['emailcontent']; 16 else 17 $emailcontent = ''; 18 19 if(empty($sendto) || empty($subject) || empty($emailcontent)) 20 { 21 echo '<b> 请填写收件人email 地址、邮件主题和邮件正文。</b><br/>'; 22 exit; 23 } 24 25 $sendmail=new smtp_mail("smtp.sina.com", "sina", false); 26 if($sendmail->send("macbooks@sina.com", $sendto, $subject, $emailcontent))// 发信 27 { 28 echo " 发送成功!"; 29 } 30 else 31 { 32 echo " 发送失败!"; 33 } 34 ?>
【代码解析】程序send_mail.php通过使用类smtp_mail完成邮件的发送。代码第26行发送邮件所使用的邮件地址是个例子,读者需要根据实际情况加以替换。
共有条评论 网友评论