378 发表于 2017-4-1 09:03:16

PHP框架设计入门(整理篇)

PHP框架设计入门
原文地址:http://bbs.phpchina.com/viewthread.php?tid=59397
PHP框架设计入门之一:布局篇
(本文译自 Leon Bukhman 的 PHP Application Framework Design ,10/13/05)
引用:
This article describes the design of a completeapplication framework written in PHP.The reader is assumed to have a working knowledge of PHP. This part of the series describes the scopeof the framework and goes over theinitial class hierarchy. The following parts cover everything from session handling to creating pagetemplates.
本文讲述了如何用PHP来做一个完整的应用框架设计。我们假定读者已有PHP的工作知识。系列的此部分讲述框架的应用和类的架构。接下来的部分将覆盖从会话处理到创造页面模板的内容。
引用:
Class Hierarchy

I like to start implementing large projects by drawingout the class diagrams and seeing how the components fit together. Luckily, as of version 4, PHP supports object oriented programming with all itsbells and whistles. Although this articleis written for PHP 4, you can accomplish all of the same results withPHP 5 (which is still somewhat bleeding edge at the time of writing this).Object oriented programming and design is beyond the scope of this article so Ihope you are at least somewhatfamiliar with it. The point is that OOP is the key to developing largeapplications and web-development is no exception.
类的架构

在贯彻大项目时,我喜欢先画出类图,并看看怎样把各部分组件拼合在一起。幸亏,PHP从版本4开始具备了相应的功能支持面向对象编程。尽管本文是用PHP4写的,但你同样能用PHP5来实现所有的功能(在本文写作时PHP5还正待发布)。面向对象编程和设计的内容超出了本文的范围,希望读者至少在一定程度上熟识其相关内容。因为OOP是开发大型应用和web应用开发的关键,这点是毫无例外的。
引用:
We will want to keep the framework minimal forperformance reasons yet scalable andflexible enough to handle any kind of project that you may encounter. Tostart of, lets agree that the logicfor each and every page of you application will be encapsulated in a class.This class will inherit from the system base class, which will handleeverything that you may want your application to do. The benefits of this approach are fairly obvious. First, youare encapsulating all of the complex code away from the project developers.Thus, you can rest assured that your new intern will not mess with your systemclasses (unless you want her to for some reason). All the pages in theapplication will be more consistent and you will not be copy-pasting the samecode into every page. More importantly, if you decide to change some underlyingfunctions of your application, you will not have to edit each page as long asyou keep the interface to your system base class consistent.
为了提高效能,我们将尽可能把框架的结构最小化,但同时又有足够的扩展性和灵活性,以确保框架能满足以后各种项目的不同需求。开始之前,我们先预设一个处理逻辑,应用中的每一个页面都将被封装在一个类里面。这个类又继承了系统的基类,它将处理(此页面)应用要做的一切事情。这样做的好处是很明显的:第一,对项目开发人员封装了所有复杂的代码,这样就不用担心新同伴搞乱了系统架构 (除非你有意这样做)。第二,应用中的所有页面都能保持一致性,你再也不用去拷贝-粘贴相同的代码到每个页面。第三,更重要的,如果要改变应用中的一些功能,只有保持对系统基类的接口的一致性,就不用去编辑每一页了。
引用:
Now lets define what we want our system base class todo. Here is a basic list of features that I will go over in due time:

Establish the database connection
Define how the page will be laid out and print theHTML
Manage authentication and user session information
Define all your application’score features

Here is the framework and all of it's glory:
现在确定一下基类要做什么。下面是准备讨论的一些基本特征:
建立数据库连接
确定页面布局和显示HTML
鉴证和用户会话信息管理
定义应用核心要素

