• 一种PHP设计模式:DPT 新风宇宙


    一、DPT介绍

    PHP为什么在主流的应用中总是那么不出色,总是不如.Net/Java,就是因为在PHP处理大型应用的时候,那些不完整的面向对象机制、数据库处理的单一,不通用性等等,影响了PHP做大型应用。那么,如何来改变这个状况呢?当然就是需要引进一些新的设计方法,把PHP中不健全的面向对象机制完整起来,进行更好的PHP大中型应用的开发
    从Java过来的MVC模式非常流行,而且已经有部分已经引伸进了PHP领域,设计模式的引进,就是为了更好的控制项目开发。今天我要说一种设计模式,类似于MVC,它叫DPT模式。其实有时候我也觉得有点象Java里面的DAO(Data Access Object),不过DAO是夹在业务逻辑层和数据库资源层之间的,而DPT更多的是把业务逻辑也封装在类里,和DAO层在相同的内容中。

    D - Data,数据收集层
    P - Php,PHP数据调用层
    T - Template模板

    首先,我们要对它进行简单的了解。
    Data,就是我们的数据层,它不是数据库抽象类,而是通过数据库接口,执行一些SQL,把数据获取的过程,一般把这种操作封装在类里面,就形成了我们的数据收集层。
    Php,就是对我们收集的数据进行整理,规划,同时解析模板进行数据的显示。
    Template,模板层,就是我们的HTML页,里面不包含任何PHP代码,只有模板标签的内容,通过它来控制数据在页面中有格式的显示。
    我们这里三层中,每一层都是鼓励由一个人来开发,然后通过PHPDoc之类的工具,把源代码中的API生成文档,由P层的人进行调用。

    那么,在实际的项目开发中,它是怎么运作的呢,我们又如何把这种设计模式引进我们的项目中呢?
    我们下面将运用一个实际的项目来讲解DPT模式。阅读一下内容必须具备基本的PHP4的面向对象编程、数据库抽象类、模板等知识。

    我们目前为了加速PHP的开发,都使用PHP封装了部分功能,比如数据库操作抽象类,模板类等等,这些都是为了开发复杂应用而应运而生的。目前比较主流的数据库抽象类有phplib db、PEAR::DB、ADODB等等,模板处理类有phplib template、smartTemplateSmarty等等。本文中都是使用PHP Group推荐的产品,数据口抽象类使用PEAR::DB,模板处理类使用Smarty,如果对这两个类库不熟悉的读者,请参考文章后面的链接。

    二、项目体系结构

    下面我们来构建我们基于DPT模式的PHP应用。(以下部分内容参考《MVC模式、类封装还是黑客代码》)

    文件目录结构(只涉及到关键的目录)
    class   类库,包含所有的数据收集层
    template  模板文件存放目录
    include   常用库,包括PEAR、Smarty等类库,同时还有自己定义的基本函数
    config.inc.php  基本配置文件,包括数据库配置,其他基本信息配置
    security.inc.php 安全处理页,主要多传递的变量进行处理
    init.inc.php
    error.php  错误处理页

    class目录中存放了我们数据收集层中的内容,一般的建议是每个类文件只是针对一个表进行操作,比如cmsMessage.class.php,那么这个类就是属于功能CMS里面的,只负责操作Message这个表。所有的数据库交互和操作都是封装在类里的,在P层不允许出现任何直接操作数据库的语句。
    template目录中存放了我们的网页模板,模板中都是使用Smarty标签进行排列的,同时,在模板中,都是建议使用JS+CSS来控制页面,模板中只有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_CONFIGS_DIR', './template/configs/');  //模板配置文件目录
    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', dirname(__FILE__));   //网站所在根目录
    define('URL_PATH', dirname($_SERVER[PHP_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($var)
    {
      global $arr_filtrate;
        foreach ($arr_filtrate as $value)
        {
            if (eregi($var, $value)) {
                return true;
            }
            return false;
        }
    }

    /* 获取不同版本下的GET和POST数组 */
    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($get[msg])) {
       exit('Not commit parameter');
    }
    echo "Error Message: ". $get[msg];
    echo "<p><a href='javascript:history.back()'>返回上一页</a>";

    就是一些错误处理的作用,一般出的GET方式传递过来的消息都是经过urlencode()过的字符。

    /*
    * init.inc.php
    * 初始化程序
    */
    require_once(dirname(__FILE__).'config.inc.php');
    require_once(ROOT_PATH.'security.inc.php');
    require_once(DB_PATH.'DB.php');
    require_once(TPL_PATH.'Smarty.class.php');

    /* 初始化数据库链接 */
    $db = DB::connect("DB_TYPE://DB_USER@DB_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->caching  = TPL_CACHEING;
    $tpl->left_delimiter = TPL_LEFT_DELIMITER;
    $tpl->right_delimiter = TPL_RIGHT_DELIMITER;

    基本文件描述完毕。代码写了不少,只是为了更好的理解这个模式。

    四、框架实际开发


    说明:

    我们以下项目代码都是以cms数据库中topic表做例子,代码只是为了演示框架结构,没有对代码进行测试,不保证能够正常运行。
    topic的表结构:

    CREATE TABLE `topic` (
      `id` int(11) NOT NULL auto_increment,
      `title` varchar(255) NOT NULL default '',
      `addtime` int(11) default NULL,
      `author` varchar(50) default NULL,
      `type` int(11) default NULL,
      `option` int(11) default NULL,
      PRIMARY KEY  (`id`),
      KEY `id` (`id`)
    );


    (一)Data层:数据采集层

    Data层主要就是针对数据库的所有操作都封装起来,然后通过接口的形式提供给Php层进行调用,同时在Data层里也封装了一些原始的数据库操作(类似于Java中的DAO)。一般Data层都是类的形式,保存在我们上面的 /class目录下,一般的准则是一个类文件操作一个数据表,就是说不管具体的业务逻辑如何,所有的数据表操作都是封装在一个类文件里的。比如说我们有一个数据表叫做topic,那么我们对应操作的类文件就是:topic.class.php。其实这里是可以做扩展的,比如说,我们的项目非常庞大,有很多内容,比如包括有CMS、Blog、BBS等等,那么我们就必须给每一个栏目分配一个数据库,那么针对当前操作数据库的话,就使用类中封装的链接方法进行链接,就可以抛弃我们上面init.inc.php中初始化的操作,而操作在类里面进行的链接。
    假设我们目前操作的栏目是CMS系统,数据库名叫做cms,那么我们下面构造一个操作cms数据库里面的topic表的类来。


    /**
    * cms_topic.class.php
    * 文章处理类
    */
    class cmsTopic
    {
    var 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;
      $this->cEncode = "utf8";
    }

    /* 链接数据库 */
    function connectDatabase()
    {
      if (!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 closeDatabase()
    {
      if (is_object($this->cDbPointer)) {
       $this->cDbPointer->disconnect();
      }
    }

    /*--------------- 数据库基本操作 --------------*/

    /* 插入操作 */
    function insert($arr)
    {
      if(!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);
      
      if(DB::isError($res)){
       return $res;
      }else{
       $insertId = ($db->getOne("select LAST_INSERT_ID();"));
       if($insertId>0) {
        return $insertId;
       } else {
        return true;
       }
      }
    }

    /* 更新操作 */
    function update($id,$arr)
    {
      if("" != $id && !(is_array($arr))){
       return false;
      }

      $db = $this->connectDatabase();
      $res = $db->autoExecute($this->cTableName,$arr,DB_AUTOQUERY_UPDATE,"id = '$id'");

      if(DB::isError($res)){
       return false;
      }else{
       return true;
      }
    }

    /* 删除操作 */
    function delete($id)
    {
      $db  = $this->connectDatabase();
      $res = $db->query("DELETE FROM ".$this->cTableName." WHERE id = '$id'");
      if(DB::isError($res)){
       return false;
      }else{
       return 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="*")
    {
      $db = $this->connectDatabase($this->cDsn);
      $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 count(id) 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");

    //实例化Data层对象
    $topic = new cmsTopic();
    //获取文章类型变量
    $topicType = intval(get("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="">{$topic[topicList].title|escape:"html"|truncate:30:"...":true}</a> |
      时间:{$topic[topicList].addtime|date_format:"%Y年%m月%d日"} |
      作者:{$topic[topicList].author|escape:"html"}<br />
    {sectionelse}
      暂时没有任何文章
    {/section}
    </div>

    {* 加载底部文件 *}
    {include file="foot.html"}


    模板页中大致可能有一些JavaScript程序,或者有样式文件,一般使用样式文件来控制页面的布局和显示效果。我们这里没有详细的描述,在实际项目中可以由网页制作人员去负责。

    五、使用DPT模式的项目规划

    一般在所有的软件项目或者是网站项目中,要保证一个项目能够顺利完整的完成,那么便需要技术主管或者架构师良好的设计和管理。一般所有项目中人是最难控制的因素,你可以把项目指定的非常完善,架构可以选择的非常合理,但是你不能控制人的因素,不能保证项目的中的某个成员可能在任何时候离开项目。当在PHP项目中,如果一个项目角色忽然的离去,可能导致项目要停顿,要重新找人来接替,影响了项目的进度,那么如何有效的控制和解决这些问题。

    在一个使用DPT设计模式的项目中,项目中个个角色分别有网页设计师、网页制作人员、客户端脚本JavaScript程序员、服务器端PHP程序员。他们的分工都是什么呢?网页设计师负责设计网页的界面,生成效果图,然后由网页制作人员去做成网页,当然,如果是遵循Web标准的项目的话,那么网页制作人员主要的任务就是负责页面布局样式的编写。客户端程序员主要是负责客户端脚本的编写,比如针对页面中需要使用的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.
         *
         * @var     bool
         * @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_class  (optional) 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的单元测试工具目前有SimpleTest和PEAR包中的phpUnit/phpUnit2,phpUnit2是针对PHP 5的测试工具,这个可以按照项目的实际情况来决定使用什么测试工具。SimpleTest和phpUnit的使用参考文章请参考文章最后的链接。


    (3)程序安全

    在今天这个黑客横行,SQL Injection泛滥的时候,为了保证程序能够正确完整的运行,那么就必须要求程序员对自己的程序进行安全的编码。

    主要是两方面,一方面是服务器端的安全,主要就是要过滤用户提交的变量,记得网络上流行的一句话:“不要相信用户提交的任何数据”,因为你不能保证这个用户是恶意的还是善意,是普通用户还是黑客,所以不管是谁,一定要过滤掉它的变量,不过是通过GET方式还是POST方式过来的数据,统统过滤。主要是过滤一些特殊自如,比如/"'等等,保证服务器和数据的安全。

    另一方面就是针对客户端的威胁,比如提交一些恶意的JavaScript代码,当普通用户查看该页的时候,那么将给对方带来影响或者伤害,那么会极大的影响网站或者软件的声誉,这是要避免的。同样的,这个也是要良好的过滤变量,如果无法在用户提交数据的时候过滤,那么就在显示输出的时候进行过滤,主要是过滤一些HTML标签和JavaScript代码,保证客户浏览的安全。

    关于一些基本的安全问题可以参考文章后面的链接。


    (4)代码同步(版本管理)

    但一个项目是多人开发的时候,那么代码同步或者说是版本管理就尤为重要,因为为了保证每个开发成员的代码都是最新的,最稳定的,那么必须要使用CVS等版本管理工具,当然,你想使用VSS也无可厚非。一般建议是专门使用一台服务器来供所有开发人员开发,不建议把代码保存在开发人员的本地上,因为大部分人都是使用Windows等操作系统,如果发生病毒感染、设备损坏或者其他因素,那么代码将壮烈牺牲,这样造成又要重复开发的浪费,如果把代码直接存放在服务器上,一般服务器安装都是Unix/Linux系统,能够基本保证代码的安全和完整。

    CVS的安装使用文章可以参考文章最后的链接。


    (5)多模块通信

    如果项目足够庞大,门户级的应用,那么肯定涉及到多栏目/模块之间的通信


    六、结束语

    DPT模式也许在很多项目中你已经应用了,只是没有认证的进行定义,如果阅读本文能够获得同感,或者恍然大悟乃至垂手顿足,那么我想本文就给了你一定帮助,这就足够了。

    写作文章前前后后花了快一个月,只是一直太懒惰,怕的写的不好,怕这个东西出来没有意义。。。但是不管如何,还是写出来了。

    如果可能,尝试考虑把你的PHP应用移植到PHP5上,也许,那才是你需要的。


    Writer by heiyeluren <heiyeluren_AT_gmail.com>
    2005年8月29日 下午 17:38

    >>>>>> 附录:(文章相应链接)<<<<<<

    PEAR     http://pear.php.net
    Smarty     http://smarty.php.net
    phpUnit    http://phpunit.sourceforge.net
    SimpleTest    http://simpletest.sourceforge.net | http://www.lastcraft.com/simple_test.php
    phpDocumentor    http://www.phpdoc.org

    《PEAR:创建中间的数据库应用层》  http://www-128.ibm.com/developer ... hp/pear4/index.html
    《模板引擎SMARTY》   http://www-128.ibm.com/developerworks/cn/linux/l-smart/index.html
    《MVC模式、类封装还是黑客代码》  http://www-128.ibm.com/developer ... p_design/index.html
    《PHP中的代码安全和SQL Injection防范》 http://blog.csdn.net/heiyeshuwu/archive/2005/06/14/394225.aspx
    《在PHP中使用SimpleTest进行单元测试》 http://www.hsboy.com/blog/archives/90-PHPOESimpleTesta.html
    《用phpUnit帮你调试php程序》  http://www.ccw.com.cn/htm/app/aprog/01_4_13_4.asp
    《php Documentor 1.2.2 使用说明规范》 http://www.phpx.com/happy/top98827.php
    《PEAR:使用PHPDoc轻松建立你的PEAR文档》 http://www-128.ibm.com/developer ... hp/pear3/index.html
    [http://www.phpres.com]
    《PHP 编码规范》    http://www.phpe.net/html/php_coding_standard_cn.html
    《CVS使用手册》    http://www.chedong.com/tech/cvs_card.html

  • 相关阅读:
    The analysis of China's holding the Olympic Games
    ASP.NET获取客户端的操作系统、浏览器、.NET版本等信息(图)
    ASP.NET 无提示关闭窗口
    VC 使用CryptoAPI计算Hash值:MD5, SHA
    SQL Server 2005 中设置某个用户对某一个数据库有完全控制的权限
    C# 计算文件的MD5值
    VC 获取物理网卡的MAC地址
    哪些免费邮箱不在邮件内容里插广告?
    忆同学
    使用正则表达式获取连接字符串某项的值
  • 原文地址:https://www.cnblogs.com/php5/p/1858870.html
Copyright © 2020-2023  润新知