【前端阅读】——《活用PHP、MySQL建构Web世界》摘记之高级应用

一、高级应用

1、计数器

计数器的原理很简单,只有两步:

第一步就是读写一个数字,第二步就是显示出来。一般CGI'大多直接写到文件系统,当然也可以利用MySQL来存储这个数字,完成第一步的操作。

第二步,除了直接写出数字之外,也可以用PHP的GD函数来试试。

(注:计数器一般都是以CGI程序做的,一般不会有人为了记这么一个数字建一个数据库,不过它很具有示范作用)

//方法1:读写文件系统Filesystem
<?
$arr = file("counter.txt");
$count = (int)$arr[0];
$fp = fopen("counter.txt","w");
fputs($fp,++$count);
fclose($fp);
?>

 这个程序要能够运行前:(勿忘修改两点)

第一要先建立一个counter.txt,里面含有一个数字0;

第二它忽略了应该对counter.txt做LOCK的操作。

//显示 这个数字
<? echo "您是第$count位访客"; ?>

高级显示:用PHP生成一个图形

//PHP 生成图形(5位数的计数器:00100)
<?
Header("Content-type:image/gif");
//...略去读入、更改count的部分
$im = ImageCreate(45,16);
$white = IMageColorAllocate($im,255,255,255);
$black = ImageColorAllocate($im,0,0,0);
imagefill($im,1,1,$black);
imagestring($im,5,0,0,sprintf("%05d",$count),$white);
imagegif($im);
imagedestroy($im);
?>

除了写到文件系统之外,还可以把到访次数,写到数据库的Table里,建立如下的数据库:

//方法二:写到数据库Database
mysql>creat table counter(   
      id int unsigned,
      count int unsigned
);

 用Id作为不同网站/页面的区分,而Count用于记录到访次数。那么一个Select命令:

select count from counter where id=3

 就可以读出我们的Count值

update counter set count = count+1 where id=3