下面是框架及其要素的实现:
复制内容到剪贴板
代码:
class_system.php- The base class which includes the following (基类包括以下部分)
|_constants.php- Application constants (应用常数)
|_functions.php- Generic functions (基本函数)
|_class_user.php- Session handling (会话处理)
|_class_template.php - Template handling (模板处理)
|_class_form.php- Form handling (表单处理)
Using this framework, all of your pages should havethis structure:
此框架内,所有页面都有这样的结构:
复制内容到剪贴板
代码:
include "include/class_system.php";
class Page extends SystemBase {
//your codehere
}
$p = new Page();
引用:
The idea here is that we want to minimize the timespent on the most common tasks such as creating new pages. In this case, all wehave to do is include the base class (which will make sure that all the othernecessary dependencies are also included), extend the base class, and simplymake an instance of the class to getthe ball rolling. This way you can start thinking about what the page willactually do rather than waste time with common tasks.
在这里,我们想要把像创造新页面这类最普通任务所花费的时间减到最小。在本例中,我们只需要包括基类,扩展基类,然后简单的把类实例化就可以玩下去了。这样大家就可以把精力放在研究页面具体要做什么上面,而不是把时间化在普通事务上。
Configuration Files and Useful Functions 文件配置和有用的函数
引用:
The simplest idea to explain is the need for a filethat will contain the application’sconfiguration. We can go with XML or some other even more intricate technologythat will inevitably make the framework less efficient but let’s start off with the basics. All we really need is a set of constants that define strings such as thedatabase connection or installationpaths. These are strings that we would not want to hunt down within each pageof the application every time the database password is changed or the application is moved. The fileconstants.php is just that. The settings that I find most useful are the URLand system paths to the application.
为简单解释起见,先建立一个包含应用配置的文件。我们可以用XML或其他一些复杂的技术来做,不过这样框架的效率就会降低了,所以我们从最基本的技术开始。我们现在需要的只是一些常数用来定义像数据库连接或安装路径之类的字符串。我们不想在每次改数据库密码或者改变应用路径时都要在每页里翻来倒去的寻找修改的地方。只要把 constants.php 这个文件找出来就行了。通常我认为最有用的设定是URL和应用的系统路径。
引用:
On a similar note, there are some functions that wewould like to always make available and I include them in the filefunctions.php. These functions are not made as methods of the base classbecause we would like to make them available to all the classes of ourapplication (including those that do not extend the base class). I will go over the functions in this filewhen we need to use them. As a simple example, I include the me() function, which returns the file name of the current PHPfile. (VB 6 programmers will remember that me is a reference to the currentform).
同样的,有一些我们随时需要用到的函数,我把它们放在functions.php文件里。我们没有把这些函数作为基类的方法,是因为应用里所有的类都可能用到它们 (包括那些没有扩展基类的)。在需要用到时,我再讲这个函数。现在作为一个简单的例子,先包括一个me()函数,它将返回现在PHP文件的文件名。
复制内容到剪贴板
代码:
function me() {
returnsubstr($_SERVER['PHP_SELF'], strrpos($_SERVER['PHP_SELF'],'/')+1,
strlen($_SERVER['PHP_SELF']));

} The BaseClass
引用:
Now lets dissect the class_system.php file line by line. The first thing that wedo is call session_start() whichstarts a new user session if none exists or does nothing otherwise (more onsessions in part 2 of this series). Then we include all the need libraries.There are two things to note about the way this is done. First, require_once is used which will onlyinclude the file once and throw an exception if the file is not available (asopposed to include which would only make a warning and proceed with executionof the page). The second thing to note is that absolute paths are used toinclude the files which are all in the same folder. This is done because we don’twant to have to reconfigure the server to get the application working (bychanging the include path used in PHP). Relative paths would not work becausethe base class will be included from various other pages and the relative pathwould have to work for each page that includes the base class.
现在我们一行一行地来讨论system.php这个类。第一行调用了session_start(),如果会话(session)不存在,它就打开一个新的会话(第二部分有更多内容)。然后把所有需要的库文件包括进来。这里要注意两件事情:第一,使用require_once 只在第一次把文件包括进来,如果没有此文件,就抛出个异常 (而用include则之显示一个警告信息)。第二,把同一个文件夹里的所有文件都包括进来的绝对路径。这样做是因为我们不想为了应用而再配置服务器 (作PHP里改变包括的路径)。用相对路径行不通,因为基类会在各种不同的页面里用到,要为用到基类的每一页设定相对路径是很困难的。
复制内容到剪贴板
代码:
session_start();
$path = dirname(__FILE__);
require_once "$path/constants.php"; //defines
require_once "$path/functions.php"; //generic functions
require_once "$path/class_user.php"; //session handling
require_once"$path/class_template.php";//template handling
require_once "$path/class_form.php"; //form handling

