天天看點

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、smartTemplate、Smarty等等。本文中都是使用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編碼後轉到該頁。

三、項目基本配置代碼 關鍵頁代碼執行個體:

  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目錄

$arr_filtrate = array("'", '"', "/"); function var_filtrate($var) {   global $arr_filtrate;     foreach ($arr_filtrate as $value)     {         if (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; } if (count($get)) {     foreach ($post as $get_var) {         if (var_filtrate($get_var)) {             exit('Commit get parameter falsity');         }     } } if (count($post)) {     foreach ($post as $post_var) {         if (var_filtrate($post_var)) {             exit('Commit post parameter falsity');         }     } } 其實,以上過濾的方法也不是最好的,建議參考我的另兩篇防注入文章擷取更好的方法,連結參考附錄。

if (!isset($get[msg])) {    exit('Not commit parameter'); } echo "Error Message: ". $get[msg]; echo "&lt;p><a href='javascript:history.back()'>傳回上一頁</a>";

就是一些錯誤處理的作用,一般出的GET方式傳遞過來的消息都是經過urlencode()過的字元。

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://[email protected]_PASS:DB_HOST/DB_NAME", DB_OPT); if (DB::isErro($db)) {     return $dg-&gt;getMessage(); } $tpl = &new Smarty(); $tpl-&gt;templates_dir = TPL_TEMPLATE_DIR; $tpl-&gt;compile_dir = TPL_COMPILE_DIR; $tpl-&gt;cache_dir  = TPL_CACHE_DIR; $tpl-&gt;configs  = TPL_CONFIGS_DIR; $tpl-&gt;lifetime  = TPL_LIFTTIME; $tpl-&gt;caching  = TPL_CACHEING; $tpl-&gt;left_delimiter = TPL_LEFT_DELIMITER; $tpl-&gt;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表的類來。

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-&gt;cfetchMode = DB_FETCHMODE_DEFAULT;   $this-&gt;cTableName = "topic";   $this-&gt;cDsn = "mysql://".    DB_USER.":".    DB_PASS."@".    DB_HOST."/".    DB_NAME;   $this-&gt;cEncode = "utf8";  }

   function connectDatabase()  {   if (!is_object($cDbPointer))   {    $this-&gt;cDbPointer = DB::connect($this-&gt;cDsn);    if ($this-&gt;cEncode=="utf8") {     $this-&gt;cDbPointer-&gt;query("set names 'utf8'");    }    $this-&gt;cDbPointer-&gt;setFetchMode($this-&gt;cfetchMode);    if (DB::isError($this-&gt;cDbPointer)) {     return false;    }    return $this-&gt;cDbPointer;   }  }

   function closeDatabase()  {   if (is_object($this-&gt;cDbPointer)) {    $this-&gt;cDbPointer-&gt;disconnect();   }  }

   function insert($arr)  {   if(!is_array($arr) || count($arr) == 0){    return false;   }   if("" == $this-&gt;cTableName) return false;   $db = $this-&gt;connectDatabase();   $res = $db-&gt;autoExecute($this-&gt;cTableName,$arr,DB_AUTOQUERY_INSERT);     if(DB::isError($res)){    return $res;   }else{    $insertId = ($db-&gt;getOne("select LAST_INSERT_ID();"));    if($insertId&gt;0) {     return $insertId;    } else {     return true;    }   }  }      function update($id,$arr)  {   if("" != $id && !(is_array($arr))){    return false;   }

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

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

   function delete($id)  {   $db  = $this-&gt;connectDatabase();   $res = $db-&gt;query("DELETE FROM ".$this-&gt;cTableName." WHERE id = '$id'");   if(DB::isError($res)){    return false;   }else{    return true;   }  } }

上面的代碼一個很基本的針對一個表操作的類雛形已經描述出來了,包括連接配接資料庫,基本的資料庫原始操作都有了。你肯定會問,為什麼沒有把 select的操作封裝進去?主要是因為select是SQL裡最複雜的操作,不可能寫那麼通用的一個方法去操作它,是以好不如不寫,自由發揮。

那麼我們需要加上一些基本的功能呢?比如讀取内容、新增加一篇文章等操作,那麼我們還必須在類裡面添加一些方法,比如我們增加提取一篇文章内容、提取指定時間的文章、提取指定類别的文章、統計目前所有文章的總數等操作。

class cmsTopic {  // ...上面已經描述的方法省略

   function getTopicContentById($id, $cols="*")  {   $db = $this-&gt;connectDatabase($this-&gt;cDsn);   $sql = "SELECT $cols FROM ". $this-&gt;cTableName ." WHERE id = '$id'";   $result = $db-&gt;getAll($sql);   if (DB::isError($result)) {    return $result-&gt;getMessage();   } else {    $db-&gt;disconnect();    return $result;   }  }

   function getTopicBySpecifyTime($startTime=0, $endTime=0, $cols="*")  {   $db = $this-&gt;connectDatabase($this-&gt;cDsn);   $start = ($startTime == 0) ? "" : "WHERE addtime &gt; $startTime";   $end = ($endTime == 0) ? "" : "AND addtime < $startTime";   $sql = "SELECT $cols FROM ". $this->cTableName ." ".$start ." ".$end;   $result = $db-&gt;getAll($sql);   if (DB::isError($result)) {    return $result-&gt;getMessage();   } else {    $db-&gt;disconnect();    return $result;   }  }

   function getTopicByType($type, $cols="*")  {   $db = $this-&gt;connectDatabase($this-&gt;cDsn);   $sql = "SELECT $cols FROM ". $this-&gt;cTableName ." WHERE type = '$type'";   $result = $db-&gt;getAll($sql);   if (DB::isError($result)) {    return $result-&gt;getMessage();   } else {    $db-&gt;disconnect();    return $result;   }  }      function getTopicSum($type="")  {   $db = $this-&gt;connectDatabase($this-&gt;cDsn);   $typeStr = ($type == "") ? "" : " WHERE type = '$type'";   $sql = "SELECT count(id) FROM ". $this-&gt;cTableName ." ".$typeStr;   $result = $db-&gt;getOne($sql);   if (DB::isError($result)) {    return $result-&gt;getMessage();   } else {    $db-&gt;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層的資料的。

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-&gt;assign("topic", $topicList); $tpl-&gt;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="" target="_blank" rel="external nofollow" >{$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

        var $_debug = false;         //其他屬性省略...     // }}}

        // {{{ constructor

        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)多子產品通信

如果項目足夠龐大,門戶級的應用,那麼肯定涉及到多欄目/子產品之間的通信

&gt;&gt;&gt;&gt;&gt;&gt; 附錄:(文章相應連結)&lt;&lt;&lt;&lt;&lt;&lt;

 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/developerworks/cn/linux/sdk/php/pear4/index.html

《模闆引擎SMARTY》   http://www-128.ibm.com/developerworks/cn/linux/l-smart/index.html

《MVC模式、類封裝還是黑客代碼》  http://www-128.ibm.com/developerworks/cn/linux/sdk/php/php_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/developerworks/cn/linux/sdk/php/pear3/index.html

《PHP 編碼規範》    http://www.phpe.net/html/php_coding_standard_cn.html

《CVS使用手冊》    http://www.chedong.com/tech/cvs_card.html