### PHP 4 完全中文手册

意见信箱

当用户在参观网页时，有时想 Email 给 Webmaster 但是再执行 Email 程序总是不方使，用户在按下 mailto:abc@abc.abc.tw 还要花段时间打开自己这儿的 Outlook 岂不麻烦。这时，若是 Homepage 能提供写信的功能就太酷了。同时意见信箱还是以提醒或者要求用户一定要填入哪些资料，这对资料仓储而言，也是了解客户的最好方法。

整个意见信箱其实就像 Outlook 或者其它电子邮件软件，打开寄发新邮件的功能，不同的地方在于使用 Outlook 时，寄件人是固定的，而要填上收件人的地址；而网站上的意见信箱，收件人几乎都是 Webmaster，反而是要填上寄件人的电子邮件地址。当然另一个不同之处是 Outlook 处理寄信的步骤；而意见信箱是由 Web 服务器处理用户发送的信件。

在更进阶的设计后，甚至可以变成 Web Mail，像 HotMail 般，只要用浏览器就可以在任何地方用任何电脑收发信件。

当然，也可以做进阶的设计，将用户的意见储放在数据库中，留待日后整理成更有用的资料。不过这就不是这节要讨论的部份了。

在 UNIX 的系统中，大部份和电子邮件有关的问题都和 sendmail 有关，除非系统管理员较偏执，才会用其它的系统。因此，意见信箱的设计开发，也是使用 sendmail 来达成所需要的功能。而 WindowsNT 系统中，由于没有 sendmail 程序，需要另行符费购买，或使用其它的邮件派送软件，因此本节程序无法在 WindowsNT 系统执行。

程序的流程如下

送出填写意见的表格到用户的浏览器上。

用户填好后送出资料到服务器。

服务器将用户填的资料整理后，存入文件。

利用 UNIX 的管道指令及 sendmail 程序将意见送给系统管理人员。

服务器通知用户意见已送出。

以下就是完整的范例程序

<html> <head> <title>意见信箱</title> </head> <body>
<?php
$mailto="yourname@hahaha.com.tw";
if (($topic!="") and ($Email!="") and ($body!="")) {
$tmpfilename = tempnam("/tmp", "dm");
$fp = fopen($tmpfilename, "w");
fwrite($fp, "From: ".$Email."\n");
fwrite($fp, "Subject: ".$topic." <访客来信>\n\n");
fwrite($fp, $body."\n\n");
fwrite($fp, "送信人:".$sender."\n");
fwrite($fp, "发信IP:".$REMOTE_ADDR."\n");
fclose($fp);
$execstr="cat ".$tmpfilename." | /usr/lib/sendmail ".$mailto;
exec($execstr);
$execstr="echo $sender $REMOTE_HOST >> /var/log/mail.log";
exec($execstr);
echo "信件已送出！！本站工作人员尽快处理您的问题<p><br><br><br><br><br>";
} else {
?>
<form action=<? echo($GLOBALS["PHP_SELF"]); ?> method=post>
<table border=0>
<tr><td>主题</td><td><input type=text size=20 name=topic></tr>
<tr><td>姓名</td><td><input type=text size=20 name=sender></tr>
<tr><td>Email</td><td><input type=text size=20 name=Email></tr>
<tr><td colspan=2>内容<br><textarea cols=26 rows=10 name=body></textarea></td></tr>
<tr><td colspan=2><div align=right><input type=submit value="送出"></td></tr>
</table>
</form>
<?
}
?>
</body>
</html>

程序在 PHP 处理解析时，先判断用户是否填入资料。若没有资料则送出意见表单给用户，如果资料则表示用户已输入相关的资料，则进行处理。

处理的原则是先将用户填写的资料写入临时文件中，但为了防止多用户同时填写意见时，会造成文件被覆盖，因此需要每次都有不同的临时文件，这个问题可以使用 tempnam() 函数来解决，用来建立独一无二的临时文件。 在文件名的问题处理完后，利用 PHP 提供的文件处理功能，将用户填写的资料写入方才建立的文件中。将文件关闭就初步完成。即使资料没有邮寄出去，系统仍能保存意见文件。值得注意的是，若存放在 /tmp 中，有些 UNIX 的系统 (如 SUN Solaris) 会在重新启动系统时遗失这些资料，而有些则不会 (如 Slackware Linux)，这方面可能要先规划好，要保存的话需要存在不会被清掉的目录下。

UNIX 中最强的功能就是管道，可以利用管道来处理寄信的步骤，如下

cat tmpfilename | /usr/lib/sendmail wilson@biglobe.net.tw

这个指令的意思为将文件送给管道彼端的 sendmail 程序，而 sendmail 将该文件寄给 wilson@biglobe.net.tw。因此可利用本管道指令将意见寄给 Webmaster 或是客服部门的人员。若要寄给多人，可利用 mailing list 或是多用几次寄信的管道指令。

在 PHP 程序中要使用 UNIX 的程序或者外部指令，可以使用 exec() 函数来做。寄完信后，通知用户已经在处理了，就完成了意见处理的初步工作。当然之后要如何处理，就不是 PHP 书中所能讨论的。

