第九章 - 网页间的信息传递
本章重点
为什么HTTP会生生不息地扩散?
GET参数
另一种使用GET风格式样的URL
处理窗体变量
PHP超数组
本章简单讲解一些有关在Web页面之间传递数据的内容。这样的一些信息不是PHP特有的,而是PHH/HTML或HTTP协议本身中很重要的部份。
HTTP是没有状态纪录的(stateless)
需要记住有关Web服务一项最重要的事情是,HTTP协议本身没有状态纪录的(stateless)。如果读者具有诗意的灵魂,可能就会说每个HTTP请求(request)孑然一身,没有家园,就像一个全然未知的……你知道这之类的说法。对缺乏诗意的我们,说白点就是指每个HTTP请求(每一次的请求和传送页面)独立于所有其它的内容、不知道客户端身份,而且也没有记忆。每个请求产生一件独立过程,完成一件档案服务、看似微小却重要的任务,然后自动消失(这样呼起来很无情,或许可以说成「回到可处理的状态中」)。
即使把网站设计成非常不严格的单向导航(页1引导到页2,页2引导到3等等),PTTP协助从来不知道也不关心某个人浏览的页2是否来自页1。因此,不可以把页1上的变量设定成透过HTML本身来导入该页。可以使用HTML显示窗体,用窗体输入一些信息,但是除非用一些别的方法把信息传送到另一页或另一个程序,否则一旦移到另一页,变量就消失了。
这是为什么导入像PHP这样的窗体处理技术的原因。PHP可以撷取从一页辗转传到另页的变量,能对它进行更进一步的动用。PHP函式正好非常擅长这种型态的数据传递函式,这样能更快、更容易地完成各种Web网站的任务。
HTML窗体是网站上用来由一个网页传递一些数据最有用的方法,有许多更持久的方式可以维护横跨许多网页的状态方法,例如cookie与session,我们会在第27章介绍这些功能。本章会着重在更基本的技术巧用来传递网页之间的信息,就是使用HTTP和GET与POST方法来进行动态产生网页以及处理数据。
ASP程序设计者看到这里可能要说「PHP真烂!」因为他们让为ASP的session变量是非常神奇的。这不是要截破谁的泡泡,而是微软正打算利用cookie来储存session变量,不过这样就打开了所有潜在问题的大门。
GET参数
GET方法把参数当成URI [Uniform Resource Indicator,一致资源指示器;也有人更习惯用URI (Uniform Resource Indicator,一致资源定位器)]查询字符串的一部份,从一个页面传递到另一个页面。当用于窗体处理时,GET用问号(?)当成分隔符号把变量名称和值附加给在ACTION属性中来反指定的URL,并把所有内容提交给提供处理的技术(在这个例子中是Web服务器)。
这是一个使用GET方法的HTML窗体范例(把档案存在team_select.html):
< HTML >
< HEAD >
< TITLE >A GET example, part 1< /TITLE >
< /HEAD >
< BODY >
< FOPM ACTION = http://localhost/baseball. php METHOD = “GET” >
< P >Root, root, root for the :< BR >
< SELECT NAME = “Team”SIZE=2 >
当使用者进行选择并按一下Submit按钮时,浏览器按照下面的顺序把这些元素接合、一起,中间不会有空格:
在单字ACTION后面,括在引号中的URL(http://localhost/baseball.php)
问号(?)指示以下字符即会组成GET字符串
NAME变量、等号、以及配合的VALUE(Team = Cubbies)
「&」符号和下一对「NAME = VALUE」(Submit = Submit);只要服务器查询字符串的长度限制允许,这些使用&区隔的name – value组合可以被重复许多次。
这样会构成这样一个URL字符串:
(http://locahost/baseball .php ? Team = Cubbies&Submit = Select)
其中字符串成为新的请求传送到浏览器的地址空间。上面的窗体提交后,处理窗体的PHP script(baseball.php)将从请求字符串的尾端取得GET变量,并对这些变量进行相对应的操作,在下面这个例子中,是把两个值中的一个插入文字字符串中。
以下程序代码是PHP用来处理原先HTML窗体的窗体处理部分:
< HTML >
< HEAD >
< TITLE >A GET example ,part 2< /TITLE >
< STYLE TYPE = “text/css”>
< !--
BODY {font-size: 24pt;}
-- >
< /STYLE >
< /HEAD >
< BODY >
< P >Go,
< ?php echo $_GET[‘Team’];? >
!
< /BODY >
< /HTML >
最后你应会看见网页上呈现大字样的Go,Cubbies。
窗体处理中的GET方法有一个比POST方法好很多的优点:它建立了一个真正新的、完全不同的URL查询字符串。如此一来使用者就可以把这一页记为书签(当开发小组意志消沈时,看到这个就能鼓舞士气了)。从使用POST方法的表单取得到的结果是不能记成书签的。
然而,你可以用GET参数完成想要的功能不代表你应该使用,对于大多数表单处理程序,GET方法的缺点实在是太严重了,以致于最初的HTML 4.0正式规格不赞成使用它。这些缺点包括:
GET不适合用于登录(login),因为把使用者名称和密码当成存取过的面潜藏储存有用户端浏览器的记忆体中时,在萤幕是也是完全可看见的。
每个GET提交都被记录在Web服务器log中,资料集也包括在内。
因为GET会分配资料到服务器环境变数,所以URL的长度有受到限制。你可以想象使用GET时非常长的URL是长什么样子,不过事实上谁也不想用这种方法尝试传递三百字的HTML格式的文章。
初使的HTML正式规格中对查询的字符串长度的限制是255个字符,虽然后来放宽了对255个字符的限制,但是使用很长的字符串真的是自寻烦恼。
在进行过很多争论后,W3仍然恢复使用表单处理中的GET方法,主要是由于书签功能的因素。虽然GET方法仍然是表单处理的预设选取项,但我们还是建议你只把它用于没什么副带作用于的地方。把两个优点和两个缺点放在一起考虑思量一番,使用GET处理表单的最适合的用途其实应该是「搜寻方块(search box)」。除非迫不得已的原因才把GET方法用在非搜寻性的表单处理程序,不然你就使用POST方法替代。
一种更好的GET风格URL用法
虽然对表单处理GET方法已经被建议不采用了。但是和它相关的URL风格对于网站的导航还是非常有用的,尤其适用于动态广告的网站,例如那些经常用PHP建构的网站,因为附加了变数格式的URL,就非常适合以样版当基础的内容发展系统。
有如下例所示,假设你所经营的是一个关于太阳能汽车资料丰富的Web网站,而你已将冗长且一致格式的资料丰富与诱有页面存放如下:
suspension_design.html
windtunnel_testing.html
friction_braking.html
但是当网站规模增大时,如此简单的档案网站结构就要耗费很多时间进行管理,因为一些琐碎的变动都必须在每个页面上重复进行。如果这些页面的结构非常简单,就可以用PHP把网站转换为以样版基底的系统。
你可能决定用一个单一的样版来将每一个主题的文字档案区分(包括资讯、照片、意见,等等):
topic.php
suspension_design .inc
windtunnel_testing . inc
friction_braking . inc
或是你可能决定一个更大、更为特殊选择处理的样版档案:
vehicle_structure . php
tubular_frames . inc
mechanical_systems . php
friction_braking . inc
electrical_systems . php
solar_array . inc
racing . php
race _strategy . inc
一个简单的样版可能有如此例所示(因为我们未含括所需的 .inc文字档案,这个档案将无法实际运作):
< HTML >
< HEAD >
< TITLE >Solar – car topics< /TITLE >
< STYLE TYPE= “text/css” >
< !-
BODY{font:verdana;font – size:12pt}
-- >
< /STYLE >
< /HEAD >
< BODY >
< TABLE BORDER = 0 CELLPADDING = 0 WIDTH = 100% >
< YR >
< !—Navbar,with Get-style URLs .-- >
< TD BGCOLOR = “#4282B4” ALIGN = CENTER VALIGN=TOP WIDTH=25% >
< P >
< A HREF=“mechanical_syatems .php? Name = friction_braking”>< B >Friction braking< /B >< /A >
< BR >
< A HREF = “mechanical_syatems.php?Name = steering” >< B >Steering< /B >< /A >
< BR >
< A HREF = “mechanical_systems .php ? Name = Suspenion” >< B >Suspenion< /B >
< /A >
< BR >
< A HREF = “mechanical_systems .php ? Name =tires” >< B >Tires and wheels< /B >
< /A >
< BR >
< /P >
< /TD >
< !—Main body of content - - >
< TD BGCOLOR = “#FFFFFF” ALIGN = LEFT VALIGN=TOP WIDTH = 75% >
< ?php include(“$_GET[‘Name’].inc”)? >
< /TD> < /TE >< /TABLE >
< /BODY >
< HTML >
请注意,当被按一下时导航列上的连接会被浏览器处理,就像是提交一个GET处理一样。
但是对于这个处理方案,仍然必须手动更改一部份程序码:以确保每个被包含进去的档案都是正确的HTML格式,每次给网站增添新页面时让导航列加入新的连接,以及其它类似的内容。尽可能按照一般常规把表单和内容分开,你可以选择使用资料库。若使用资料库,URL就会类似如下:
(http://localhost/topic .php ?topicID = 2)
它将指向某个处理资料库呼叫的PHP样版(使用数字变数而不是使用单字可以更快地查询资料库)。当给资料库新增新的主题时,该系统会帮导航列加入连结,所以不用手动就可以产生所有的Web页面(这里的「所有」一词是有些夸大了,但是的确可以省去人员与时间的多余劳力)。
POST参数
POST是目前相对好的表单处理方法,尤其适合需要不是一次使用完的情况(指定长期配合处理的一些资料或作用),例如在为资料库添加资讯的处理。当表单资料被传送到处理程序(在这里指PHP)时,被包含在表单本体内。提交的资料不同时,在URL中看不出什么变化。
POST方法有以下这些优点:
◎ 它比GET更安全,因为在URL查询字符串、服务器log中,或在萤幕上(如果采用了预防措施,例如总是使用HTML的password输入格式来表达密码栏位)看不到使用者输入的资讯。
◎ 对能被传递的资料的数量限制更宽松了(可到二千个元组,而不只是二百多个字符)。
不过POST也有一些缺点:
◎ 其结果不能被标记为书签。
◎ 这种方法和某些防火墙设定不兼容,为了安全上的考虑,防火墙会去掉某些表单资料。
在本书中我们一致使用POST方法来处理表单,尤其在使用写入档案或INSERT的SQL语法将资料填入系统时。我们只在网站的浏览与搜寻方块才会使用GET的方法,换句话说,使用时机分别为将资料写到资料储存位置以及显示网页,本章其余表单皆会使用POST方法。
同时使用GET和POST方法
你知道吗?PHP允许在同一页上同时使用GET和POST变数,因此可以自在地编写动态表单!
但是这样立刻引发一个问题:如果在GET 和POST阵列中(故意或由于其它原因)使用同样的变数名称会怎么样呢?如果你将你的php .ini档案内的register_globals指令设定为on的话,PHP把ENVIRONMENT、GET、POST、COOLIE与SERVER 等变数存放在$GLOBAL阵列中,如果产生冲突的话,它会根据你所设定的顺序来重新调整变数内容来解决,藉由你在php .ini内的变数_order选项设定。较后者会取代前者,所以如果你使用预设的“EGPCS”值的话,POST会取代GET,COOKIE会取代 POST,你可以藉由在这个档案内适当调整字母的顺序来控制取代的顺序,或是甚至最好将register_globals关闭并使用PHP新的超全域阵列,我们会在以下部分介绍。
在PHP中的变数处理
PHP对于传递资料非常有效率是因为开发者决定使用一项很方便但(在理论上)有点复杂的设计。当使用GET或POST方法提交资料集时,PHP会自动担以看不见的方式为新页面上的变数地行指定。其它大多数的程序语言让程序设计者自己在页面上执行明确显生的指定处理;如果忘记指定或写错了,则资讯就不能传到处理代理程序中。相较之下,PHP更快、更简单,更能防呆。
但是由于这样的自动变数指定,你必须永远为每个INPUT控制项取一个好的NAME属性,其实NAME属性并不是HTML强制需要的,你的表单即使少了它仍可以运作正常,但是资料将会没有任何功用,因为这些HTML的NAME表单栏位属性将是表单处理程序的变数名称。
换句话说,以下的表单:
< FORM ACTION = “< ?php echo $_SERVER[‘PHP_SELF’]; ? >” METHOD =“POST” >
< INPUT TYPE=“text”NAME=“email” >
< INPUT TYPE=“submit”NAME=“Submit” VALUE=“Send” >
< /FORM >
email文字栏位将会使得当表格传送时PHP产生变数为$_OPET[ ‘email’](或是如果你使用旧式的阵列变数则为$ HTTP_POST_VARS[ ‘email’],甚或是你将register_globals启动则为$email),同样的,传送按钮会使得下一个网页产生$_POST [‘submit’]变数,你在HTML表单所使用的名称将会成为你PHP表单处理的变数表单栏位。
另外一个产和生HTML表单必须记住的就是如果你希望表单在填写之前显示初始字样的话,你必须设定VALUE属性,这特别有用在两种类型的表单上:用来将资料输入资料库的表单,以及用来传送超过一次的表单,其中后者常常出现在表单需要在出现填入错误时重新显示表单的情形,例如,一个用来登录的表单会直到使用才输入有效的email地址或是其它相关资料才能送出。
例如,以下的表单(用来当作退休金试算表)被设计为当使用者填入资料时可以传送多次,每当你传送表单,你前一次所填入的次料会自动填入,请注意以下程序范例表单栏位的VALUE属性。
< HTML >
< HEAD >
< TITLE >A POST example:retirement savings worksheet< /TIELE >
< STYLE TYPE = “text.css” >
< !- -
BODY {font-size:14pt}
.heading {font-size:18pt; color:red}
-->
< /STYLE >
< /HEAD >
< ?php
//This test,along with the Submit button value in the form below,
//will check to see if the form is being rendered for the first time
//(in which case it will display with only the default annual gain
//filled in )
If (!IsSet($_POST[′Submit‵])||$_POST[′Submit‵]!=′Calculate‵){
$_POST[‘CurrentAge’] = “”;
$_POST[‘RetieAge’]= “”;
$_POST[‘Contrib’]= “”;
$Total = 7;
}else{
$AnnGain = $_POST[‘AnnGain’];
$Years = $_POST[‘RetireAge’] - $_POST[‘CurrentAge’];
$YearCount = 0;
$Total = $_POST[‘Countrib’];
While($YearCount<= $Years){
$Total = round($Total *(1.0 + $AnnGain/100)+$_POST[‘Contrib’]);
$YearCount = $YearCount+1;
}
}
? >
< BODY >
< BIV ALIGN = CENTER ID = Divl class = heading >A retirement – savings calculator
< /DIV >
< P class = blurb >Fill in all the values(except “Nest Egg”)and see how much money you′ll have for your retirement under different scenarios.You can change the values and resubmit the form as many times as you like.You must fill in the two “Age”variables.The “Annual return” variable has a default inflation-adjusted value (7% = 8% growth minus 1% inflation)which you can change to reflect your greater optimism or pessimism.< /P >
< FORM ACTIIN = “” METHOD=“POST” >
< P >Your age now:< INPUT TYPE=“text” SIZE= 5 NAME=“CurrentAge”VALUE=“” >
< P >The age at which you plan to retire:< INPUT TYPE = “text”SIZE=6
NAME = “RetireAge”VALUE = “< ?php echo $_POST[‘RetireAge’];? >” >
< P >Annual contribution:〈 INPUT TYPE=“text” SIZE=15 NAME=“Contrib“ VALUE=“ < ?php echo $_POST[ˋContribˊ]; ? > “ >
< P > Annual return: < INPUT TYPE==“text”SIZE=15 NAME=”AnnGain ”VALUE= ”< ? php echo $annGain; ? > ” > %
< BR >< BR >
< P >< B >NEST EGG< /B >:< ?php ECHO $Total;? >
< P >< INPUT TYPE=“submit”NAME=“Submit”VALUE=“Calculate” >
< /FORM >
< /BODY >
< /HTML >
图9-1显示上述程式的结果.
图9-1:使用方法与VALUE属性的表单
强化表单与表单处理
如同你可以在上述程序所见,通常将HTML表单与表单处理程序放在同一个程序是简易的,这样的方式有许多优点,例如,你的登录网页将会在登录失败时显示错误讯息,如果你将表单处理程序分开的话,你可能还需额外藉由GET变数来重新转址,如果你强化表单运作的话,你可以更加简易的控制显示而不需使用如此的机制。
当你强化表单的时假,表单处理程序应该出现在表单显示之前,有人可能会让为因为先设计表单才处理程序所以应该将顺序颠倒,但是如果你按照这里的方式,你便会了解其间的逻辑,你必须使你能够先将变数名称取好并且在显示表单之前做选择,这对如果你在某些情形必须将使用者导向不同的网页,藉由使用header ()函式,更重要的,因为这个决定必须在任何HTML输出显示在浏览器之前就变成了。