(译注:在PHP5我们可以用__autoload()的方法。)
The implementation of the system base class is not atall surprising if you consider the way we intended to use it. Since the code isonly executed when an instance of the class is created, we put everything thatwe want to happen into the class’s constructor. Also, since the baseclass will not be able to know exactly how to render each page, we make themethods abstract and let polymorphism take care of the rest (i.e. the derivedclasses will override the base classes methods.) Here is a list of the methodscalled from within the constructor:

如果考虑到我们使用它的方式,系统基类的实施就没什么令人惊奇的了。因为只有在类的实例创造时,代码才被执行,我们可以把所有要用到的东西都放到类的构造器里。还有,因为基类不能确切的知道如何呈现出具体的每一页,我们要用到抽象的方法,并使用多态性来照料这一切。(如子类重载基类的方法。) 下面是构造器里要调用的一些方法:
复制内容到剪贴板
代码:
init()- initialize the page
authenticate()- perform authentication
handleFormEvents()- handle page submits
printPage()- output the HTML
destroy()- destroy page (close DB connection)
Unfortunately, PHP 4 does not enforce many OOPprinciples (note that PHP 5 has anew and much more robust object model). Youcan get everything to work,such as inheritance and polymorphism, but it takessome effort and some faith. For example, there is no concept of methodprotection so all methods (and attributes for that matter) are made public.Thisis a big no-no when it comes to OOP so acommon convention is to prefix all methods that are intended to beprivate with anunderscore ( _ ) and then take it on faith that the users of theclass will not call these methods. Another problem is that we cannot declarethe class to be abstract (i.e. we do not want people to declare instances ofthe base class but rather force them to inherit from it). We can get aroundthis limitation by including the following lines at the top of our constructor(you can read this article for an in-depth analysis of how thisworks). The code checks to see if the instance of the class is the base classand throws anexception.
复制内容到剪贴板
代码:
if (!is_subclass_of($this,'SystemBase')) {
trigger_error('Base instantiation from nonsubclass',E_USER_ERROR);
return NULL;}Database Access
引用:
The application framework should provide a cleaninterface to your data regardless of the type of SQLserver uses. It is alsovery desirable to have loose coupling between your application and yourdatabase backend. So, for example, ifyou ever decide to change the location of the database or even change the typeof SQL server used, you don’t want to have to rewrite all yourcode. Luckily, someone has already tackled this problem for us and all we haveto do is use the PEAR::DB module, which provides a consistent interface toseveral popular database servers. You can read all about it at the PHPExtension and Application Repository and I recommend reading the followingPEAR::DB tutorial as well.
无论使用什么类型的SQL数据库,应用框架都应该对数据提供一个清晰的接口。在应用和数据库后台之间,最好能保持松散的偶合。比如说,如果用户打算要改变数据库的地理位置,或甚至于要改变SQL数据库的类型,你也用不着去改写代码。幸好,已经有人为我们处理好了这些问题了,我们要做的就是使用PEAR::DB模块,它对一些通用的数据库服务器都提供了统一的接口。你可以在”PHP扩展和应用分类“里阅读这部分,我建议大家也阅读下面的PEAR::DB辅导部分。
引用:
Assuming that the PEAR::DB module is installed andconfigured correctly, all we need to have a persistent database connectionavailable in every page is a referenceto a PEAR::DB object. We will create this object as a member variable in theSystem Base class and create the database connection inside the constructor. Wealready have the necessary constants to build a DNS string and there is also afunction in functions.php that returns a pointer to a new PEAR::DB object.
假如PEAR::DB模块已经正确地安装和配置好了,在每一页里需要用到一个持续的数据库连接时,我们要做的就是引用PEAR::DB对象。我们将在系统基类里创建此对象作为一个成员变量,并在构造器里建立数据库的连接。我们已经有了建立DNS字串所需要的常量,并且在 functions.php 里也有一个函数可以返回一个指向新PEAR::DB 对象的指针。
复制内容到剪贴板
代码:
function &db_connect() {
require_once 'DB.php';//pear db class
PEAR::setErrorHandling(PEAR_ERROR_DIE);

$db_host = DB_HOST;
$db_user = DB_USER;
$db_pass = DB_PASS;
$db_name = DB_NAME;
$dsn ="mysql://$db_user:$db_pass@$db_host/$db_name";
$db = DB::connect($dsn);
$db->setFetchMode(DB_FETCHMODE_ASSOC);
return $db;
}