当然执行寄信的方式不只一种，可以利用 mail() 函数来寄信，亦可利用 UNIX 的网络 socket 来做，所谓戏法人人会变，巧妙各有不同。

广告轮播

网络广告，变成了 Internet 上的热门学问。而 468x60 更变成了广告人员绞尽脑汁的尺寸。

在处理广告时，若能直接使用浏览器将广告的 468x60 图文件送到处理广告的服务器中，相信是件很舒服的事，不用再开 FTP 程序，搞大半天只为了 upload。

这个问题，是所有 Web CGI 程序的痛，包括 ASP、Prel....等等，都需要再经过系统元件的增加才能达成。号称最强的 Web CGI 程序: PHP，在这方面的表现没有令人失望，甚至傲视其它的 CGI 工具。

File Upload 功能在 RFC 1867 文件有有详细的说明，是利用特殊的文件格式 (content-type) multipart/form-data。值得注意的是浏览器一定要用 Netscape 3.0 以上或 MS Internet Explorer 4.0 以上的版本才能将文件上传。

先看下面的 HTML 源代码

<form enctype="multipart/form-data" action="next.php" method=post> 您的大名: <input type=text name=user><br> 文件名称: <input name="myfile" type="file"><br> <input type="submit" value="送出"> </form>

在 form 的标签中，要加入 enctype="multipart/form-data" 的字符串，表示用户输入的资料上有文件上传，同时 method 一定要用 POST 而不能用 GET。

在上面的码中，若用户姓名填入 Wilson Peng，并选 c:\myphoto.gif 的文件，在用户按下送出键后，浏览器则传送出下面的 POST 资料。

Content-type: multipart/form-data, boundary=AaB03x

--AaB03x

content-disposition: form-data; name="user"

Wilson Peng

--AaB03x

content-disposition: form-data; name="myfile"

Content-type: multipart/mixed, boundary=BbC04y

--BbC04y

Content-disposition: attachment; filename="myphoto.gif"

Content-type: image/gif

Content-Transfer-Encoding: binary

...myphoto.gif 内容略...

--BbC04y--

--AaB03x--

看到上面的资料中，boundary=AaB03x 即为分开不同字段资料的信息，其中的 AaB03x 编码方法，视浏览器的版本不同而异，通常是浏览器哈稀产生的。之后就可以看到用 --AaB03x 来隔开不同的字段。

以上面为例，处理 form 的 action 程序 next.php，会主动产生四个变量，见下表

变量名 | 说明
$myfile | 即上传的文件内容
$myfile_name | 上传文件在用户端的名称
$myfile_size | 上传文件的大小
$myfile_type | 上传文件的格式，如 "image/gif"

在 next.php 程序要做的最重要步骤，就是好好的使用这四个变量，否则程序一结束，用户上传的文件就消失了。因此，要先将 $myfile 复制到存放广告图的目录中

copy($banner,"/home1/biglobe3/ad/".$banner_name);

这行程序就是将文件存在 /home/htdocs/ad 的目录中，就上面的例子而言，就将文件存到 /home/htdocs/ad/myphoto.gif。重要的是，存放的目录不能是 Web Server 无法读到的目录，而应放在网站的 Homepage 所在目录中，才可以在网络上看到。

或许程序要更详细的处理，例如比对取得的文件大小与系统回报的是否相同....等等，就可以用 $myfile_size 变量了。

若在 form 中配置 input file 的名称改掉，则在 Upload 的变量也一起改，如

<input name="upfile" type="file">

则变量就改成 $upfile、$upfile_name、$upfile_size、与 $upfile_type。

因此，下面的例子就利用 File Upload 及 Oracle 7.x 后端数据库，将文件放在 Web Homepage 目录中，相关信息则存在 Oracle 中。当然，加上用户认证，让有帐号的用户才能上传图片，可避免刽客 (cracker) 等将不雅或不适当的广告上传。例中有关数据库的配置和 5.4 留言板的配置相同。

