PHP框架设计:布局篇

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

PHP框架设计入门之一:布局篇
本文讲述了如何用PHP来做一个完整的应用框架设计。

我们假定读者已有PHP的工作知识。

系列的此部分讲述框架的应用和类的架构。

接下来的部分将覆盖从会话处理到创造页面模板的内容。

类的架构
在贯彻大项目时,我喜欢先画出类图,并看看怎样把各部分组件拼合在一起。

幸亏,PHP 从版本4开始具备了相应的功能支持面向对象编程。

尽管本文是用PHP4写的,但你同样能用PHP5来实现所有的功能(在本文写作时PHP5还正待发布)。

面向对象编程和设计的内容超出了本文的范围,希望读者至少在一定程度上熟识其相关内容。

因为OOP是开发大型应用和web应用开发的关键,这点是毫无例外的。

为了提高效能,我们将尽可能把框架的结构最小化,但同时又有足够的扩展性和灵活性,以确保框架能满足以后各种项目的不同需求。

开始之前,我们先预设一个处理逻辑,应用中的每一个页面都将被封装在一个类里面。

这个类又继承了系统的基类,它将处理(此页面)应用要做的一切事情。

这样做的好处是很明显的:第一,对项目开发人员封装了所有复杂的代码,这样就不用担心新同伴搞乱了系统架构(除非你有意这样做)。

第二,应用中的所有页面都能保持一致性,你再也不用去拷贝-粘贴相同的代码到每个页面。

第三,更重要的,如果要改变应用中的一些功能,只有保持对系统基类的接口的一致性,就不用去编辑每一页了。

现在确定一下基类要做什么。

下面是准备讨论的一些基本特征:
建立数据库连接
确定页面布局和显示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(表单处理)
此框架内,所有页面都有这样的结构:
复制内容到剪贴板
代码:
include"include/class_system.php";
class Page extends SystemBase{
//your code here
}
$p=new Page();
在这里,我们想要把像创造新页面这类最普通任务所花费的时间减到最小。

在本例中,我们只需要包括基类,扩展基类,然后简单的把类实例化就可以玩下去了。

这样大家就可以把精力放在研究页面具体要做什么上面,而不是把时间化在普通事务上。

Configuration Files and Useful Functions文件配置和有用的函数
为简单解释起见,先建立一个包含应用配置的文件。

我们可以用XML或其他一些复杂的技术来做,不过这样框架的效率就会降低了,所以我们从最基本的技术开始。

我们现在需要的只是一些常数用来定义像数据库连接或安装路径之类的字符串。

我们不想在每次改数据库密码或者改变应用路径时都要在每页里翻来倒去的寻找修改的地方。

只要把constants.php 这个文件找出来就行了。

通常我认为最有用的设定是URL和应用的系统路径。

同样的,有一些我们随时需要用到的函数,我把它们放在functions.php文件里。

我们没有把这些函数作为基类的方法,是因为应用里所有的类都可能用到它们(包括那些没有扩展基类的)。

在需要用到时,我再讲这个函数。

现在作为一个简单的例子,先包括一个me()函数,它将返回现在PHP文件的文件名。

复制内容到剪贴板
代码:
function me(){
return substr($_SERVER['PHP_SELF'],strrpos($_SERVER['PHP_SELF'],'/')+1,
strlen($_SERVER['PHP_SELF']));
}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()的方法。

)
如果考虑到我们使用它的方式,系统基类的实施就没什么令人惊奇的了。

因为只有在类的实例创造时,代码才被执行,我们可以把所有要用到的东西都放到类的构造器里。

还有,因为基类不能确切的知道如何呈现出具体的每一页,我们要用到抽象的方法,并使用多态性来照料这一切。

(如子类重载基类的方法。

)下面是构造器里要调用的一些方法:复制内容到剪贴板
代码:
init()-initialize the page
authenticate()-perform authentication
handleFormEvents()-handle page submits
printPage()-output the HTML
destroy()-destroy page(close DB connection)
Unfortunately,PHP4does not enforce many OOP principles(note that PHP5has anew and much more robust object model).You can get everything to work,such as inheritance and polymorphism,but it takes some effort and some faith.For example, there is no concept of method protection so all methods(and attributes for that matter)are made public.Thisis a big no-no when it comes to OOP so a common convention is to prefix all methods that are intended to be private with anunderscore(_) and then take it on faith that the users of the class will not call these methods. Another problem is that we cannot declare the class to be abstract(i.e.we do not want people to declare instances of the base class but rather force them to inherit from it).We can get around this limitation by including the following lines at the top of our constructor(you can read this article for an in-depth analysis of how this works).The code checks to see if the instance of the class is the base class and throws anexception.
代码:
if(!is_subclass_of($this,'SystemBase')){
trigger_error('Base instantiation from non subclass',E_USER_ERROR);
return NULL;}Database Access
无论使用什么类型的SQL数据库,应用框架都应该对数据提供一个清晰的接口。

在应用和数据库后台之间,最好能保持松散的偶合。

比如说,如果用户打算要改变数据库的地理位置,或甚至于要改变SQL数据库的类型,你也用不着去改写代码。

幸好,已经有人为我们处理好了这些问题了,我们要做的就是使用PEAR::DB模块,它对一些通用的数据库服务器都提供了统一的接口。

你可以在”PHP扩展和应用分类“里阅读这部分,我建议大家也阅读下面的PEAR::DB辅导部分。

假如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;
}
此函数在没有引进PEAR:DB库时予以引进,创建一个DNS字串,并连接数据库。

我们要做的就是在系统基类的构架器里像这样:$this->db=db_connect();来使用此函数,而我们已经这样做了。

也许你会认为现在讨论代码优化还为时太早,因为很多需要的特征还没贯彻。

代码优化是一种艺术,我也不会一行一行的去分析怎么取得优化。

关键的一点是,现在我们已经可以说出主要的瓶颈是什么,我们如何在其失控之前,把问题处理好。

最明显导致效能丧失的是每一页都要用到的数据库的持续连接。

复制内容到剪贴板
代码:
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 our application will be haveand how the framework will be used.We established an OOP design for the application,defined some constants and function to expedite routine work,and added database support. Read the next part to see how to manage session data and users.。

相关文档
最新文档