The function imports the PEAR:DB library if it has notyet been imported, creates a DNS string,and connects to the database. All we have to do now is use this function in theSystem Base constructor as follows: $this->db = db_connect(); and we aredone.

此函数在没有引进PEAR:DB库时予以引进,创建一个DNS字串,并连接数据库。我们要做的就是在系统基类的构架器里像这样:$this->db = db_connect(); 来使用此函数,而我们已经这样做了。
Optimization

You may think it is a bit too early to discuss codeoptimization at this point since we have yet to implement most of the necessaryfeatures. Code optimization is more of an art and I will not go into aline-by-line analyses of how we can achieve the same effect faster. The pointhere is that we already can tell what the major bottlenecks will be so let’snip the problem now before its gets out of hand. The most obvious performanceloss will be due to the persistent databaseconnection that we establish for each page. In fact, if you make a fewtiming test to benchmark the performance of the framework you will see thatestablishing the connection alone take more time than everything else combined.Although it is a necessary evil and we will have to pay that price for it whenusing the database, not every page in our application will have to connect tothe database. In fact, not every page will need toprint out HTML content (i.e.it may just process the request and redirect). So before we go on lets define afew constants for the base class to indicate that we don’t need a database connection or to print the page. You will simplydefine these constants in pages thatneed them.

也许你会认为现在讨论代码优化还为时太早,因为很多需要的特征还没贯彻。代码优化是一种艺术,我也不会一行一行的去分析怎么取得优化。关键的一点是,现在我们已经可以说出主要的瓶颈是什么,我们如何在其失控之前,把问题处理好。最明显导致效能丧失的是每一页都要用到的数据库的持续连接。
复制内容到剪贴板
代码:
define('NO_DB', 1) if persistent DB connection
is not needed
define('NO_PRINT', 1) if page does not get rendered

Summary
So far, we have laid the foundation for how ourapplication will be haveand how the framework will be used. We established anOOP design for the application,defined some constants and function to expedite routine work, and addeddatabase support. Read the next part to see how to manage session data andusers.

PHP框架设计入门之二:管理用户
In part 1, we covered the basic class structure of theframework andlaid out the scope of the project. This part adds session handlingtoour application and illustrates ways of managing users.

这是PHP应用程序框架设计系列教程的第二部分。在第一部分,我们已经介绍框架的基础类结构,并展示了项目的大体。这一部分,我们将在程序中添加会话处理功能,并演示管理用户的各种方法。

