開發自己PHP MVC架構(一)
本教程翻譯自John Squibb 的Build a PHP MVC Framework in an Hour,但有所改動,原文地址:http://johnsquibb.com/tutorials
這個教程可以使大家掌握用mvc模式開發php應用的基本概念。此教程分為三個部分,現在這篇是第一部分。
現在市面上有很多流行的架構供大家使用,但是我們也可以自己動手開發一個mvc架構,採用mvc模式可以大大減少我們開發應用的時間,並且能夠更好的組織項目原始碼,而且其中的某些模組還可在其它項目中使用。現在我要教大家寫一個簡單的mvc架構。由於這個項目很簡單,輕量,所以可能並不是最佳實務,也不具備安全性,還需要大家在實際應用中完善。
所用技術:php,物件導向開發方法。
開始
首先在網站根目錄下建立三個檔案夾
然後在根目錄下建立一個檔案:
現在項目結構應該像這樣
§ 網站根目錄
§ index.php
§ models/
§ views/
§ controllers/
index.php是整個web應用的進入點,所有的使用者請求都會經過它。我們會寫一些代碼來把使用者請求指派到相應的控制器中,這些控制器存放在controllers檔案夾裡。之後,我們就可以用下面的方式來實現頁面跳轉:
- http://你的網域名稱.com/index.php?page1
- http://你的網域名稱.com/index.php?page2
- http://你的網域名稱.com/index.php?page3
設定前端控制器index.php
首先在index.php中定義網站根目錄和網站網域名稱,以便在整個應用中訪問。
定義了網站根目錄後,在任何php檔案中,都能很方便的引用其它目錄的php檔案,因為index.php是入口檔案,這樣就能夠在整個應用中訪問在它之中定義的這些變數。
設定路由器router.php(轉寄使用者請求到相應控制器)
在controllers目錄下建立一個檔案,名字為“router.php",這個檔案用來處理所有頁面請求。想像一下你家裡的路由器,它負責把internet路由到家中的每個電腦。router.php檔案將會擷取傳入到index.php的頁面請求,然後把請求指派給不同的控制器(controllers)。
route.php中的代碼:
這句代碼會擷取傳入到應用中的請求參數。QUERY_STRING就是”?“後面的所有字串。
- http://你的網域名稱.com/index.php?page1
上面的地址會在代碼中得到”page1&action=login“,為了把page1和後面的參數分開,我們需要在route.php中繼續加入下列代碼:
//解析$request變數,得到使用者請求的頁面(page1)和其它GET變數(&分隔的變數)如一個請求http://你的網域名稱.com/index.php?page1&article=buildawebsite,則被解析為array("page1", "article=buildawebsite")$parsed = explode('&' , $request);//使用者請求的頁面,如上面的page1,為$parsed第一個變數,shift之後,數組為array("article=buildawebsite")$page = array_shift($parsed);//剩下的為GET變數,把它們解析出來$getVars = array();foreach ($parsed as $argument){ //用"="分隔字串,左邊為變數,右邊為值 list($variable , $value) = split('=' , $argument); $getVars[$variable] = $value;}//這是測試語句,一會兒會刪除print "The page your requested is '$page'";print '
';$vars = print_r($getVars, TRUE);print "The following GET vars were passed to the page:".$vars."
";
現在我們需要在index.php中引入route.php
如果順利的話,你可以開啟瀏覽器輸入:
- http://你的網域名稱.com/index.php?news&article=howtobuildaframework
我們會看到如下輸出
The page you requested is 'news'The following GET vars were passed to the page:Array([article] => howtobuildaframework)
如果沒有上述輸出,請檢查你的伺服器配置是否正確,並檢查代碼是否有錯誤。
現在來讓我們添加一個頁面到我們的網站裡,這樣就可以讓router.php來產生一個頁面,而不是直接輸出上面的資訊。
建立一個控制器(controller)
在controllers檔案夾裡建立一個檔案名稱為“news.php",定義如下的類:
'; $vars = print_r($getVars, TRUE); print ( "The following GET vars were passed to this controller:" . "".$vars."
" ); }}
注意我們把route.php中的測試代碼複製過來了,並做了一些修改,我們把它放置在main函數裡。現在讓我們來修改route.php中的代碼:
_Controller”,即News_Controller) $class = ucfirst($page) . '_Controller'; //初始化對應的類 if (class_exists($class)) { $controller = new $class; } else { //類的命名正確嗎? die('class does not exist!'); }}else{ //不能在controllers找到此檔案 die('page does not exist!');}//一但初始化了控制器,就調用它的預設函數main();//把get變數傳給它$controller->main($getVars);?>
再次訪問http://你的網域名稱.com/index.php?news&article=howtobuildaframework,你將會看到從News_Controller列印出來的資訊。注意,我們現在用die()來處理錯誤,我們可以用其它更好的錯誤處理來規制它,但現在使用die()足夠了,試試訪問其它頁面如http://你的網域名稱.com/index.php?books,你會看到"page does not exist!"錯誤。建立一個Model(模型)完善News_Controller。假設我們有一些新聞片段來供讀者閱讀,那麼就需要News_Controller這個控制器去調用一個模型來抓取相關的新聞片段,無論它們是儲存在資料庫還是檔案裡。在models檔案夾裡建立一個檔案,“news.php”,代碼如下:
現在,我們需要對新聞控制器稍做一些更改,開啟controllers裡的news.php,把News_Controller類的main函數的代碼改為如下,這樣,我們就會在“News_Model”初始化時,看到列印在螢幕上的資訊:
public function main(array $getVars){ $newsModel = new News_Model;}
現在重新整理頁面,你會看到:Fatal error: Class 'News_Model' not found in /var/www/mvc/controllers/news.php on line xx
等一下,這不是我們想要的結果!我們正試圖去載入一個不存在的類。那麼原因就是我們並沒有引入/models/news.php檔案。為瞭解決這個問題,讓們重新來看一下router.php,然後在它的頂部添加一些代碼://當類初始化時,自動引入相關檔案function __autoload($className){ //解析檔案名稱,得到檔案的存放路徑,如News_Model表示存放在models檔案夾裡的news.php(這裡是作者的命名規範) list($filename , $suffix) = split('_' , $className); //構成檔案路徑 $file = SERVER_ROOT . '/models/' . strtolower($filename) . '.php'; //擷取檔案 if (file_exists($file)) { //引入檔案 include_once($file); } else { //檔案不存在 die("File '$filename' containing class '$className' not found."); }}
這個函數重載了PHP內建的autoload函數。當我們試圖去初始化一個不存在的類時,這個‘魔術方法’允許我們攔截php所執行的動作。通過使用__autoload函數,我們能夠告訴php尋找包含此類的檔案的位置。假設你遵循了這篇文章中類和檔案名稱的命名規範,那麼每當你初始化一個類時,你就不必手動去引入包含此類的檔案了!儲存route.php,再重新整理一次瀏覽器,你會看到:
I am the news model
讓我們在新聞模型類裡定義一些函數來提供文章。現在,我們只簡單的定義了一個數組,並儲存一些文章,然後提供一個函數,讓控制器從中根據標題擷取一篇文章。修改models/news.php:
array ( 'title' => 'New Website' , 'content' => 'Welcome to the site! We are glad to have you here.' ) , //2 'mvc' => array ( 'title' => 'PHP MVC Frameworks are Awesome!' , 'content' => 'It really is very easy. Take it from us!' ) , //3 'test' => array ( 'title' => 'Testing' , 'content' => 'This is just a measly test article.' ) ); public function __construct() { } /** * 根據標題擷取文章 * * @param string $articleName * * @return array $article */ public function get_article($articleName) { //從數組中擷取文章 $article = $this->articles[$articleName]; return $article; }}?>
現在修改controllers/news.php中的main函數:public function main(array $getVars){ $newsModel = new News_Model; //擷取一篇文章 $article = $newsModel->get_article('test'); print_r($article);}
現在我們並沒有考慮過濾使用者輸入的問題,因為我們現在只是為了儘快讓大家掌握PHP MVC的基本內容,所以我們現在不必太關心這些。
如果訪問如下網址:
§ http://yourdomain.com/mvc/index.php?news&article=test
你會看到如下輸出:
Array ( [title] => Testing [content] => This is just a measly test article. )
建立視圖(VIEW)現在我們已經有控制器和模型了,只差一個視圖。視圖是表現層,它是你的應用中,與使用者接觸最頻繁的部分。之前我提到過,視圖是提供與商務邏輯分離的使用者介面,有很多方法可以做到這個。你可以使用模板引擎Smarty或其它類似的。你也可以寫一個自己的模板引擎,但那肯定是相當艱巨的任務。最後,你可以使用原生php視圖。
對於目前來說,php視圖足夠了。這個就像以前php與html代碼混合編程一樣,但是有一點不同是,我們的商務邏輯已經和視圖分離了。看一下如下代碼:
Welcome to Our Website!
News
注意,嵌入的php標籤利用了PHP 快捷操作符。這樣就能夠把我們的內容直接輸出到HTML裡面了。在views檔案夾裡建立一個檔案“news.php”,把上述代碼拷貝進來。現在我們有了視圖檔案,但是我們需要一個與視圖互動的方法。在models檔案夾裡建立一個檔案“view.php”,添加如下代碼:
render = $file; } } /** * 接受從控制器賦予的變數,並儲存在data數組中 * * @param $variable * @param $value */ public function assign($variable , $value) { $this->data[$variable] = $value; } public function __destruct() { //把類中的data數組變為該函數的局部變數,以方便在視圖模板中使用 $data = $this->data; //渲染視圖 include($this->render); }}
現在,最後一件要做的事就是從News_Controller裡載入視圖。修改controllers/news.php:get_article($getVars['article']); //建立一個視圖,並傳入該控制器的template變數 $view = new View_Model($this->template); //把文章資料賦給視圖模板 $view->assign('title' , $article['title']); $view->assign('content' , $article['content']); }}?>
再載入頁面,你就能夠看到你的視圖模板中的變數,已經被正確的替換掉了。好了,你的簡單的MVC架構已經搭建好了,下面我會繼續講《開發自己PHP MVC架構(二)》