張志瑜
PHP是時下流行的動態(tài)網(wǎng)頁開發(fā)語言之一,受到包括FACEBOOK等行業(yè)巨頭在內(nèi)的眾多企業(yè)青睞,其實際應用非常之廣。掌握基本的PHP語言有利于計算機專業(yè)學生日后從事網(wǎng)站維護管理的需要。然而在實際中,工業(yè)生產(chǎn)的潮流已經(jīng)不甘于使用半成品的CMS(內(nèi)容管理系統(tǒng))或者WordPress,而是使用更具個性化的PHP框架來進行快速開發(fā),掌握簡單的幾個函數(shù)已經(jīng)難以處理日益豐富的網(wǎng)站開發(fā)維護需要。業(yè)界的發(fā)展潮流傾向代碼和界面分離的做法使得不僅PHP程序員,甚至連前端的頁面美工也需要對PHP有較為深刻的認識。
主流的PHP框架多數(shù)采用MVC框架,MVC框架把網(wǎng)頁請求分為Controller(控制),Model(模型)和View(視圖)三部分,通過減低內(nèi)容模塊之間的耦合度,從而簡化開發(fā)流程和提高代碼復用。用PHP語言來實現(xiàn)MVC框架可以看作是繼PHP語言入門的后續(xù)課程和進階,通過實現(xiàn)MVC框架來使得學生對PHP和MySQL數(shù)據(jù)庫有更加具體和深刻的認識,從建立項目,解決問題中更感性地掌握PHP的用途,比面向過程式的開發(fā)更加有趣。雖然現(xiàn)成的MVC框架(Yii,CodeIgniter,CakePHP)為數(shù)不少,但是掌握獨自開發(fā)一個完整的MVC框架能夠使得開發(fā)者更加了解整套框架的運作,也能夠使得開發(fā)者更快地掌握運用其他框架開發(fā)。
對于使用PHP語言來實現(xiàn)完整的MVC網(wǎng)站框架,需要開發(fā)者使用現(xiàn)有的工具解決好不同的類之間的控制和處理。我們大致可以根據(jù)右圖提供的思路來建立MVC模型的雛形。雖然沒有專業(yè)框架擁有很多處理類(緩存類,安全類,輸入輸出處理類),但是最基本的MVC框架然仍不能想象得過于簡單,很多問題需要我們不斷地思考和解決。
1 獲取用戶請求
用戶發(fā)出的網(wǎng)頁請求通過URL來傳遞,傳遞的思路就是把需要訪問的頁面分解成多個$_GET參數(shù)(如:http://localhost/account/list,控制類是account,輸出list頁面)。實際上網(wǎng)頁服務器會解析成網(wǎng)站目錄下account目錄下的list頁面。在實際和預設兩者的歧義中,我們考慮使用.htaccess來實現(xiàn)請求的轉(zhuǎn)化。.htaccess文件是Apache服務器中的一個配置文件,它負責相關目錄下的網(wǎng)絡配置,通過.htaccess文件,可以實現(xiàn)網(wǎng)頁重定向,自定義錯誤頁面,改進文件擴展名,特定用戶訪問權(quán)限設置,配置默認文檔等功能。.htaccess建立在網(wǎng)站站點的目錄里而不是在Apache安裝目錄下,它具有分布式配置的方式,在目錄中可以放置一個包含一個或多個指令的文件方式,以及作用于此目錄及其所有子目錄。最普遍的例子是使用一下語句來實現(xiàn)用戶請求的轉(zhuǎn)義:
REWRITERULE
^(.*)$ INDEX.PHP?URL=$1
經(jīng)過.htaccess處理之后,PHP會把用戶的請求轉(zhuǎn)化成$_GET[‘url]變量。最普遍的做法卻存在一個小問題,用戶輸入包含后綴名的完整URL的時候(如http://localhost/account/list.php),PHP則會把list.php作為整體而不把.php作為后綴名看待。后續(xù)操作會因為找不到list.php(只有l(wèi)ist方法)而報錯。所以我們可以通過小小的修改來對請求進行細化:
REWRITERULE
^([A-ZA-Z0-9\/\-_]+)\.?([A-ZA-Z]+)?$ INDEX.PHP?URL = $1&EXTENSION = $2
經(jīng)過修改后,PHP會把.php作為$_GET[‘extention]參數(shù),則更有利于后續(xù)操作針對不同的后綴名來進行不同的處理。
2 對用戶請求進行分析
無論是使用單個文件或者獨立出一個路由的類,思路都是對$_GET[‘url]進行分析分拆,使用explode_array函數(shù)提取不同的部分。再以call_user_func_array()來進行控制類和方法類的調(diào)用。除了控制類參數(shù)部分和方法類參數(shù)部分,其余部分可以數(shù)組的方式調(diào)用模型類。
$RT = new Router($request);
Session::init();
$controller = $RT->getController();
$controller = new $controller;
$method = $RT->getMethod();
$params = $RT->getParams();
if (empty($params)) {
call_user_func(array($controller, $method));
} else {
call_user_func_array(array($controller, $method), $params);
}
3 控制類的設計
控制類(Controller)是MVC處理模式的主要部分,常用思路是把單一類別的網(wǎng)頁作為類名(例如與用戶有關的可以定義為account類),具體的某個頁面就是類中的方法(例如查看單個用戶的信息,如account類中的profile函數(shù))。我們一般先建立一個名為Controller的抽象類,統(tǒng)一定義初始化函數(shù)(__construct())和主函數(shù)(index(),某個類的默認頁面),具體的類則通通繼承這個抽象類。
控制類不能簡單的包含模型類(model)和視圖類(view)兩個元素,因為在常用的網(wǎng)站開發(fā)中往往需要Session,網(wǎng)頁分頁等支持。所以我們采用流行框架中用到load類的方法。值得一提的是,CodeIgniter不是使用Loader類而是使用指針函數(shù)來加載其他類,采用這種方法的話需要額外建立一個全局變量來存儲加載了的類的指針數(shù)組。另外還可以額外加載registry類來存儲加載函數(shù),registry類作為控制類的一個元素。解決了存儲加載類,我們則用家里Loader類來加載不同模塊(Session,Input,Output)等。
模型類(Model類)與視圖類(View類)不同,正如上圖所示,模型類(Model類)并不是每個控制類都需要調(diào)用的(如只顯示靜態(tài)頁面,或者調(diào)用緩存頁面),所以通常的做法是把模型類也歸納在Loader類的調(diào)用范疇。
視圖類(View類)則是必須包含的元素,因為每個控制類最終目錄都是要通過調(diào)用頁面(視圖類,PHP頁面文件)來顯示。View類既可以也歸納為Loader類調(diào)用,也可以獨立在Loader類之外。需要開發(fā)者考慮調(diào)用的時候考慮調(diào)用單個PHP頁面還是包含額外的PHP頁面模塊。開發(fā)者可以根據(jù)設計思路來做出不同的選擇。
abstract class Controller
{
protected $_registry;
protected $load;
public function __construct()
{
$this->_registry = Registry::getInstance();
$this->load = new Load;
}
abstract public function index();
final public function __get($key)
{
if ($return = $this->_registry->$key) {
return $return;
}
return false;
}
}
4 模型類的設計
模型類(Model類)的作用在與對數(shù)據(jù)進行處理,把數(shù)據(jù)處理的結(jié)果和分析數(shù)據(jù)返回給視圖類(View類)進行顯示。與控制類(Controller類)相同,我們需要先建立模型的抽象類。主要是對數(shù)據(jù)庫的加載,眾所周知數(shù)據(jù)庫對一個動態(tài)網(wǎng)頁是多么的重要,所以數(shù)據(jù)庫的處理我們也必須使用獨立的數(shù)據(jù)庫類(Database類)。Database類可以是對PDO類的繼承,這樣方便我們快速調(diào)整不同的數(shù)據(jù)庫(MySQL,SQLite等),對于只專注于某種數(shù)據(jù)庫的應用,我們可以使用其特定的類(如MySQLi類)。
在Database類的方法實現(xiàn)中,我們建議繁瑣的多次SQL操作描述成較為容易理解的操作集合函數(shù)。此外我們也可以直接使用現(xiàn)有的ORM庫(如Redbean)來代替Database類,把ORM作為模型類(Model類)的元素。
對象關系映射(Object Relational Mapping,簡稱ORM)是一種為了解決面向?qū)ο笈c關系數(shù)據(jù)庫存在的互不匹配的現(xiàn)象的技術(shù)。 簡單的說,ORM是通過使用描述對象和數(shù)據(jù)庫之間映射的元數(shù)據(jù),將程序中的對象自動持久化到關系數(shù)據(jù)庫中。本質(zhì)上就是將數(shù)據(jù)從一種形式轉(zhuǎn)換到另外一種形式。ORM提供了所有SQL語句的生成,代碼人員遠離了數(shù)據(jù)庫概念。從一個概念需求(例如一個HQL)映射為一個SQL語句,并不需要什么代價,連1%的性能損失都沒有。真正的性能損失在映射過程中,更具體地講,是在對象實例化的過程中。
abstract class Model
{
protected $_registry;
protected $load;
public function __construct()
{
require_once CONFIG_PATH.'db.php';
$this->db = new Database($CFG['db']);
}
}
5 視圖類的設計
與其說是視圖類(View類),不如說是PHP頁面就更為準確,視圖類(View類)可以看做基本的PHP頁面,控制類(Controller類)調(diào)用(require(),require_once())這些頁面進行顯示出最后效果。關鍵是需要對模型類(Model類)產(chǎn)生的數(shù)據(jù)進行處理在傳遞給View。
在MVC框架的過程中,我們可以使用Output類來輔助視圖語句的輸出,例如把HTML的form代碼拆分成幾個echo()函數(shù)。
6 使用現(xiàn)有的PHP庫
我們已經(jīng)建立一個簡單的MVC框架雛形,而且可以在這個雛形之上不斷的改進和賦予更加高級的特性和框架功能(Cookies,Security等)。我們發(fā)現(xiàn)原來很多功能,函數(shù)都可以手工去一一打造,然而在快速開發(fā)或團隊開發(fā)的時候,重復的快發(fā)基本的功能模塊,除了加深開發(fā)者的基本功之外,對開發(fā)幫助不大。我們的框架可以和容易的去調(diào)用現(xiàn)有得第三方的PHP庫(如PEAR)。但是我們也需要通過類似于spl_autoload_register()去改進PHP的自動加載函數(shù)。
通過對MVC框架的實現(xiàn),開發(fā)者可以加深對PHP語言認識和開拓網(wǎng)站開發(fā)的思路。針對于PHP初學者來說,本案例就是一門PHP動態(tài)網(wǎng)站開發(fā)的進階課程。從中可以學習到PHP語言課學習中很少用到的方法:全局函數(shù),指針函數(shù),類,繼承,抽象類等。也為日后的CodeIgniter等框架的學習打下基礎。
[責任編輯:湯靜]