Sessions
会话
引用:
HTTP is a stateless protocol and, as such, does notmaintain anyinformation about the connections made to the server. Thismeansthat, with HTTP alone, the webserver cannot know anything about theusers connected to your web applicationand will treats each pagerequest as a new connection. Apache/PHP gets aroundthis limitation byoffering support for sessions. Conceptually sessions are afairlysimple idea. The first time a webuser connects to the server, he isassigned a unique ID. The web server maintains sessioninformation ina file, which can be located using this ID. The user also hastomaintain this ID with each connection to the server. This is typicallydone bystoring the ID in a cookie, which is sent back to the server aspart of thetypical Request-Response1sequence. If the user does not allow cookies, thesession ID can alsobe sent to the server with each page request using the querystring(the part of the URL after the “?”). Because the web client isdisconnected, the web server will expiresessions after predefinedperiods of inactivity.
HTTP是一种无状态的协议,正因为如此,它没有包含任何与服务器连接的相关信息。这就意味着,HTTP是孤立的,web服务器并不知道用户与你web程序相连接的任何信息,并且服务器会将每个页面请求视为一个新的连接。Apache/PHP通过提供对会话的支持来避开这一限制。从概念上来说,会话是相当简单的。在一个用户第一次连接到服务器的时候,他被分配一个唯一的ID。web服务器在一个文件中维护会话信息(译注:即把会话信息存储到文件中),于是可以通过这个ID来定位用户信息。用户同样会在每次连接中维护这个ID。最典型的作法,就是将ID存储在cookie中,之后,这个ID会作为请求-应答序列的一部分发回给服务器。如果用户不允许使用cookie,会话ID同样可以在请求每个页面时,通过query字符串(即URL中?以后的部分)发回给服务器。因为web客户端会断开连接,所以web服务器会在一定周期后,使那些不活动的会话信息过期。
引用:
We willnot go over configuring Apache/PHP in this article butwill utilize sessions tomaintain user information in our application.It is assumed that session supportis already enabled and configured onyour server. We will pick up where we leftoff in part 1 of this serieswhen we described the system base class. You mayrecall that the firstline in class_system.phpis session_start(), which starts a new user session if noneexists ordoes nothing otherwise. Depending on how your server isconfigured,this will cause the session ID to be saved in the client’scookie fileor passed as part of the URL. The session ID is always available toyouby calling the build in function session_id(). With these tools athand, we can now build aweb application that can authenticate a userand maintain the user’s information as he is browsing the differentpages on the site.Without sessions, we would have to prompt the userfor their login credentialsevery single time they request page.
我们不想在这篇文章中过多地谈论Apache/PHP的配置,除了利用会话来维护用户信息。我们假设会话支持功能已经开启,并在你的服务器上配置好了。我们将直接从本序列教程第一部分谈论系统基础类时,被我们搁在一边的地方谈起。你可能还记得class_system.php的第一行是session_start(),这一句的作用是,如果不存在会话信息,则开始一个新的用户会话,否则不做其他的事情。根据你服务器的配置,开始会话的时候,会话ID会被保存在客户端的cookie里或者作为URL的一部分进行传递。当你调用内建的session_id()函数时,总可以得到会话ID。通过这些工具,我们现在可以建立一个web应用程序,它可以对用户进行验证,并且能够在用户浏览网站不同页面的时候去维护用户的信息。如果没有会话,那么用户每一次请求页面的时候,我们就不得不提醒用户进行登录。
引用:
So whatwill we want to store in the session? Let’s start withthe obvious like theuser’s name. If you take a look at class_user.phpyou will see the rest of thedata being stored. When this file is included, the first thing that is checkedis whether a user is loggedin (default session values are set if the users idis not set). Notethat the session_start() must have already been called beforewe start playing with the $_SESSION array which contains all our sessiondata.The UserID will be used to identify the user in our database (whichshouldalready be accessible after part one of this series). The Rolewill be used todetermine whether the user has sufficient privileges toaccess certain featuresof the application. The LoggedIn flag will beused to determine if the user hasalready been authenticated and thePersistent flag will be used to determinewhether the user wants toautomatically be logged in based on their cookiecontent.
那么,我们应该在会话中存储什么信息呢?我们一下子就可以想到如用户名这类信息。如果你看一下class_user.php,你会看到其他要存储的数据。(在程序中)include这个文件的时候,首先会检查用户是否登录(如果没有用户id,那么会设置一个默认的会话值)。注意,session_start()必须在我们使用$_SESSION数组之前调用,$_SESSION数组包含所有我们的会话数据。UserID用来标识存储在我们数据库中的用户(如果您已经完成了本系列教程的第一部分,那么这个数据库中的数据应该可以访问了)。Role(角色)是用来检测用户是否有足够的权限去访问程序中的某一部分功能。LoggedIn标识用来检测用户是否通过验证,Persistent标识用来检测用户是否想依靠他们的cookie内容自动进行登录。
复制PHP内容到剪贴板
PHP代码:
//session has not been established
if(!isset($_SESSION['UserID']) ) {
set_session_defaults();
}