也能轻易地Update数值(注意:不是count = $count +1

这个方法如果没有做LOCK,影响层面不大,最多显示的数字差几个,而count+1的操作不会Lost掉

使用数据库的好处是:对于每一个PHTML文件,都可以一个简单的Include操作,让这个Page的到访次数被统计(不一定显示)。

2、流量统计

       流量统计是计数器的高级应用:世界上有许多软件可以帮助做流量统计的工作,大多使用HTTPD的Log文件(access.log)直接作分析,可以做到相当详尽而漂亮、清楚的图形显示,甚至会在适当的时候提醒一些信息。

       这里介绍的是另外一种较简单的作法。对于“流量”,一般较感兴趣的,是使用者从什么地方联机进来?进来的时间?使用什么浏览器等信息。这些在PHP里,就是PHP的环境参数。如果在记录到访次数时,同时将这些参数记录下来,就可以用来统计。至于统计的结果,当然也可以用简单的图形来表示。

//第一步:建立Flow Table
mysql>create table flow(
    id int unsigned,
    accdate DATE,
    acctime TIME,
    browser varCHAR(30),
    usrhost varCHAR(30)
);
//第二步:PHP记录日期、时间、浏览器信息以及使用者的Host Name等,Id一样是用来区隔不同的网站/网页的流量数据
<?
 $str = "insert into flow values(3,CURRENT_DATE(),CURRENT_TIME(),'$HTTP_USER_AGENT,'$REMOTE_HOST')"
 mysql_query($str,$link);
?>

第三步:读出数据,做一些简单的统计工作。比方说,想知道有多少人是从Hinet过来

select count(*) from flow where usrhost like '%hinet.net'

输出接口可以考虑用百分比长条图,直接一个ImageFilledRectangle(),就可以把算出来的一部分数据画成长条图了。、

3、留言板

利用数据库做留言板,最简单了。考虑一下需要的字段:姓名、张贴时间、Email、标题、内容、签名文件等。

第一步:建立一个数据库MSGboard(以及相关的Privileges),含有这么一个Table

mysql>create table board(
     serial INT unsigned not null auto_inctement,
     id INT unsigned not null,
     postname CHAR(10),
     postemail varCHAR(30),
     posttime  DATETIME,
     subject var CHAR(40),
     content tinyBLOB,
     remarks  varCHAR(255),
     primary key(serial),
     index(id)
);

特别的:我们使用一个Serial代表序号,Id来分别不同的留言板,另外加入了Index的概念,这样子在读取数据时会比较有效率。

第二步:写一个HTML接口让读者可以输入留言

<form antion="board.phtml" method="POST">
您的大名:<input type="Text" name="postname"><br>
您的信箱:<input type="Text" name="postemail"><br>
留言标题:<input type="Text" name="subject"><br>
留言内容:<textarea name="content" cols="20" rpws="4" 
wrap="VIRTUAL"></textarea><br>
签名文字:<textarea name="remarks" cols="20" rows="2"
wrap=“VIRTUAL”></textarea><br>
<input type="Submit" name="" value="传送">
<input type="reset" vlue="重写">
</form>

第三步:在borad.phtml中,只要一行Insert命令,把数据写进去即可。至于读取留言,直接一次Select出来,然后显示出来就可以了。当然必要的美化画面是免不了的。

<?//php读取留言
$str = "select postname,postemail,
            dateformat('y-m-d H:i',posttime),"
            subject,content,remarks
            form board
            where id=3";
$result = mysql_query(...);
while(list($pname,$$pemail,$ptime,$subj,$content,$remk)=
mysql_fetch_row($result);
?>
姓名:<a href="mailto:"<?echo $pemail;?>"<?echo $pname;?></a><br>
留言时间:<?echo $ptime;?><br>
标题:<?echo $subj;?><br>
内容:<?echo $cotent;?><br>
签名:<?echo $remk;?><br><p>
<?
endwhile
?>

几个可议的地方:

  • 在“留言内容”里贴上HTML代码,会怎样?直觉的看法,应该在输出的时候,原来的HTML会发生作用,例如<fontsize="+3">...</font>会真的把字放大3级;然而,由于HTML里含有双引号(“”),会造成PHP对字符串的混淆。所以结果反而可能出现Error信息。解决这个问题的方法,在输入到数据库之前,先把所有的双引号等特殊字符Escape掉(在前面加上反斜线字符),具体参考字符串函数addslashes()与quotemeta()。
  • 至于要不要开发使用者输入HTML代码,也是依个人喜好而定,原则上来说应该开放,不过万一读者的HTML代码不小心写错了,那么辛苦做好的格式可能会走样
  • 在Textarea里面,用户也许很自然的用ENTER换行,用TAB或数个空格来达到他要的效果。记录到数据库里,不过对应到( ),( )与Space字符。于是在输出的时候,HTML代码真的会有换行、TAB与空格,但是用Browser看到却完全连在一起。所以,应该事先把这些字符换掉。例如:nl2br(),很轻松的处理完换行的问题。

4、公告栏(新闻发布)

把留言板稍微变化一下,可以做到另一种效果:公告栏,也有人叫它新闻系统。一进去会看到很多新闻标题(一两句话),点进去看全文。

延续上面留言板的结构,只要在Select上下功夫就可以了。

第一步:Show标题的页面

<?php
   $str = "select serial,subject from board where id=3 order by posttime desc limit 10";
   ...
   echo "<a href="show.phtml?$serial">$subj</a>;

?>

这样会显示出标题,点一下会Link到show.phtml:

<?
$str = "select .. from board where id=3 and seria = $serial";
...
?>

第二步:做一个类似留言板的Web接口,也许用一下密码保护,让客户自己去找开他们的新闻,直接插入到数据库,这样下一个浏览器的Request,显示的新闻标题立刻更新了。(这样管理许多网站的WebMaster一定会很高兴!不用再因为客户的一通电话,就去修改一堆HTML代码了)

升级1.0:加上一些管理接口,开放让客户自己去修改/删除贴过的东西,也不是什么难事。把数据读进来,放进Form的Value值当作默认值,修改完的数据用Update送回数据库即可。

升级2.0    像是SeedNet或是一些证券公司的网站,常常有一些新闻或消息,不但显示出标题,还显示出大约100字的内容,然后一个More Information...链接到全文。基本上跟上面的结构没什么两样,只不过在要Show Subject时,同时显示出Content前面固定长度的字符串而已。

//显示出Content前面固定长度的字符串
<?
$str = "select serial,subject,left(content,100) from boar dwhere id=3 order by posttime desc limit 10";
...
echo "<a href="show.phtml?$serial">More...</a>"
?>

5、讨论区

讨论区一般常见的有两种:

第一种像是Matt`s Script(http://www.worldwidemart.com/scripts/)使用的是全部展开标题的树状结构,一篇Follow的文章紧跟在原来的下面,优点是层次分明,一目了然,而且可以有好几层。

第二种原则上只有两层,首先像公告栏似的列出一堆标题,然后点进去就是所有的关于这个主题的文章。优点是速度快,管理与程序结构比较单纯。

这里重点以第二种快速的讨论区说明,然后等到抓到精髓之后,应该可以设计出更个性化、方便灵活的讨论区。第一种树状结构在数据库的角度没什么太大不同,不过显示的接口比较花功夫,可以自行试试看。

第一步:规划一下Table的结构。每篇文章(每笔数据)其实内容跟“留言”或“新闻”差不多,有主题、有内容、有发布者的姓名等。

  • 关键点在“Group”跟“Order”,就是说这些一篇一篇的文章,哪些是同一个主题下的讨论,其中先后次序又应该如何。
  • 另外,假设有20,000篇文章在数据库里,也许只分属2,000个主题,因此为了空间的考虑,应该把Subject取出来独立做一个Table。
create table subject(
    subject_id INT unsigned not null auto_increment,
    subject_name CHAR(20),
    primary key(subject_id)
);

creat table content(
    content_id INT unsigned not null auto_increment,
    subject_id INT unsigned not null,
    post_order INT unsigned,
    postname CHAR(10),
    postemail CHAR(30),
    ...
    primary key(content_id),
    index(subject_id)
);
  • 现在我们用了两个Tables,其中Content已经不存在Subject数据,直接用一个subject_id join到Subject Table。另外设计一个post_order,用来记录该文章到底在同一个主题的第几篇。
  • 用户张贴新的主题时,要做两个Insert操作,一个到Subject Table,另一个到Content Table,用户对某个主题Follow讨论时,则只有Insert到Content Tabel。如过要同一主题所有新的文章一律放在最后面,那post_order只要最大的加一就行。如果要插入在Follow的文章后面,还得Update同一主题的所有post_order值。

看显示的Query会更清楚一些:

$str = "select subject_id,subject_name from subject";//列出所有Subject
...
echo "<a href="show.phtml?$subject_id">$subject_name</a>;
$str = "select post_name,...from content
            where subject_id = $subject_id
            order by post_order";
...

上面的,只是原理而已,真的要运行,站在用户接口的角度,做到这样还太简单。可以多发挥想象力,例如结合后面的会员程序等,创造一个真正方便又快速的讨论区程序出来。

6、全文检索

坑:简单的想法,把所有的字放在数据库中,一搜寻就好了,问题是,第几页?第几行?怎么记录呢?总不能回答“找到”就可以把!

思路:把一整页当成一笔数据总行吧!这样可以知道找到的字符串是在第几页了。

还有问题:刚好在分页的地方的字符串怎么办?

思路:要设计全文检索,(认为)应该考虑的是,要做到什么程度。比方论文检索,也许找到哪一篇论文中有就可以了,而一本书的检索,至少应该到某一章节。

  • 两种方式:文件形式,数据库形式。
  • 可以自定义一种规则,读到某个特殊字符/字符串代表换行/换页等,然后把文件一行一行读进来。

    编号id

    章chapter 节section 段parag 内容content
    1 1 1 1 这是第一段,这是第一段,这是第一段
    1 1 1 2 这是第二段,这是第二段,这是第二段

参考:这样只要一个Select...Where ContentLike ‘%$key%’就可以了

7、会员

做一个Table记录会员数据,不是什么难事。困难的地方是,有了会员数据之后,那些要会员才能存取的网页,要如何保护?

坑:第一个直觉的想法,利用Apache本身的提供的登入限制功能就好了。通过.htaccess的设置,把这些网页放在一个目录下面,就可以达到这样的效果。

  • 第一个困扰:未来有新的用户,或者要更改密码时,都必须执行Htpasswd,要做到Web接口管理的话,可以用PHP写一个exec()做到。
  • 第二个困扰:如果会员数量庞大,速度会慢下来,当然也可以用Apache提供的DBM authentication解决。
  1. 因为Apache Authentication通过Header传送,PHP一样可以传送Header,因此可以利用这点,向Browser要求一个Aiuthentication,传回来Username跟Password自己到MySQL去做Check,不通过Apache。(注意:这个方法不要跟Apache本身的.htaccess一起用)
  2. 如下代码,读入Username和Password,配合MySQL的Member,后面就没问题了、如果在每一个需要保护的PHTML都加上这一段,就可以达到预期的效果,当然跟Apache Authentication一样,最好Realm都是同一个。
<?
if(!$PHP_AUTH_USER):
    Header("WWW-authenticate:basic realm="会员登入"");
    Header("HTTP/1.0 401 Unauthorized");
   //用户按了cancel的话
   echo "很抱歉!您没有读取的权限!
"
   exit;
else:
   echo "嗨! $PHP_AUTH_USER.<p>";
   echo "您用的密码是 $PHP_AUTH_PW.<p>";
endif;
?>
  • 上述两种方式,基本对Browser端而言是一模一样,只是Server运行方式不同而已。换句话说,每一次Browser激活后,只要通过一次检查,对相同的Realm的Username/Password都会被保存下来,再次进去则不必输入用户名称与密码。这也是最大的缺点:没有Logout,除非关闭Browser。

正确的做法:一般网站多半采用另一种方式:Unique ID

原理是:不管同一个时刻有多少人进来,对每个Login指定一个暂时的、唯一的ID,存在Cookie或者用参数传递。这样只要把Cookie删除,或者把Server端相对的ID删掉,就算Logout了。

问题:这个ID要用什么比较好?又不会重复?

解决:PHP提供一个函数Uniqueid(String Prefix)以微秒为基准产生一个字符串,加上Prefix可以长到一百多个字符。更安全的作法,则是利用随机数以及编码

mt_srand((double)microtime()*1000000);
$id = des(uniqid(mt_rand(1,2000)));

这样会以1~2000的数字为Prefix,产生Unique ID,再经过编码加密,让Id成为一个长32个字符的“唯一”字符串。

当然,Member Table必须也要多一个字段了:

uid CHAR(32)

用这个Unique ID的方式,应该另外设计一个简单的Form,输入Username跟Password

<form action="login.phtml" method="POST">
    名称:<input type="Text" name="username"><br>
    密码:<input type="Text" name="userpass"><br>
    <input type="Submit" name="" value="进入">
</form>

进来之后,检查Username/Password跟在数据库里的是否一样,一样的话,产生Unique ID,存到Cookie中,也记到Member Table的Uid里。之后的Select命令,只要一个where uid=$id,就可以找出是哪一位Member。

把上述的操作,放在一个PHTML文件里,然后所有需要被保护的PTHML(没错,只有PHTML文件)最前面加上一个Enclude就能达到预期的效果。

8、个人专属网页

现在许多网站,尤其是证券的站点,都已经提供专为个人设计的画面,比如你只想看财经消息,不想看国际新闻,那么你Login之后的首页,就只显示财经消息,每个会员都可以自行设置一些选项。

用PHP怎么做到?把上述Member Tabe多加一些字段,用来存储属性,Login之后把这些属性一并抓出来,像这样:

<?
if($show_news&1):
    //001   代表显示财经信息
?>

财经信息:...

<?
endif;
if($show_news&2):
    //010   代表显示国际消息
?>

国际消息:...

<?
endif;
?>

9、购物车

沿用Member的概念,当第一件产品加入购物车的时候,给它一个Unique ID,并且记录下来,结算的时候再把同样的Unique ID的所有产品列出来即可。

第一步:至少需要两个像这样的Tables

create table product(
     product_id INT not null,
     product_name..
     ...
);

create table cart(
    cart_id INT not null,
    uid   CHAR(32),
    product_id INT,
    primary key(cart_id),
    index(uid)
);

记录的数据会如下表所示:

cart_id uid product_id
1 ijghgdfafgsd 158
2 ijghgdfafgsd 134
3 ijghgdfafgsd 241
4 ygbfdsjdghgf 63
5 ygbfdsjdghgf 123

 第二步:在PHP中一次找出来该Unique ID对应的,被选择的产品数据。当然时常要用到的“清除购物车”或是“修改”甚至“数量”,对应到Cart Table就可以了。

$str = "select product.product_name,...
            from product,cart
            where product.product_id=cart.product_id
            and cart.uid='$id'";

购物车的另一种写法:利用联机进来的IP、Domain以及Browser信息($REMOTE_ADDR、$REMOTE_HOST、$HTTP_USER_AGENT),来确认“一辆”购物车,而不用Unique ID。

这样做的好处:不用通过Cookie或者参数的传递,对于不支持或拒绝接受Cookie的用户,也不会发生兼容的问题。 



注:转载请注明出处 

原文地址:https://www.cnblogs.com/ljq66/p/7745708.html