DPT-一种PHP设计模式 .docx
精品名师归纳总结一、 DPT 介绍PHP 为什么在主流的应用中总是那么不杰出,总是不如.Net/Java,就是由于在 PHP 处理大型应用的时候,那些不完整的面对对象机制、数据库处理的单一,不通用性等等,影响了PHP 做大型应用。那么,如何来转变这个状况了?当然就是需要引进一些新的设计方法,把PHP 中不健全的面对对象机制完整起来, 进行更好的 PHP 大中型应用的开发。从 Java过来的 MVC 模式特殊流行,而且已经有部分已经引伸进了 PHP 领域,设计模式的引进,就是为了更好的把握工程开发。今日我要说一种设计模式,类似于 MVC ,它叫 DPT 模式。其实有时候我也觉得有点象 Java里面的 DAOData Access Object ,不过 DAO 是夹在业务规律层和数据库资源层之间的,而DPT 更多的是把业务规律也封装在类里,和DAO 层在相同的内容中。D Data,数据收集层P Php,PHP 数据调用层T Template,模板层第一,我们要对它进行简洁的明白。Data,就是我们的数据层,它不是数据库抽象类,而是通过数据库接口,执行一些SQL,把数据猎取的过程,一般把这种操作封装在类里面,就形成了我们的数据收集层。可编辑资料 - - - 欢迎下载精品名师归纳总结Template,模板层,就是我们的HTML 页,里面不包含任何 PHP 代码,只有模板标签的内容,通过它来把握数据在页面中有格式的显示。我们这里三层中, 每一层都是鼓励由一个人来开发,然后通过 PHPDoc 之类的工具, 把源代码中的API生成文档,由 P 层的人进行调用。那么,在实际的工程开发中,它是怎么运作的了,我们又如何把这种设计模式引进我们的工程中了?我们下面将运用一个实际的工程来讲解DPT 模式。阅读一下内容必需具备基本的PHP4 的面对对象编程、数据库抽象类、模板等学问。我们目前为了加速 PHP 的开发,都使用PHP 封装了部分功能,比如数据库操作抽象类,模板类等等, 这些都是为了开发复杂应用而应运而生的。目前比较主流的数据库抽象类有phplib db 、PEAR:DB 、ADODB 等等,模板处理类有 phplib template 、smartTemplate、Smarty 等等。本文中都是使用PHP Group 举荐的产品,数据口抽象类使用PEAR:DB ,模板处理类使用 Smarty,假如对这两个类库不熟识的读者,请参考文章后面的链接。二、工程体系结构下面我们来构建我们基于DPT 模式的 PHP 应用。(以下部分内容参考MVC 模式、类封装仍是黑客代码)文件目录结构(只涉及到关键的目录) class类库,包含全部的数据收集层template 模板文件存放目录include常用库,包括 PEAR、Smarty 等类库,同时仍有自己定义的基本函数config.inc.php基本配置文件,包括数据库配置,其他基本信息配置security.inc.php 安全处理页,主要多传递的变量进行处理init.inc.phperror.php错误处理页可编辑资料 - - - 欢迎下载精品名师归纳总结如 cmsMessage.class.php,那么这个类就是属于功能CMS 里面的,只负责操作Message这个表。全部的数据库交互和操作都是封装在类里的,在P 层不答应显现任何直接操作数据库的语句。template 目录中存放了我们的网页模板,模板中都是使用Smarty 标签进行排列的,同时,在模板中, 都是建议使用 JSCSS 来把握页面,模板中只有DIV 标签来简洁的排版,这样,特殊利于网站改版和更换皮肤。include 目录就是对常用文件的包含,比如PEAR:DB 类、 Smarty 类库文件等。 config.inc.php 就是基本的配置文件,包括数据库、基本常量等等,security.inc.php 是安全处理页,我们这里主要是做一个变量的安全检查,下面内容我们将仔细介绍。init.inc.php 是一个初始化操作的页面,包括初始化数据库链接,实例化模板处理类等等操作, error.php 是错误信息处理页,全部的错误信息通过URL 编码后转到该页。三、工程基本配置代码关键页代码实例:/* config.inc.php* 配置文件*/* 数据库配置*/define'DB_HOST', 'localhost' 。 /数据库主机define'DB_USER', 'root' 。 /数据库链接用户define'DB_PASS', '' 。 /连接密码define'DB_NAME', 'cms' 。 /默认数据库define'DB_PORT', '3306' 。 /数据库端口define'DB_TYPE', 'mysql' 。 / 数据库类型define'DB_OPT', '1' 。 /是否长期链接/* 模板信息配置 */define'TPL_TEMPLATE_DIR', './template/'。 / 模板目录define'TPL_COMPILE_DIR', './template/templates_c/'。 /模板编译目录可编辑资料 - - - 欢迎下载精品名师归纳总结define'TPL_CACHE_DIR','./template/cache/'。 /模板缓存目录define'TPL_LIFTTIME','1'。/ 缓存时间define'TPL_CACHEING','true'。 /是否缓存define'TPL_LEFT_DELIMITER', ''。/左边界符define'TPL_RIGHT_DELIMITER', ''。/右边界符/* 网站路径配置 */define'ROOT_PATH', dirnameFILE。 /网站所在根目录define'URL_PATH', dirname$_SERVERPHP_SELF。 /网站 URL 的址路径define'DB_PATH', ROOT_PATH.'/include/db'。 /PEAR:DB 目录define'TPL_PATH', ROOT_PATH.'/include/smarty'。 /Smarty 目录/* security.inc.php* 安全过滤文件*/* 过滤规章*/$arr_filtrate = array"'", '"', ""。/* 过滤函数*/function var_filtrate$varglobal $arr_filtrate 。foreach $arr_filtrate as $valueif eregi$var, $value return true 。return false。可编辑资料 - - - 欢迎下载精品名师归纳总结if phpversion < '4.1.0' $get = &$HTTP_GET_VARS。$post = &$HTTP_POST_VARS 。 else $get = &$_GET 。$post = &$_POST 。/* 检查 GET 变量 */ if count$get foreach $post as $get_var if var_filtrate$get_var exit'Commit get parameter falsity' 。/* 检查 POST变量 */ if count$post foreach $post as $post_var if var_filtrate$post_var exit'Commit post parameter falsity' 。其实,以上过滤的方法也不是最好的,建议参考我的另两篇防注入文章猎取更好的方法,链接参考附录。/* error.php可编辑资料 - - - 欢迎下载精品名师归纳总结*/if .isset$getmsg exit'Not commit parameter' 。echo "Error Message: ". $getmsg 。echo "<p><a href='javascript:history.back'> 返回上一页 </a>"。就是一些错误处理的作用,一般出的GET 方式传递过来的消息都是经过urlencode 过的字符。/* init.inc.php* 初始化程序*/require_oncedirnameFILE.'config.inc.php' 。require_onceROOT_PATH.'security.inc.php' 。require_onceDB_PATH.'DB.php' 。require_onceTPL_PATH.'Smarty.class.php' 。/* 初始化数据库链接*/$db = DB:connect"DB_TYPE:/DB_USERDB_PASS:DB_HOST/DB_NAME", DB_OPT。if DB:isErro$db return $dg->getMessage 。$tpl = &new Smarty 。/* 初始化模板 */$tpl->templates_dir = TPL_TEMPLATE_DIR。$tpl->compile_dir = TPL_COMPILE_DIR。$tpl->cache_dir= TPL_CACHE_DIR 。$tpl->configs= TPL_CONFIGS_DIR 。$tpl->lifetime= TPL_LIFTTIME。可编辑资料 - - - 欢迎下载精品名师归纳总结$tpl->left_delimiter = TPL_LEFT_DELIMITER。$tpl->right_delimiter = TPL_RIGHT_DELIMITER。基本文件描述完毕。代码写了不少,只是为了更好的懂得这个模式。四、框架实际开发说明: 我们以下工程代码都是以 cms 数据库中 topic 表做例子, 代码只是为了演示框架结构, 没有对代码进行测试,不保证能够正常运行。topic 的表结构:CREATE TABLE topic id int11 NOT NULL auto_increment,title varchar255 NOT NULL default '',addtime int11 default NULL,author varchar50 default NULL,type int11 default NULL,option int11 default NULL, PRIMARY KEYid,KEY id id。(一) Data 层:数据采集层Data 层主要就是针对数据库的全部操作都封装起来,然后通过接口的形式供应应 Php 层进行调用,同时在 Data 层里也封装了一些原始的数据库操作(类似于 Java中的 DAO )。一般 Data 层都是类的形式,储存在我们上面的 /class 目录下,一般的准就是一个类文件操作一个数据表,就是说不管详细的业务规律如何,全部的数据表操作都是封装在一个类文件里的。比如说我们有一个数据表叫做topic,那么我们对应操作的类文件就是: topic.class.php。其实这里是可以做扩展的,比如说,我们的工程特殊庞大,有很多内容, 比如包括有 CMS 、Blog 、BBS 等等,那么我们就必需给每一个栏目支配一个数据库,那么针对当前操作数可编辑资料 - - - 欢迎下载精品名师归纳总结作在类里面进行的链接。假设我们目前操作的栏目是CMS 系统,数据库名叫做 cms,那么我们下面构造一个操作cms 数据库里面的 topic 表的类来。/* cms_topic.class.php* 文章处理类*/class cmsTopicvar cmsDBName。 / 数据库名var cTableName。 /当前操作的表名var cDsn。 /数据链接源var cDebug。 /是否打开调试 ,1 为是, 0 为否var cDbPointer/链接资源var cfetchMode/猎取数据库资料的方式var cEncode /数据库中数据储存的编码格式,默认是UTF-8/* 构造函数 */ function cmsTopic/配置信息从 config.inc.php 中设置$this->cfetchMode = DB_FETCHMODE_DEFAULT。$this->cTableName = "topic" 。$this->cDsn = "mysql:/". DB_USER.":".DB_PASS."".DB_HOST."/".DB_NAME 。可编辑资料 - - - 欢迎下载精品名师归纳总结/* 链接数据库*/function connectDatabaseif .is_object$cDbPointer$this->cDbPointer = DB:connect$this->cDsn 。if $this->cEncode="utf8" $this->cDbPointer->query"set names 'utf8'" 。$this->cDbPointer->setFetchMode$this->cfetchMode 。if DB:isError$this->cDbPointer return false。return $this->cDbPointer 。/* 关闭数据库连接 */ function closeDatabaseif is_object$this->cDbPointer $this->cDbPointer->disconnect 。/*-数据库基本操作*/可编辑资料 - - - 欢迎下载精品名师归纳总结function insert$arrif.is_array$arr | count$arr = 0 return false。if"" = $this->cTableName return false 。$db = $this->connectDatabase 。$res = $db->autoExecute$this->cTableName,$arr,DB_AUTOQUERY_INSERT。ifDB:isError$res return $res。else$insertId = $db->getOne"select LAST_INSERT_ID。" 。if$insertId>0 return $insertId 。 else return true 。/* 更新操作 */function update$id,$arrif"" .= $id && .is_array$arr return false。可编辑资料 - - - 欢迎下载精品名师归纳总结$res = $db->autoExecute$this->cTableName,$arr,DB_AUTOQUERY_UPDATE,"id = '$id'"。ifDB:isError$res return false。elsereturn true。/* 删除操作 */ function delete$id$db= $this->connectDatabase。$res = $db->query"DELETE FROM ".$this->cTableName." WHERE id = '$id'"。ifDB:isError$res return false。elsereturn true。上面的代码一个很基本的针对一个表操作的类雏形已经描述出来了,包括连接数据库,基本的数据库原始操作都有了。你确定会问,为什么没有把select 的操作封装进去?主要是由于select 是 SQL 里最复杂的操作,不行能写那么通用的一个方法去操作它,所以好不如不写,自由发挥。那么我们需要加上一些基本的功能了?比如读取内容、新增加一篇文章等操作,那么我们仍必需在类里面添加一些方法,比如我们增加提取一篇文章内容、提取指定时间的文章、提取指定类别的文章、统计目前全部文章的总数等操作。可编辑资料 - - - 欢迎下载精品名师归纳总结* cms_topic.class.php* 文章类增强*/class cmsTopic/ .上面已经描述的方法省略/* 函数: getTopicContentById* 功能: 猎取指定 ID 的文章的内容* 参数: $id指定要猎取的 ID* $cols 要提取的字段值* 返回: 成功返回数据集数组,失败返回错误信息*/function getTopicContentById$id, $cols="*"$db = $this->connectDatabase$this->cDsn 。$sql = "SELECT $cols FROM ". $this->cTableName ." WHERE id = '$id'"。$result = $db->getAll$sql 。if DB:isError$result return $result->getMessage。 else $db->disconnect 。return $result。/* 函数: getTopicBySpecifyTime可编辑资料 - - - 欢迎下载精品名师归纳总结* 参数: $startTime 指定开头时间* $endTime 终止时间* $cols 要提取的字段值* 返回: 成功返回数据集数组,失败返回错误信息*/function getTopicBySpecifyTime$startTime=0, $endTime=0, $cols="*"$db = $this->connectDatabase$this->cDsn 。$start = $startTime = 0 . "" : "WHERE addtime > $startTime"。$end = $endTime = 0 . "" : "AND addtime < $startTime"。$sql = "SELECT $cols FROM ". $this->cTableName ." ".$start ." ".$end。$result = $db->getAll$sql 。if DB:isError$result return $result->getMessage。 else $db->disconnect 。return $result。/* 函数: getTopicByType* 功能: 猎取指定类别的文章的列表* 参数: $type 指定的类型* $cols 要提取的字段值* 返回: 成功返回数据集数组,失败返回错误信息*/function getTopicByType$type, $cols="*"可编辑资料 - - - 欢迎下载精品名师归纳总结$sql = "SELECT $cols FROM ". $this->cTableName ." WHERE type = '$type'"。$result = $db->getAll$sql 。if DB:isError$result return $result->getMessage。 else $db->disconnect 。return $result。/* 函数: getTopicSum* 功能: 猎取全部文章总数,假如指定类别,就猎取指定类别总数* 参数: $type 指定的类型* 返回: 成功返回猎取的总数,失败返回错误信息*/function getTopicSum$type=""$db = $this->connectDatabase$this->cDsn 。$typeStr = $type = "" . "" : " WHERE type = '$type'"。$sql = "SELECT countid FROM ". $this->cTableName ." ".$typeStr。$result = $db->getOne$sql 。if DB:isError$result return $result->getMessage。 else $db->disconnect 。return $result。可编辑资料 - - - 欢迎下载精品名师归纳总结上面我们构造了一些数据提取类,这应当就是我们Data 层的核心了。写方法的时候要尽量考虑到扩展性,比如对列的提取,比如一个方法适合多种情形,比如排序等等,考虑的越多,以后爱护起来就比较容易,当然,我举荐的方法是一个方法尽量就做一件事情,假如一个函数要做多个事情,那么就写成多个函数,这样便于代码重用和爱护性,我个人认为一个方法最用不要超过100 行。假如函数中有涉及到数据库的操作,确定记得结尾的时候把数据关闭掉,不然很简洁把服务器资源占 用光。当然,你也可以在PHP 层去关闭连接。比如,你需要很多次调用同一个方法,那么这个方法假如反复的连接数据库又关闭数据库,也很铺张资源,而且速度慢,这个时候就可以把关闭数据库的操作在Php层进行关闭,你可以先构造好一个方法来进行,比如我们上面的closeDatabase 方法。(二) Php 层:数据调用层PHP 层主要就是把从 Data 层收集的数据再这一层进行调用。由于我们基本的原就就是把全部跟数据库的操作都封装在Data 层里,在其他层都不涉及到任何的直接对数据库的操作,这样能够进行良好的封装,这样有点类似于 JSP和 Javabean,Javabean的类负责和数据库交互,JSP负责调用 Javabean来输出数据。我们这里的 PHP 层就相当于 JSP层,前面的 Data 层就相当于 Javabean层,这样玻璃他们之间的耦合度, 能够便利程序日后的爱护。我们这里的 PHP 层主要就是复杂从数据库种提取数据,完成一些简洁的规律,然后把数据输出到Template(模板层)。现在我们利用示例代码来看看PHP 层是如何调用 Data 层的数据的。/* topic_list.php* 文章列表程序*/* 包含基本文件 */ require_once"init.inc.php" 。require_once"class/cms_topic.class.php" 。可编辑资料 - - - 欢迎下载精品名师归纳总结$topic = new cmsTopic 。/猎取文章类型变量$topicType = intvalget"type"。/从 Data 层中把数据提取过来$topicList = getTopicByType$topicType。/给模板变量赋值后输出页$tpl->assign"topic", $topicList。$tpl->assign"topic.html" 。代码是不是很简洁?就是把数据猎取过来,然后解读到模板层中去处理,所以这样假如以后显现问题改起来比较简洁,比如是数据猎取的问题,那么直接改上面的类文件就行,假如是模板显示的问题,那么直接修改模板层中的对应的模板页就可以,特殊便于爱护。(三) Template 层:模板层这个模板层就是我们常说的网页了,不过这里就是包含了一些Smarty 的模板变量和 HTML 混和,模板页处理的时候就对页面中的模板变量进行替换,最终我们看到的结果就是模板页和PHP 层中的程序输出混和的结果。一般模板页设计的时候, 最好遵循 Web 规范,就是说尽量在页面中不使用表格等html 标签来把握页面, 而是使用 div 层来存放数据,使用css样式表来把握页面布局,这样对包括JavaScript 脚本的编写,以后页面的改版等等特殊有好处。而且假如要仍模板也很简洁,只需要把css文件替换就可以达到成效。当然, 假如对 web 规范不明白,那么建议去阅读一下网站重构这本书。我们下面就简洁的描述一下Template(模板层)的代码是如何的。*加载头部文件 *include file="header.html"可编辑资料 - - - 欢迎下载精品名师归纳总结<div>* 左边导航条 *</div><div><h3> 文章列表 </h3>section name=topicList loop=$topic标题: <a href="">$topictopicList.title|escape:"html"|truncate:30:".":true</a> |时间: $topictopicList.addtime|date_format:"%Y年%m 月%d 日" |作者: $topictopicList.author|escape:"html"<br />sectionelse暂时没有任何文章/section</div>*加载底部文件 *include file="foot.html"模板页中大致可能有一些JavaScript 程序,或者有样式文件, 一般使用样式文件来把握页面的布局和显示成效。我们这里没有详细的描述,在实际工程中可以由网页制作人员去负责。五、使用 DPT 模式的工程规划一般在全部的软件工程或者是网站工程中,要保证一个工程能够顺当完整的完成,那么便需要技术主管或者架构师良好的设计和经管。一般全部工程中人是最难把握的因素,你可以把工程指定的特殊完善, 架构可以选择的特殊合理,但是你不能把握人的因素,不能保证工程的中的某个成员可能在任何时候离开 工程。当在 PHP 工程中,假如一个工程角色突然的离去,可能导致工程要停顿,要重新找人来接替,影响了工程的进度,那么如何有效的把握和解决这些问题。在一个使用 DPT 设计模式的工程中,工程中个个角色分别有网页设计师、网页制作人员、客户端脚本JavaScript 程序员、服务器端 PHP 程序员。他们的分工都是什么了?网页设计师负责设计网页的界面,生可编辑资料 - - - 欢迎下载精品名师归纳总结主要的任务就是负责页面布局样式的编写。客户端程序员主要是负责客户端脚本的编写,比如针对页面中需要使用的 JavaScript 进行编写,PHP 程序员主要是负责我们上面Data & PHP & Template 三层的代码编写, 当然,假如工程足够庞大, 完全可以拆分出来, 有 PHP 程序员负责 Data 层,有 PHP 程序员负责 Php 和 Template 层,分工清晰, Php 层程序员只是需要调用Data 层程序员已经写好的类库进行调用,不用关怀类是如何实现的。这样一个工程架构下来,可以依据任务需要来支配某个模块的人的数量,最大限度的把工程规划好。当然,工程中一些必要的因素是要考虑的,比如,如何让网页制作人员、客户端脚本程序员和PHP 程序员良好的合作,那么就是分别他们的责任,比如,模板页必需由PHP 程序员来编写,然后提交给JavaScript 程序员制作客户端脚本,最终再由网页制作人员通过CSS 来把握布局,那么 Php 程序员在模板页中就必需使用 div 等标签来定义一个块的数据,假如任何一块出了问题,那么对应找相应责任人,就能够很好的避免彼此推卸责任,或者权责不分的情形,这样有利于经管,也有利于每个开发成员之间的良好合作。为了防止工程失控,或者不会由于工程成员的离开而影响工程的进度和经管,必需有相应的方法和规章。我们主要针对 PHP 程序员来进行描述,部分方法同样适用于网页制作人员和客户端脚本程序员。(1) 编码规范工程开发中为了便于爱护和以后其他人接手代码,必需统一编码规范,包括对目录、文件名、类、函数、变量、注释等等都必需遵循规范,而且为了代码的爱护,必需要求PHP 程序员编写注释。目前基本遵循的是 Fredrik Kristiansen 写的 PHP 编码规范,或者是PEAR 中代码的规范。假如代码为了做成接口, 或者需要做成参考的文档便利以后爱护代码,使用 phpDoc 等工具, 那么为了能够使用 PEAR 包中的 phpDoc 能够正常识别, 所以一般建议遵循 PEAR 包的规范,主要是 DPT 模式中 Data 层中类的的编码必需规范。PEAR 中 pear.php 中基类的部分代码:class PEAR可编辑资料 - - - 欢迎下载精品名师归纳总结/ properties/* Whether to enable internal debug messages.* varbool* access private*/var $_debug = false。/ 其他属性省略 ./ /* 对方法的描述 */ constructor/* Constructor.Registers this object in* $_PEAR_destructor_object_list for destructor emulation if a* destructor object exists.* param string $error_classoptional which class to use for* error objects, defaults to PEAR_Error.* access public* return void*/function PEAR$error_class = null可编辑资料 - - - 欢迎下载精品名师归纳总结/ 对于上面内部标签的说明请参考文章结尾链接中的php Documentor 1.2.2 使用说明规范(2) 单元测试单元测试是对程序进行的第一步测试,并且是程序员自己做的测试,没有谁比自己更明白自己写的代码,所以单元测试对保证程序的稳固性尤为重要。所以一般工程中,特殊是写类库的程序员,确定要对自己写的代码进行单元测试, 只要保证数据能够稳固的猎取,才能保证 Php 层的程序员能够正确的猎取数据。针对 PHP的单元测试工具目前有Simp