//reset session values
functionset_session_defaults() {
$_SESSION['UserID'] = '0';//User ID in Database
$_SESSION['Login'] = '';//Login Name
$_SESSION['UserName'] = '';//User Name
$_SESSION['Role'] = '0';//Role

$_SESSION['LoggedIn'] = false;//is user logged in
$_SESSION['Persistent'] = false;//is persistent cookie set
}
User Data

用户数据

We store allthe user data in our database in table tblUsers. Thistable can be created using the followingSQL statement (mySQL only).

我们将所有的用户数据存储到数据库的tblUsers表,这个表可以使用下面的SQL语句来创建(仅限MySQL)
复制PHP内容到剪贴板
PHP代码:
CREATE TABLE `tblUsers` (
`UserID`int(10) unsigned NOT NULL auto_increment,
`Login`varchar(50) NOT NULL default '',
`Password`varchar(32) NOT NULL default '',
`Role` int(10)unsigned NOT NULL default '1',
`Email`varchar(100) NOT NULL default '',
`RegisterDate`date default '0000-00-00',
`LastLogon`date default '0000-00-00',
`SessionID`varchar(32) default '',
`SessionIP`varchar(15) default '',
`FirstName`varchar(50) default NULL,
`LastName`varchar(50) default NULL,
PRIMARYKEY (`UserID`),
UNIQUE KEY `Email` (`Email`),
UNIQUE KEY`Login` (`Login`)
) TYPE=MyISAM COMMENT='Registered Users';

引用:
This statement creates a bare-bones user table. Mostof the fields are selfexplanatory. We need the UserID field to uniquelyidentify each user.The Login field, which must also be unique, stores theuser's desiredlogin name. The Password field stores the MD5 hash of theuser'spassword. We are not storing the actual password for security andprivacyreasons. Instead we can compare the MD5 hash of the passwordentered with thevalue stored in this table to authenticate the user.The user's Role will beused to assign the user to a permission group.Finaly, we will use theLastLogon, SessionID, and SessionIP fields totrack the user's usage of oursystem including the last time the userlogged in, the last PHP session ID theuser had, and the IP address ofthe user's host. These fields are updated eachtime the usersuccessfully logs in using the _updateRecord() function in theusersystem class. These fields are also used for security inpreventingcross-site scripting attacks.
这个语句建立了一个大概的用户表。大多数字段不言自明。我们用UserID这个字段来唯一标识每个用户。Login字段同样也必须是唯一的,存储用户使用的登录名。Password字段用来存储用户密码的MD5散列值。我们没有存储实际的密码是因为安全和隐私的原因。我们可以拿用户输入的密码的MD5散列值与数据表中的进行对比来验证用户。用户角色用来将用户分配到一个许可组。最后,我们用LastLogon, SessionID和SessionIP字段来跟踪用户对系统的使用情况,包括用户最后登录时间,用户最后使用的会话ID,用户机器的IP地址。用户每次成功登录后,会调用user系统类中的_updateRecord()函数来更新这些字段值。这些字段同时也可以用来保证安全性,保证不受XSS(跨站脚本)攻击。
复制PHP内容到剪贴板
PHP代码:
//Update session data on the server
function _updateRecord () {
$session =$this->db->quote(session_id());
$ip = $this->db->quote($_SERVER['REMOTE_ADDR']);

$sql ="UPDATE tblUsers SET
LastLogon = CURRENT_DATE,
SessionID = $session,
SessionIP = $ip
WHERE UserID = $this->id";
$this->db->query($sql);
}
Security Issues
Authentication

