- 浏览: 200096 次
- 性别:
- 来自: 北京
文章分类
最新评论
-
输入法:
学习了
在PHP里,如何求得当前日期的前一天 -
l16426434:
答案我是这么想的
A说不知道 可以肯定这个数字出现2次以上
B ...
逻辑推理 -
l16426434:
第一个是方块8吗?
第二题我做过的
逻辑推理 -
888wu:
你好,这个好象不全,我想看一下源码,不知道你有没有,麻烦给我传 ...
自己编写基于MVC的轻量级PHP框架 -
lel08090:
<?php$name="w3pop.com&q ...
php之heredoc语法
PHP框架设计入门之四:表单和事件
PHP框架设计入门之四:表单和事件
PHP, framework
This is part 4 of a multi-part series on the design of a complete application framework written in PHP. In part 1, we covered the basic class structure of the framework and laid out the scope of the project. The second part described methods for managing users and session data. The third part described a practical implementation of page templates. In this fourth and final section, we will expand on our implementation of page templates to provide the web application persistent form data capabilities. We will also apply the framework to built an error page and a page dispatcher.
这是PHP框架设计入门系列教程的第四部分。在第一部分,我们已经介绍框架的基础类结构,并展示了项目的大体。第二部分叙述了管理用户和会话数据的一些方法。第三部分谈论了页面模板的具体实现。在第四部分这个最后的章节中,我们将详细地讨论如何通过页面模板来为我们的Web应用程序提供持久型的数据。同时,我们会用我们的框架来创建一个出错页面和一个页面分发器。
Forms
表单
If you've read Part 3 and felt a little gypped, you are right. The fact of the matter is that our use of templates as described in that article is not quite practical. One template for the entire application. Really? Does that mean each page has to have exactly the same layout? Of course not. The part that was not entirely stated was that the page templates described the general principle for separating the presentation layer out from the application logic. Now that this is established, we can look into applying these templates more practically. For starters, it is reasonable to have a global template as long as there is some provision for customizing each page. Ergo forms.
如果你已经读完第三部分,并且有点上当受骗的感觉,那就对了。(你有这样的感觉)主要的原因是,我们使用的那篇文章中所描述的模板,并没有多少可以动手实践的地方。整个应用程序中就只用一个模板。真的吗?那是不是意味着每个页面都有一个完全相同的外观?当然不是。这不能一概而论,页面模板只不过是描述了将表现层从应用逻辑层分离的一般方法。现在一切就绪,我们可以动手将模板付诸实践了。只要我们为定制页面的可能做好了准备,那么在开始的时候,有一个全局模板是合乎情理的。Ergo forms
Forms are actually nothing more than a template within a template. In fact the class_form.php class actually inherits from the class_template.php class. In the global template we put a place-holder for the form, <?=$form;?>, and then in the code set the $form variable of the page template to the result of fetching another template for the form. The form template itself does not necessarily have to have an HTML form element; but, since web application frequently deal with dynamic content, you will usually want to place the form tag into the the
template anyway. Now that we have solved that problem lets discuss the real power of forms: data.
表单其实只不过是模板中的模板。实际上,class_form.php类继承自class_template.php类。在全局模板中,我们放置一个表单的占位符:<?=$form?>,然后,在代码中将$form这个页面模板变量的值设置为指定的表单模板的内容。表单模板自身并非一定要有一个HTML的表单元素。然而,因为web应用程序总是要处理动态内容,所以,无论如何,你都会经常将表单标签放入到模板中。现在我们已经解决了那个问题,接下来我们来讨论一下表单真正的灵魂所在——数据。
Persistent Data
持久型数据
One of the difficulties in writing web applications is maintaining persistent data across multiple submits of the same page. This is not a problem for thick client applications because all of the data is stored in the client's memory. Since HTTP is a stateless protocol, however, we have to collect all the variables that were submitted using the POST or GET methods and send them back to the client with every single post back. Traditional web sites don't have to do this because every page is designed to do only one task so it gets submitted only once. The difference is that we are writing a web applications where each page is a modular entity. If we were to write a separate page for every button that's contained on the page then we would have an order of magnitude more pages to maintain. Furthermore, the very concept of a button (in a traditional thick client sense) doesn't exist in HTML. Yes you have something that looks like a button control but all it does is submit a page. Its up to us to connect the server side logic and have it execute when that button is clicked.
编写web应用程序一个困难的地方,就是在同个页面的多次提交中维护持久数据。这对富客户端应用程序(译者注:如桌面应用程序即可视为富客户端应用程序)来说并不是什么问题,因为所有的数据都存储在客户端的内存中。因为HTTP是一个无状态的协议,所以,我们不得不使用POST或者GET方法来收集所有提交过来的变量,然后在每一次回发(post back)中,再将其发回给客户端。传统的web站点并不需要做这件事,因为每个页面都被设计为仅处理单一的任务,所以,只需要提交一次就够了。而我们现在编写的这个web应用程序与之不同,每个页面都是一个模块实体。如果我们为每个按钮编写一个单独的页面,那么我们可能就会有一大堆的页面要维护了。此外,传统意义上的(富客户端中的)按钮概念,在HTML中并不存在。是的,你要说确实有一些看似按钮的控件,但它所能做的,仅仅是提交页面。按钮被点击的时候,连接服务端逻辑,并执行相应的操作,这些事情都取决于我们。
Another point about data is that not all of it gets submitted during a HTTP connection. What about the data that is generated at the server and needs to be maintained inside each page. For example, lets say you are displaying a table of data and want to remember which column was used to sort the data. You can store this in a hidden input control inside the form; but, if your page has many of these kinds of properties, then you may not want the overhead and burden of maintaining all of them this way. What other choice do you have?
另外,在一个HTTP连接中,并非所有的数据都是来自于页面提交。比如服务器产生的需要在每个页面中维护的数据。举个例子,你正在展示填满数据的表格,并且想要记住哪一列是被用来排序数据的。你可以将这个信息存储在表单里头一个隐藏的输入控件中,然而,如果你的页面有许多的属性,用这样的方法去维护它们,可能会使你耗费精力,负担过重。有没有其他的方法呢?
If you look at the template class carefully, you will notice that everything that we are considering data is just a value that needs to be embedded somewhere within our page template. If we store this data across post backs, then we don't have to worry about maintaining the other data that gets submitted with each page. This is precisely the what the form class does. We save all the variables just before we output the form to the client and load them back as soon as the form class gets instantiated. All the form variables are stored in a hidden input control named __FORMTATE. If you are familiar with ASP.NET way of doing things then you will realize that this a very similar approach. Note that when using the framework you do not have to worry about how this happens because the form class updates the information accordingly and even updates the values that were submitted during the last post-back. The one cumbersome issue with this approach is that any form property that corresponds to an actual control on the form (i.e. an item that sends a name/value pair with the page request) needs to be indicated to the form class so that the form properties can be automatically updated with the latest value send back. This is why we need to pass a $form_controls array when instantiating the form class. The code needed to maintain persistent form data is reproduced below.
如果你细心地看一看template class(译注:在class_template.php中定义了这个类),你会注意到,我们只是将数据当成一个值,这个值会被插入到我们页面模板的某个地方。如果我们在回发的过程中,一直保存这个数据,我们就不必为维护其他提交过来的数据操心。这正是form class所要做的(译注:原文链接有误,应该是form class)。在我们将表单输出给客户端之前,我们保存了所有的变量,在表单类实例化之后,我们立即将这些变量加载回来了。如果你熟悉ASP.NET的话,我们的这种处理方法与它是非常相似的。需要注意的是,当你使用这个框架的时候,你不必操心这一切是如何发生的,因为form class(译注:原文链接有误,应该是form class)会相应地更新信息,甚至会在最后一次回发时,更新那些提交过来的值。但是,这种方法有一个令人讨厌的缺点,和表单中实际的控件相关联的表单属性(比如:页面请求时发送一个键/值对的数据项),都必须在表单类中指明,以便表单的属性能够自动被更新为最新回发过来的值。这就是为什么在实例化form类的时候,我们需要传递一个$form_controls数组。维护持久型数据的代码如下:
function _saveState() {
$this->set("__IsPostback",true);
$state = chunk_split(encrypt(serialize($this->state_vars),session_id()));
$_POST["__FORMSTATE"] = $state;
}
function _restoreState() {
if(isset($_POST['__FORMSTATE'])) {
$state = decrypt($_POST['__FORMSTATE'],session_id());
$this->vars = unserialize($state);
$this->state_vars = $this->vars;
}
}
function _updateState() {
if (is_array($this->form_controls)) {
foreach ($this->form_controls as $state_var => $control_name) {
$this->set($state_var, $_POST[$control_name]);
}
}
}
Encryption
加密
HTTP is inherently a very insecure protocol. It was designed, as were many Internet protocols in the 1980's, with a security model based on user trust. Decades later, this model fell apart as the popularity of the Internet grew (and the users became less trustworthy). Although, we can still send data in clear text when implementing public forums, banking and e-commerce applications require a great deal more security. Why do I bring this up now? For starters, because SSL does not solve all of our problems. Yes we can encrypt the communication channel between the server and client with confidence but what happens to the data once it reaches the client. Once a client opens a web site, the HTML code used to generate the page is always available in clear text even if the communication channel was encrypted. That means that any data that we send to the client can remain in their browser cache or in their cookies indefinitely (and its all in clear text)[url=#notes]1[/url]. All of this makes it easier for mal-ware and spy-ware programs to gather data about its victims. Even worse is the situation where you don't want the user to see what properties are stored in the form.
HTTP天生就是一种很不安全的协议。和20世纪80年代被设计出来的众多internet协议一样,它将安全模型架构在对用户的信任之上。数十年后,随着Internet的增长扩大(同时,用户变得更加不可信),这样的模型便面临着崩溃的危险。虽然,对于公共论坛(这样的web程序),我们仍然可以发送明文数据。但是,对于银行、电子商务这样的应用程序来说,就需要更高的安全性。为什么现在我要谈论这些呢?首要的原因是,SSL并不能解决我们所有的问题。确实,我们完全可以对服务端和客户端之间的通信进行加密,但那些已经发送到客户端的数据呢?尽管我们加密了服务端和客户端之间的通信,但当一位用户打开一个web站点,HTML代码生成的页面总是以明文方式呈现。那就意味着,我们发送到客户端的任何数据都有可能被保存在客户端浏览器的缓存中,或者可能保存在cookie中(都是以明文的方式保存)。这会给那些恶意软件或间谍软件收集用户(受害者)的数据提供便利。当你不想让用户看到表单中存储了哪些属性值的时候,情况会更糟。
Now that I have hopefully convinced you that encryption of our form data is necessary, lets look in to how we can implement it. You will notice that the _saveState() and _restoreState() methods reproduced above call the encrypt() and decrypt() functions respectively. These functions are implemented in the functions.php file and require the mcrypt library for PHP to be installed on the server. Lets look at how these functions work.
现在,我希望你深信,加密表单数据是十分必要的,下面就让我们来看一下,如何来实现加密。你会注意到复制在上面的_saveState()和_restoreState()方法,都分别调用了encrypt()和decrypt()函数。这些函数在functions.php中编写好了,函数使用了安装在服务器上的PHP的mcrypt库。下面,让我们来看一看这些函数是如何运作的:
//encrypt plane text using a key and an mcrypt algorithm
function encrypt($pt, $key, $cipher='MCRYPT_BLOWFISH') {
$td = mcrypt_module_open(constant($cipher), "", MCRYPT_MODE_OFB, "");
$key = substr($key, 0, mcrypt_enc_get_key_size($td)); //truncate key to length
$iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND); //create iv
mcrypt_generic_init($td, $key, $iv); //init encryption
$blob = mcrypt_generic($td, md5($pt).$pt); //encrypt
mcrypt_generic_end($td); //end encryption
return base64_encode($iv.$blob); //return as text
}
//decrypt ciphered text using a key and an mcrypt algorithm
function decrypt($blob, $key, $cipher='MCRYPT_BLOWFISH') {
$blob= base64_decode($blob); //convert to binary
$td = mcrypt_module_open(constant($cipher), "", MCRYPT_MODE_OFB, "");
$key = substr($key, 0, mcrypt_enc_get_key_size($td)); //truncate key to size
$iv = substr($blob, 0, mcrypt_enc_get_iv_size($td)); //extract the IV
$ct = substr($blob, mcrypt_enc_get_iv_size($td)); //extract cipher text
if (strlen($iv) < mcrypt_enc_get_iv_size($td)) //test for error
return FALSE;
mcrypt_generic_init($td, $key, $iv); //init decryption
$pt = mdecrypt_generic($td, $ct); //decrypt
mcrypt_generic_end($td); //end decryption
$check=substr($pt,0,32); //extract md5 hash
$pt =substr($pt,32); //extract text
if ($check != md5($pt)) //verify against hash
return FALSE;
else
return $pt;
}
复制代码
By default, we will encrypt the form data using the Blowfish algorithm with the client's unique (pseudorandom/dynamically generated) session ID as the key. The Blowfish algorithm was used because benchmarking showed that it ran in a reasonable time for encrypting both small and large amounts of data. Also, the cypher text produced is proportional in size to the plane text. These are all good qualities to have when we are planing to encrypt arbitrary amounts of data.
默认情况下,我们使用Blowfish算法,并用客户端唯一的(伪随机/动态产生的)会话ID作为加密密钥,来加密表单数据。之所以使用Blowfish算法,是因为基准测试(benchmarking)显示,在加密少量和大量数据时,这个算法所耗费的时间都比较合理。对于加密不确定数量的数据,这个算法给我们提供了良好的保证。
I will not get into the details of using the mcrypt library in this article (see the PHP manual for details) but will describe the general algorithm used for encryption. After the library is initialized using our key, we take the plane text and append the MD5 hash of the text prior to encryption. When we are decrypting the cypher text, we will take the decrypted text, separate out the MD5 hash that was appended (this is always 32 bytes in length), and compare that MD5 hash to the MD5 hash of the text just decrypted. This way even if someone manages to modify the the cypher text in a meaningful way, we will be able to see that it was changed. Also, since the MD5 hash contains 32 bytes of random data, adding it to the plain text will make cracking the code without the key very difficult. This approach will work well as long as the the hacker does not have access to the session ID of the client (which is not always a good assumption… expecially when the Session ID is stored in a session cookie on the client).
我不会在这篇文章中过多地谈论使用mcrypt库的细节(详细的使用方法请参考PHP手册),但我会谈论用来加密的常规算法。在mcrypt库用我们的密钥初始化后,我们将原始文本的MD5散列值添加到原始文本的前面,来进行加密。在我们解密的时候,我们从加密了的文本中分离出添加了的MD5散列值(MD5散列值总是32位的)和原始文本,然后将这个分离出来的MD5散列值与原始文本的MD5散列值进行比较。这样的话,即使有人能够修改加密后的文本值,我们也可以察觉出改动。同时,因为MD5散列值是32位无规律的数据,将它添加到明文中,如果没有解密密钥,就很难进行破译。只要黑客没有获取客户端的会话ID(我们不应该总做这样的假设,特别是当会话ID保存在客户端的会话cookie中的时候),那么我们的加密机制就会正常运作。
While we are on this subject, why not compress the data before/after it is encrypted and reduce the amount of data that is transmitted between the server and client. Well, to be honest, I tried. It turned out that the time it took to compress/decompress this data was longer that any amount of time that we would have saved by reducing the transition delay. In fact, the format of the serialized or encrypted data that we would be compressing is such that compression is not very effective in reducing the size (i.e. there are not many repeatable elements in the text).
既然我们谈到这里,那就顺便来谈谈为什么不在加密前(后)压缩一下数据,以减少服务端和客户端的数据传送量。老实说,我试过了。结果表明,压缩解压缩数据所耗费的时间比我们减少数据量所节约的传送时间还要长。实际上,串行化(serialize)或加密了的数据,压缩并不能有效地减小数据的大小(比如:文本中没有太多重复东西的)。
Form Events
表单事件
So we have our new page, it looks pretty, and is somewhat secure. What do we do when the user actually clicks on a button. If you look back to Part 1 of the series you will remember that the system base class has an abstract function handle FormEvents() which gets called as the page is processed. You can overwrite this function in each page and handle the events accordingly. For example, lets say you have a button on your page called cmdSearch and you want to see if it was clicked. All you have to do is test for isset($_POST["cmdSearch"]) inside the handleFormEvents() function.
现在我们的新页面看起来很漂亮,并且具有一定的安全性。那么,当用户真的点击一个按钮的时候,我们应该做什么呢。如果你回头看一看本系列教程的第一部分,你会记得系统的基础类中有一个抽象函数handleFormEvents(),在处理页面的时候会被调用。你可以在每个页面中覆盖(重写)这个函数,以更好地处理事件。举个例子来说,在你的页面上有一个按钮叫做cmdSearch,你想知道它是否被点击了。你所要做的事情,就是通过handleFormEvents()函数中的isset($_POST["cmdSearch"])语句来进行检测。
This approach looks simple enough but it only solves half of our problem. Any HTML element can trigger an onClick JavaScript event on the client. What if we want that event to be handled on the server side. Since the object clicked does not necessarily have to be a button, you wouldn't always be able to find out if it was clicked using the approach described above. To solve this problem, our form template will need two more hidden input objects: __FORMACTION and __FORMPARAM. We can test for these values when the page is submitted and simply set the values in JavaScript whenever any event is raised that needs to be handled by the server. To set these objects in JavaScript and then submit the form we would do something like:
这个方法看起来很简单,但它只是解决了我们问题的一半。在客户端的任何的HTML元素都可以触发一个JavaScript的onClick事件。如果我们想在服务端处理那样的onClick事件,那又该怎么办呢。因为被点击的对象并不一定是一个按钮,所以,如果你使用上面所描述的方法,并不总能知道那个对象是否被点击了。要解决这个问题,我们的表单模板需要两个额外的隐藏输入控件:__FORMACTION和__FORMPARAM。我们可以在页面提交的时候检测这些值,一旦(客户端)有任何需要服务端处理的事件产生,我们可以用JavaScript简单地 设置这些值。要在JavaScript设置这些对象,并提交表单,我们可以用像下面这样的代码:
function mySubmit(action, param) {
document.forms[0].__FORMACTION.value = action;
document.forms[0].__FORMPARAM.value = param;
document.forms[0].submit();
}
复制代码
Note that since our model supports only one form element per page, we can simply refer to the first form in the forms array when accessing the objects. In practice, you wouldn't want to have multiple forms on a single page anyway.
注意,因为我们的模型仅支持一个页面有一个表单,所以,在访问对象的时候,我们可以简单地通过forms数组来引用第一个表单(译注:即document.forms[0])。总的来说,在实际的应用中,我们并不会想在一个单一的页面上放置多个表单。
Error Handling
错误处理
Customized error pages are necessary for any web application in order to improve the users experience whenever the inevitable errors occur. So, since we have to build the pages anyway, lets demonstrate how to apply this framework to writing a custom error page which lets the user save the error that occurred into our database (whether you actually address the problem is up to you). Lets start with a file for the form template which will contain our user interface.
对任何一个web应用程序来说,总是不可避免地会产生错误,因此我们需要有自定义错误页面这样的功能,以便(发生错误的时候)提升用户体验。虽然我们已经建立过若干个页面了,但还是演示一下,如何使用这个框架来编写一个自定义的错误页面,这个错误页面可以让用户在遇到错误的时候,将所产生的错误保存到我们的数据库中(当然,是否考虑这个问题完全取决于你)。我们先创建一个表单模板文件,在文件中编写我们的用户界面,代码如下:
<form action="<?=$_SERVER['PHP_SELF']?>" method="POST" name="<?=$formname?>">
<input type="hidden" name="__FORMSTATE" value="<?=$_POST['__FORMSTATE']?>">
<div align="center" style="width: 100%">
<div style="height: 280px;">
<img src='<?=IMAGE_PATH."error/error".$err.".gif"?>' style="padding-top: 20px">
</div>
<div style="height: 120px">
<?=$message?><br><br>
<? if (isset($_POST["log"])) : ?>
Click <a href="#" onclick="history.go(-2)">here</a> to go back.
<? else : ?>
If you think there is something wrong with the site, <br>
please submit the error for review so that it can be fixed. <br><br>
<input type="submit" name="log" value="Submit">
<? endif; ?>
</div>
</div>
</form>
复制代码
As you can see there is a little logic embedded in the template file but so be it. I didn't want to over complicate things by doing everything by the book (this is after all an example of how to mold the application framework to various needs). Now lets look at the main PHP file. The first thing that you will notice is that the NO_DB constant is defined because, in the usual case, we will not need a database connection. If the user wants to log the error, then we can create the database connection ourselves [url=#notes]2[/url]. Also notice that this page does not use the same generic template as the rest of the web site is using (the “error.inc.php” template is used to give the error page a special look). The rest of the page is fairly straight forward. We have to include the system base class in every page and then we derive our current page from the base class. In the init() method we indicate that we want to use a form and specify where the form template is located. Then, we check if this is the fist time that this page loaded (i.e. the user has not yet clicked any buttons on this page which would cause it to post back to itself). If so, we set the appropriate form properties. When the user clicks on the submit button, the handleFormEvents() function records the error in our database. Note that you will probably want to log a lot more information in your error pages.[url=#notes]3[/url].
正如你所看到的,我们在模板文件中放入了一些逻辑处理代码。我不想墨守成规,一切都按书本行事,那样会把问题复杂化(毕竟这只是一个例子,只是用来说明如何来架构一个框架,以满足各种各样的需求)。现在我们来看一看主要的PHP文件。首先,你会注意到,我们定义了NO_DB常量,通常情况下,我们不需要一个数据库连接。如果用户想记录下错误,我们可以自己创建一个数据库连接。同时,这个页面没有使用与web站点其余部分相同的通用模板(error.inc.php模板用来为错误页面展现一个特别的外观)。页面的其余部分十分明了。我们在每个页面中include系统基础类,然后让当前的页面类继承系统基础类。在init()方法中,我们说明了我们要使用一个表单,并且指定了表单模板的位置。然后,我们检测这个页面是不是首次加载(比如:用户还没有点击这个页面上的任何按钮,从而导致页面内容的回发)。如果是,我们就设置好适当的表单属性。当用户点击了提交按钮,handleFormEvents()函数就会将错误记录到我们的数据库中。当然,你可能想在错误页面类中记录更多的信息。
define("NO_DB", 2);
include "include/class_system.php";
class Page extends SystemBase {
function init() {
$this->form = new FormTemplate("error.frm.php", "frmError", true);
if ( !$this->form->IsPostback() ) {
switch($_GET['err']) {
//specify all the error which you want to handle here
case 404:
$title = _("File not Found");
$message = _("The URL that you requested, '") . $_SERVER[REDIRECT_URL] .
_("', could not be found.");
$this->form->set("err","404");
break;
case 500:
$title = _("Server Error");
$message = _("The requested for ") . $_SERVER[REDIRECT_URL] .
_(" caused a server configuration error.");
$this->form->set("err","500");
break;
default:
$title = _("Unknown error");
$message = _("An unknown error occured at ") . $_SERVER[REDIRECT_URL];
break;
}
$this->form->set("notes",$_SERVER[REDIRECT_ERROR_NOTES]);
$this->form->set("redirectURL",$_SERVER[REDIRECT_URL]);
$this->form->set("redirectQueryString",$_SERVER[REDIRECT_QUERY_STRING]);
$this->form->set("remoteIP",$_SERVER[REMOTE_ADDR]);
$this->form->set("userAgent",$_SERVER[HTTP_USER_AGENT]);
$this->form->set("referer",$_SERVER[HTTP_REFERER]);
$this->form->set("lang","en");
} else {
$title = _("Error Page");
$message = _("Thank you for reporting the error.");
}
$this->page->set("title",$title);
$this->form->set("message",$message);
}
function handleFormEvents() {
if (isset($_POST["log"])) {
$db = db_connect();
$Number = $db->quote($this->form->get("err"));
$Notes = $db->quote($this->form->get("notes"));
$RequestedURL = $db->quote($this->form->get("redirectURL"));
$Referer = $db->quote($this->form->get("referer"));
$sql = "INSERT INTO tblErrorLog(Number, Notes, RequestedURL, Referer)
values($Number, $Notes, $RequestedURL, $Referer)";
$result = $db->query($sql);
if (DB::isError($result))
die($result->getMessage());
$db->disconnect();
}
}
}
$p = new Page("error.inc.php");
The Page Dispatcher
页面分发器
Here is another example of how to apply this framework to a page that doesn't quite fit the mold. Look at the URL of the current page. Notice anything interesting. The entire site is written in PHP and yet you are looking at a .html file. I'll give you a hint, the HTML page is not actually there. In fact, the page that you looking at is called articles. The web server was configured to interpret the file as a PHP file even though it does not have a .php extention. Also, using the AcceptPathInfo On directive, you can configure Apache to backtrack along the requested URL until it find the desired file. So in this case, the HTML file is not found and the file articles is found so everything after the valid file name (i.e. /article_name.html) is passed into the articles PHP file as a query string. Why would we want such a convoluted system. Well, for starters, this hides the server's internal folder structure from the users. More importantly, however, it makes the URL more human readable than something like "articles.php?ID=12345") and increases the chances that search engines will cache the page. Many search engines don't cache dynamic pages and having a query string like "?ID=12345" is a dead give away.
这儿是另一个例子,它演示了如何将这个框架应用于一个 看一看当前页面的URL。注意任何有趣的东西。整个网站都是用PHP编写的,然而你却看到一个.html文件(译注:即整个URL看起来是指向一个html文件)。我给你一个提示:其实那里并没有一个真正的HTML页面。实际上,你看到的页面是articles。web服务器被配置成将文件解析为一个PHP文件,尽管(被解析的)文件并没有一个.php的扩展名。同时,你可以将AcceptPathInfo设成On,这样apache就会沿着URL(所指示的路径)一直往回查找,直到找到所需要的文件为止。这样一来,没有找到HTML文件,但找到了articles文件,在有效文件名之后的字符串(比如:/article_name.html)就会作为查询字符串(query string)传递给articles这个PHP文件。为什么我们要用这样一个拐弯抹角,令人费解的机制呢?首先,它对用户隐藏了服务器内部真正的文件夹结构。更重要的是,比起"articles.php?ID=12345"这样的形式,它使URL更具可读性,并增加了搜索引擎缓存页面的机率。许多搜索引擎并不缓存动态页面,像通过传递"?ID=12345"这样的查询字符串才能看到的页面,搜索引擎会完全对它置之不理。
How does one implement articles using this application framework? Because of the template engine, its actually quite simple. Take a look.
如何使用本应用程序框架来实现一个articles呢?通过我们已有的模板引擎,实现起来相当简单。下面是实现代码:
class Page extends SystemBase {
function init() {
//determine page from query string
$page = substr($_SERVER['PATH_INFO'],1,strpos($_SERVER['PATH_INFO'], '.')-1);
if (file_exists("templates/_$page.frm.php")) {
$this->form = new FormTemplate("_$page.frm.php", "frmArtile");
} else { //article not found
$this->redirect(BASE_PATH."error.php?err=404");
}
}
}
$p = new Page("view.inc.php");
Wrapping up
装配
By now, we have covered all the necessary aspects of designing web applications in PHP. Although the framework described here may not be exactly what you need, hopefully this series of articles provided a basic idea of the issues that need to be considered when writing your own web applications in PHP. Feel free to contact me if you have any questions or comments.
到现在为止,我们已经谈论了设计PHP Web应用程序所需要的方方面面。虽然这里描述的这个框架可能并不是你确切想要的,但我希望这个系列的教程,能够在你编写自己的PHP Web应用程序时,给你一个基本的参考,知道应该考虑哪些问题。如果您有任何问题或看法,欢迎随时跟我联系。
相关推荐
这份"Web开发入门经典——使用PHP6、Apache和MySQL"的教程资源将帮助初学者理解这三个工具的基本概念和实际应用。 PHP(Hypertext Preprocessor)是一种广泛使用的开源服务器端脚本语言,尤其适合于Web开发。PHP6,...
Yii框架是一款基于组件的高性能PHP框架,用于开发Web2.0应用。它的设计目标是高效、易用且可扩展。Yii这个名字来源于一个中文词汇“易”,意味着简单和便捷。本教程将带你逐步走进Yii的世界,了解其核心概念,以及...
为了提升开发效率和代码质量,许多开发者选择使用PHP框架,如Laravel、Symfony、CodeIgniter等。这些框架提供了丰富的功能和最佳实践,简化了常见的开发任务。 【PHP学习资源】 作为初学者,可以通过在线教程、...
通过这个CHM版的PHP入门教程,学习者不仅可以获得理论知识,还能通过实例练习巩固所学,从而逐步掌握PHP编程技能,为进一步深入学习PHP框架和Web开发打下坚实基础。记得在学习过程中,多动手实践,结合实际项目来...
8. **模板引擎和框架**:了解如何使用PHP模板引擎如Smarty,以及熟悉一些流行的PHP框架,如Laravel、Symfony或CodeIgniter,它们可以提高开发效率并提供更好的架构。 9. **PHP安全**:学习防止SQL注入、XSS攻击等...
- **属性、事件和行为**: - **组件属性**: 定义组件的状态。 - **组件事件**: 可以被监听的事件。 - **组件行为**: 组件的行为扩展。 **8. 模块** - **功能**: 模块提供了扩展应用功能的方式。 - **实现步骤**:...
4PHP是一套完全遵守APACHE开源协议的便捷PHP框架,独特的模块化设计,使得框架简洁易懂。一行代码即可完成在线支付、发送短信、分页等功能,前所未有的简单和高效!同时,84PHP还具有云服务功能,这也是国内首款与...
2. **框架应用**:探讨流行的PHP框架,如Laravel、Symfony、CodeIgniter等,了解其架构原理和使用方法,提升开发效率。 3. **Web服务API**:学习如何创建RESTful API,以及使用PHP调用外部API,进行数据交换。 4. ...
**PHP初学者入门教程** PHP(Hypertext Preprocessor,超文本预处理器)是一种广泛使用的开源脚本语言,尤其在Web开发领域应用广泛。作为初学者,了解并掌握PHP是迈入网站后台编程世界的关键步骤。这个教程将帮助你...
### Zend Framework 框架开发入门教程 #### 一、简介 本文档旨在提供一个针对初学者的 **Zend Framework** 开发指南。该框架是一种基于 PHP 的开源 Web 应用框架,提供了大量的组件来帮助开发者构建高效且健壮的 ...
- PHP框架:介绍一些流行的PHP框架,如Laravel、Symfony、CodeIgniter等,理解框架带来的便利。 - CMS系统:了解WordPress、Drupal等开源内容管理系统,学习如何利用它们快速构建网站。 8. 安全性: - 输入验证...
【PHP+MySQL网络开发从入门到精通源码】是一份全面深入学习PHP和MySQL数据库开发的资源集合。这个源码包旨在帮助初学者快速掌握这两种技术,并逐步提升为专业开发者。PHP是一种广泛使用的服务器端脚本语言,尤其适合...
13. **表单处理与验证**:指导如何设计合理的HTML表单,并利用PHP进行表单数据的验证和处理。 14. **安全性**:强调网络安全的重要性,分享防止SQL注入、XSS攻击等常见安全威胁的方法。 15. **模板引擎与MVC框架**:...
- 常用PHP框架:如Laravel、Symfony、Yii等框架的特点和使用方法。 - 实战项目:通过构建实际的Web应用,如CMS系统或电商网站,巩固和提升PHP编程技能。 这本《PHP经典从入门到精通》不仅覆盖了PHP的方方面面,...
- **框架支持**: Laravel、Symfony等现代PHP框架提供了更为高效、便捷的开发方式。 - **社区贡献**: 积极参与开源项目,贡献代码或文档,可以帮助PHP生态系统更好地发展。 通过以上知识点的梳理,我们可以看出PHP...
**PHP快速入门** ... ... ...Windows用户可以使用XAMPP或WAMP集成环境,Linux或Mac用户则需要手动配置Apache和PHP。...变量以$符号开头,如 `$name = "John";...同时,了解和使用PHP框架可以进一步提升开发效率和项目质量。
8. **PHP框架简介**:可能简述一些流行的PHP框架,如Laravel或CodeIgniter,以及它们如何简化开发过程。 通过这个教程,读者将能够结合Dreamweaver的可视化编辑工具和PHP的动态功能,创建出功能强大的动态网站。...
对于初学者和有经验的开发者来说,理解并掌握一个通用的PHP框架是提高开发效率和代码质量的关键。本资源提供了一个基于PHP 5.6.3版本的入门级框架,其设计目标是代码简洁易懂,适合新手学习和小型项目开发。 首先,...