<html> <head> <?php // adadd.php if (($banner=="") and ($url=="")) {
?>
<title>新增广告</title>
</head>
<body>
加权值数字愈大，图片出现的机率就愈高，默认值为 1。
<FORM ENCTYPE="multipart/form-data" ACTION="adadd.php" METHOD=POST>
<table border=0>
<tr><td align=right>广告 Banner: </td><td><input name=banner TYPE="file"></td></tr>
<tr><td align=right>广告网址 URL: </td><td><input name=url type=text size=30></td></tr>
<tr><td align=right>辅助字符串 ALT: </td><td><input name=alt type=text size=30></td></tr>
<tr><td align=right>广告说明: </td><td><input name=descript type=text size=30></td></tr>
<tr><td align=right>显示加权: </td><td><input name=priority type=text size=5 value=1></td></tr>
<tr><td colspan=2 align=right><input type="submit" VALUE="确定"></td></tr>
</table>
</FORM>
<?
} else {
if (file_exists("/home/htdocs/ad/".$banner_name)) {
CommonHeader("文件 ".$banner_name." 已存在");
echo "<p><br><br>广告文件已经存在\n<p><br><br></body></html>";
exit;
};
copy($banner,"/home1/biglobe3/ad/".$banner_name);
putenv("ORACLE_SID=WWW");
putenv("NLS_LANG=american_taiwan.zht16big5");
putenv("ORACLE_HOME=/home/oracle/product/7.3.2");
putenv("LD_LIBRARY_PATH=/home/oracle/product/7.3.2/lib");
putenv("ORA_NLS=/home/oracle/product/7.3.2/ocommon/nls/admin/data");
putenv("ORA_NLS32=/home/oracle/product/7.3.2/ocommon/nls/admin/data");
$handle=ora_logon("user38@WWW","iam3849") or die;
$cursor=ora_open($handle);
ora_commitoff($handle);
$query="insert into ad(url, banner, alt, descript, priority) values('$url', '$banner_name', '$alt', '$descript', $priority)";
ora_parse($cursor, $query) or die;
ora_exec($cursor);
ora_close($cursor);
ora_logoff($handle);
echo "<title>广告新增完成</title>";
echo "</head>";
echo "<body>";
echo "<a href=".$url."><img src=/ad/".$banner_name." alt=\"".$alt."\" border=0></a><p>";
echo "<ul type=disc>";
echo "<li>广告网址: ".$url;
echo "<li>辅助字符串: ".$alt;
echo "<li>广告说明: ".$descript;
echo "<li>显示加权: ".$priority;
echo "</ul>";
}
?>
</body>
</html>

当然要使用上面的程序之前别忘了先增加 ad 资料表，SQL 及字段如下

CREATE TABLE ad (

url varchar2(1024) not null,

banner varchar2(1024) not null,

alt varchar2(255) null,

descript varchar2(255) null,

priority number(4) not null default 1

);

序号 | 字段 | 名称 | 资料类型 | 资料长度 | 字段说明
0 | 广告网址 | url | varchar2 | 1024 |
1 | 图片路径 | banner | varchar2 | 1024 |
2 | 字符串显示 | alt | varchar2 | 255 |
3 | 广告说明 | descript | varchar2 | 255 |
4 | 显示加权 | priority | number | 4 | 1 为默认值，0 表停用

值得一提的是在这加入了加权的功能，若一个广告要提升曝光率，则可以将显示加权的字段数字加大，例如 5，它的出现机率就会比只设为 1 的高五倍。

<?php // ad.php putenv("ORACLE_SID=WWW"); putenv("NLS_LANG=american_taiwan.zht16big5"); putenv("ORACLE_HOME=/home/oracle/product/7.3.2");
putenv("LD_LIBRARY_PATH=/home/oracle/product/7.3.2/lib");
putenv("ORA_NLS=/home/oracle/product/7.3.2/ocommon/nls/admin/data");
putenv("ORA_NLS32=/home/oracle/product/7.3.2/ocommon/nls/admin/data");
$handle=ora_logon("user38@WWW","iam3849") or die;
$cursor=ora_open($handle);
ora_commitoff($handle);
$query="SELECT url, banner, alt, priority FROM ad where priority > 0";
ora_parse($cursor, $query) or die;
ora_exec($cursor);
$i=$pricount=0;
while(ora_fetch($cursor)) {
$ad[$i][0] = ora_getcolumn($cursor,0);
$ad[$i][1] = ora_getcolumn($cursor,1);
$ad[$i][2] = ora_getcolumn($cursor,2);
$ad[$i][3] = ora_getcolumn($cursor,3);
$pricount += $ad[$i][3];
$i++;
};
ora_close($cursor);
ora_logoff($handle);
srand((double)microtime()*1000000);
$pri = rand(1,$pricount);
$pricount=0;
for($i=0; $i<count($ad); $i++) {
$pricount += $ad[$i][3];
if ($pri <= $pricount) {
$ad1[]="<a href=".$ad[$i][0]." target=new><img src=/ad/".$ad[$i][1]." width=468 height=60 border=0 alt=\"".$ad[$i][2]."\"></a>";
}
}
echo $ad1[0];
?>

上面的程序为公用的广告显示程序，其中的 $pricount 变量为所有广告 priority 加起来的和。程序先将所有的广告信息读到数组变量 $ad 中，随即关上数据库。再依时间取随机数种子，之后再从 1 到 $pricount 间随机取一个数字。

网页中要用广告程序，只要在需要广告的地方加上 <? include("ad.php"); ?> 就可以了，当然 Include 的路径 (在 httpd.conf 中) 要先设好才行。

上面的程序还有改进空间，可以加入广告的 Click Log 功能，或是显示的 Log 功能，改动显示加权的程序....等等，就不做范例了，毕竟在这儿是要介绍 PHP 的实际应用及程序开发，而不是套件开发。真的需要现成的广告套件，不妨到 http://www.phpwizard.net/phpAds，这是一套用 PHP 开发出来的广告程序。