验证

Now that we understand the various security issuesinvolved, lets lookat the code for authenticating a user. The login() functionaccepts alogin name and password and returns a Boolean reply toindicatesuccess. As stated above, we must assume that the values passed intothefunction came from an untrusted source and use the quote() functionto avoidproblems. The complete code is provided below.

现在我们已经了解了各种相关的安全问题,下面我们来看一看验证用户的代码。login()函数接收一个登录名和密码,返回一个Boolean(布尔值)来标明是否正确。正如上面所说的,我们必须假定传入函数中的值是来自于不可靠的来源,用quote()函数来避免问题。完整的代码如下:
复制PHP内容到剪贴板
PHP代码:
//Login a user with name and pw.
//Returns Boolean
function login($username, $password) {
$md5pw = md5($password);
$username =$this->db->quote($username);
$password =$this->db->quote($password);
$sql ="SELECT * FROM tblUsers WHERE
Login = $username AND
Password = md5($password)";

$result =$this->db->getRow($sql);

//check ifpw is correct again (prevent sql injection)
if ($resultand $result['Password'] == $md5pw) {
$this->_setSession($result);
$this->_updateRecord();//update session info in db
returntrue;
} else {
set_session_defaults();
returnfalse;
}
}

To logout, we have to clear the session variables onthe server as well asthe session cookies on the client. We also have to closethe session.The code below does just that.

用户注销的时候,我们要清理在服务器上的会话变量,还有在客户端的会话cookie。我们还要关闭会话。代码如下:
复制PHP内容到剪贴板
PHP代码:
//Logout the current user (reset session)
function logout() {
$_SESSION =array(); //clear session
unset($_COOKIE);//clear cookie
session_destroy();//kill the session
}

Inevery page that requires authentication, we cansimply check thesession to see if they user is logged in or we can check theuser'srole to see if the user has sufficient privileges. The role is definedasa number with the larger numbers indicating more rights. The codebelow checksto see if the users has enough rights using the role.

在每一个页面都要求验证,我们可以简单地检查一下会话,看用户是否已经登录,或者我们可以检查用户角色,看用户是否有足够的权利。角色被定义为一个数字(译者注:即用数字来表明角色),更大的数字意味着更多的权利,下面的代码使用角色来检查用户是否有足够的权利。
复制PHP内容到剪贴板
PHP代码:
//check if user has enough permissions
//$role is the minimum level required for entry
//Returns Boolean
function checkPerm($role) {
if($_SESSION['LoggedIn']) {
if($_SESSION['Role']>=$role) {
return true;
} else {
return false;
}
} else {
returnfalse;
}
}
Login/Logout Interface

登录/注销的接口
引用:
Now that we have a framework for handling sessions anduser accounts,we need an interface to allow the user to login and out. Usingourframework, creating this interface should be fairly easy. Let us startwiththe simpler logout.php page which will be used to log a user out.This page hasno content to display to the user and simply redirectsthe user to the indexpage after having logged him out.
现在我们已经有一个处理会话和用户账号的框架了,我们需要一个接口,这个接口允许用户登录和注销。使用我们的框架,建立这样的一个接口应该是十分简单的。下面我们就从比较简单的logout.php页面开始,这个页面用来注销用户。这个页面没有任何内容展现给用户,只是在注销用户以后,简单将用户重定向到index页面。
复制PHP内容到剪贴板
PHP代码:
define('NO_DB', 1);
define('NO_PRINT', 1);
include "include/class_system.php";

class Page extends SystemBase {
functioninit() {
$this->user->logout();
$this->redirect("index.php");
}
}

$p = new Page();

引用:
First we define the NO_DB and NO_PRINT constants tooptimize the loading time of this page (asdescribed in Part 1 of this series).Now all we have to do is use theuser class to log the user out and redirect toanother page in thepage's initialization event.
首先,我们定义NO_DB和NO_PRINT常量来优化加载这个页面的时间(正如我们在本系列教程中第一部分描述的那样)。现在,我们要做的所有事情,就是使用user类来注销用户,并在页面初始化事件中重定向到另外的页面。
引用:
The login.php page will need an interface and we willuse the system'sform handling abilities to simplify the implementationprocess. Details of how this works willbe described in Parts 3 and 4 of thisseries. For now, all we need to know isthat we need an HTML form thatis linked the application logic. The form isprovided below.
这个login.php页面需要一个接口,我们使用系统的表单处理能力简化处理的实现过程。至于这个过程是如何运作的,我们将会在本系列教程的第三和第四部分详细介绍。现在呢,我们所需要知道的全部事情,就是我们需要一个HTML表单,这个表单与应用程序的逻辑相连接。表单代码如下:
复制PHP内容到剪贴板
PHP代码:
<formaction="<?=$_SERVER['PHP_SELF']?>" method="POST"name="<?=$formname?>">
<inputtype="hidden" name="__FORMSTATE" value="<?=$_POST['__FORMSTATE']?>">

<table>
<tr>
<td>Username:
<td><input type="text" name="txtUser"value="<?=$_POST['txtUser']?>"></td>
</tr>
<tr>
<td>Password:
<td><input type="password" name="txtPW"value="<?=$_POST['txtPW']?>"></td>
</tr>
<tr>
<tdcolspan="2">
<inputtype="checkbox" name="chkPersistant"<?=$persistant?>>
Rememberme on this computer
</td>
</tr>
<trstyle="text-align: center; color: red; font-weight: bold">
<tdcolspan="2">
<?=$error?>
</td>
</tr>
<tr>
<tdcolspan="2">
<inputtype="submit" name="Login" value="Login">
<inputtype="reset" name="Reset" value="Clear">
</td>
</tr>
</table>
</form>
引用:
Now we need the code to log a user in. This codesample demonstrates how touse the system framework to load the above form intoa page template,handle the form events, and use the user class to authenticatethe user.
现在我们来编写用户登录的代码。这个代码演示了如何使用系统框架将上面的表单加载到一个页面模板中,以及处理表单事件、使用user类来验证用户。
复制PHP内容到剪贴板
PHP代码:
class Page extends SystemBase {
functioninit() {
$this->form = new FormTemplate("login.frm.php","frmLogin");
$this->page->set("title","Login page");
if(!isset($_POST['txtUser']) && $name=getCookie("persistantName")){
$this->form->set("persistant","checked");
$_POST['txtUser']=$name;
}
}

functionhandleFormEvents() {
if(isset($_POST["Login"])) {
if($this->user->login($_POST['txtUser'],$_POST['txtPW'])) {
if (isset($_POST['chkPersistant'])) {
sendCookie("persistantName", $_POST['txtUser']);
} else {
deleteCookie("persistantName");
}
$this->redirect($_SESSION['LastPage']);
}else
$this->form->set("error","Invalid Account");
}
}
}
$p = new Page();

引用:
Onpage initialization, the form is loaded into thepage template, thepage's title is set and the user's login name is pre-enteredinto theinput field if the persistent cookie is set. The real work happenswhenwe handle the form events (i.e. when the user presses a button tosubmit thepage). First we check if the login buttonwas clicked. Thenwe use the login name and password submitted to authenticatethe user.If authentication is successful,we also set a cookie to remember theusers name for the next time. If theauthentication fails, an error isdisplayed on the page.

在页面初始化时,表单被加载到页面模板中,页面的标题已经被设置好,如果有永久的cookie,那么用户的登录名还会被预先输入到输入框中。当我们处理表单的时候,我们就真正有事情要做了(比如:当用户按下一个按钮提交页面)。首先,我们检查登录按钮是否被点击了。然后,我们使用提交过来的登录名和密码来验证用户。如果验证成功,我们同时设置一个cookie来记住用户名以便下次使用。如果验证失败,则会在
页: [1]
查看完整版本: PHP框架设计入门(整理篇)