開發本身PHP MVC框架(一)

本教程翻譯自John Squibb 的Build a PHP MVC Framework in an Hour,但有所改動,原文地址:http://johnsquibb.com/tutorialsphp

這個教程可使你們掌握用mvc模式開發php應用的基本概念。此教程分爲三個部分,如今這篇是第一部分。html

如今市面上有不少流行的框架供你們使用,可是咱們也能夠本身動手開發一個mvc框架,採用mvc模式能夠大大減小咱們開發應用的時間,而且可以更好的組織項目源代碼,並且其中的某些模塊還可在其它項目中使用。如今我要教你們寫一個簡單的mvc框架。因爲這個項目很簡單,輕量,因此可能並非最佳實踐,也不具有安全性,還須要你們在實際應用中完善。前端

所用技術:php,面向對象開發方法。web

開始數據庫

首先在網站根目錄下創建三個文件夾編程

 

  • models
  • views
  • controllers

 

而後在根目錄下新建一個文件:數組

 

  • index.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]  view plain copy
 
  1. <?php  
  2.   
  3. //應用的根目錄就是index.php的父目錄  
  4. define("SERVER_ROOT", dirname(__FILE__));  
  5.   
  6. //你的域名.comm 是你的服務器域名  
  7. define('SITE_ROOT' , 'http://你的域名.com');  

 

定義了網站根目錄後,在任何php文件中,都能很方便的引用其它目錄的php文件,由於index.php是入口文件,這樣就可以在整個應用中訪問在它之中定義的這些變量。

設置路由器router.php(轉發用戶請求到相應控制器)

在controllers目錄下新建一個文件,名字爲「router.php",這個文件用來處理全部頁面請求。想像一下你家裏的路由器,它負責把internet路由到家中的每一個電腦。router.php文件將會獲取傳入到index.php的頁面請求,而後把請求分派給不一樣的控制器(controllers)。

route.php中的代碼:

 

[php]  view plain copy
 
  1. <?php  
  2.   
  3. //獲取全部請求  
  4. $request = $_SERVER['QUERY_STRING'];  


這句代碼會獲取傳入到應用中的請求參數。QUERY_STRING就是」?「後面的全部字符串。

 

 

  • http://你的域名.com/index.php?page1
上面的地址會在代碼中獲得」page1&action=login「,爲了把page1和後面的參數分開,咱們須要在route.php中繼續加入下列代碼:
[php]  view plain copy
 
  1. //解析$request變量,獲得用戶請求的頁面(page1)和其它GET變量(&分隔的變量)如一個請求http://你的域名.com/index.php?page1&article=buildawebsite,則被解析爲array("page1", "article=buildawebsite")  
  2. $parsed = explode('&' , $request);  
  3.   
  4. //用戶請求的頁面,如上面的page1,爲$parsed第一個變量,shift以後,數組爲array("article=buildawebsite")  
  5. $page = array_shift($parsed);  
  6.   
  7. //剩下的爲GET變量,把它們解析出來  
  8. $getVars = array();  
  9. foreach ($parsed as $argument)  
  10. {  
  11.     //用"="分隔字符串,左邊爲變量,右邊爲值  
  12.     list($variable , $value) = split('=' , $argument);  
  13.     $getVars[$variable] = $value;  
  14. }  
  15.   
  16. //這是測試語句,一下子會刪除  
  17. print "The page your requested is '$page'";  
  18. print '<br/>';  
  19. $vars = print_r($getVars, TRUE);  
  20. print "The following GET vars were passed to the page:<pre>".$vars."</pre>";      
  21.       

如今咱們須要在index.php中引入route.php

[php]  view plain copy
 
  1. <?php  
  2. /** 
  3.  * 定義文檔路徑 
  4.  */  
  5. define("SERVER_ROOT", dirname(__FILE__));  
  6. define('SITE_ROOT' , 'http://你的域名.com');  
  7. /**  
  8.  * 引入router.php  
  9.  */  
  10.  require_once(SERVER_ROOT . '/controllers/' . 'router.php');  
  11. ?>  
若是順利的話,你能夠打開瀏覽器輸入:

  • http://你的域名.com/index.php?news&article=howtobuildaframework
咱們會看到以下輸出
[html]  view plain copy
 
  1. The page you requested is 'news'  
  2.     The following GET vars were passed to the page:  
  3.   
  4.     Array  
  5.     (  
  6.         [article] => howtobuildaframework  
  7.     )  
  8.       

若是沒有上述輸出,請檢查你的服務器配置是否正確,並檢查代碼是否有錯誤。
如今來讓咱們添加一個頁面到咱們的網站裏,這樣就可讓router.php來產生一個頁面,而不是直接輸出上面的信息。
 
建立一個控制器(controller)
在controllers文件夾裏新建一個文件名爲「news.php",定義以下的類:
[php]  view plain copy
 
  1. <?php  
  2. /** 
  3.  * 這個文件處理文章的查詢,並提供文章 
  4.  */  
  5. class News_Controller  
  6. {  
  7.     /** 
  8.      * $template變量會保存與此控制器相關的"view(視圖)"的文件名,不包括.php後綴  
  9.      */  
  10.     public $template = 'news';  
  11.   
  12.     /** 
  13.      * 此方法爲route.php默認調用 
  14.      *  
  15.      * @param array $getVars 傳入到index.php的GET變量數組 
  16.      */  
  17.     public function main(array $getVars)  
  18.     {  
  19.         //測試代碼,之後會刪除  
  20.         print "We are in news!";  
  21.         print '<br/>';  
  22.         $vars = print_r($getVars, TRUE);  
  23.         print   
  24.         (  
  25.             "The following GET vars were passed to this controller:" .  
  26.             "<pre>".$vars."</pre>"  
  27.         );  
  28.     }  
  29. }  

注意咱們把route.php中的測試代碼複製過來了,並作了一些修改,咱們把它放置在main函數裏。如今讓咱們來修改route.php中的代碼:
[php]  view plain copy
 
  1. <?php  
  2. /** 
  3.  * 此文件會把全部的傳入參數分派到相應的控制器中 
  4.  */  
  5.   
  6. //獲取請求參數  
  7. $request = $_SERVER['QUERY_STRING'];  
  8.   
  9. //解析請求頁面和其它GET變量  
  10. $parsed = explode('&' , $request);  
  11.   
  12. //頁面是第一個元素  
  13. $page = array_shift($parsed);  
  14.   
  15. //剩餘的爲GET變量,也把它們解析出來  
  16. $getVars = array();  
  17. foreach ($parsed as $argument)  
  18. {  
  19.     //split GET vars along '=' symbol to separate variable, values  
  20.     list($variable , $value) = split('=' , $argument);  
  21.     $getVars[$variable] = $value;  
  22. }  
  23.   
  24. //構成控制器文件路徑  
  25. $target = SERVER_ROOT . '/controllers/' . $page . '.php';  
  26.   
  27. //引入目標文件  
  28. if (file_exists($target))  
  29. {  
  30.     include_once($target);  
  31.   
  32.     //修改page變量,以符合命名規範(如$page="news",咱們的約定是首字母大寫,控制器的話就在後面加上「<strong>_Controller」</strong>,即News_Controller)  
  33.     $class = ucfirst($page) . '_Controller';  
  34.   
  35.     //初始化對應的類  
  36.     if (class_exists($class))  
  37.     {  
  38.         $controller = new $class;  
  39.     }  
  40.     else  
  41.     {  
  42.         //類的命名正確嗎?  
  43.         die('class does not exist!');  
  44.     }  
  45. }  
  46. else  
  47. {  
  48.     //不能在controllers找到此文件  
  49.     die('page does not exist!');  
  50. }  
  51.   
  52. //一但初始化了控制器,就調用它的默認函數main();  
  53. //把get變量傳給它  
  54. $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」,代碼以下:

[php]  view plain copy
 
  1. <?php  
  2. /** 
  3.  * 新聞模型爲新聞控制器作複雜的後臺操做 
  4.  */  
  5. class News_Model  
  6. {  
  7.     public function __construct()  
  8.     {  
  9.         print 'I am the news model';  
  10.     }  
  11. }  
如今,咱們須要對新聞控制器稍作一些更改,打開controllers裏的news.php,把News_Controller類的main函數的代碼改成以下,這樣,咱們就會在「News_Model」初始化時,看到打印在屏幕上的信息:
[php]  view plain copy
 
  1. public function main(array $getVars)  
  2. {  
  3.     $newsModel = new News_Model;  
  4. }  
如今刷新頁面,你會看到:
[php]  view plain copy
 
  1. Fatal error: Class 'News_Model' not found in /var/www/mvc/controllers/news.php on line xx  
  2.       
等一下,這不是咱們想要的結果!咱們正試圖去加載一個不存在的類。那麼緣由就是咱們並無引入/models/news.php文件。爲了解決這個問題,讓們從新來看一下router.php,而後在它的頂部添加一些代碼:
[php]  view plain copy
 
  1. //當類初始化時,自動引入相關文件  
  2. function __autoload($className)  
  3. {  
  4.     //解析文件名,獲得文件的存放路徑,如News_Model表示存放在models文件夾裏的news.php(這裏是做者的命名約定)  
  5.     list($filename , $suffix) = split('_' , $className);  
  6.   
  7.     //構成文件路徑  
  8.     $file = SERVER_ROOT . '/models/' . strtolower($filename) . '.php';  
  9.   
  10.     //獲取文件  
  11.     if (file_exists($file))  
  12.     {  
  13.         //引入文件  
  14.         include_once($file);          
  15.     }  
  16.     else  
  17.     {  
  18.         //文件不存在  
  19.         die("File '$filename' containing class '$className' not found.");      
  20.     }  
  21. }  
這個函數重載了PHP內置的autoload函數。當咱們試圖去初始化一個不存在的類時,這個‘魔術方法’容許咱們攔截php所執行的動做。經過使用__autoload函數,咱們可以告訴php尋找包含此類的文件的位置。假設你遵循了這篇文章中類和文件名的命名約定,那麼每當你初始化一個類時,你就沒必要手動去引入包含此類的文件了!
保存route.php,再刷新一次瀏覽器,你會看到:
[html]  view plain copy
 
  1. I am the news model  
讓咱們在新聞模型類裏定義一些函數來提供文章。如今,咱們只簡單的定義了一個數組,並保存一些文章,而後提供一個函數,讓控制器從中根據標題獲取一篇文章。修改models/news.php:
 
[php]  view plain copy
 
  1. <?php  
  2. /** 
  3.  * 新聞模型爲新聞控制器作複雜的後臺操做 
  4.  * 
  5.  */  
  6. class News_Model  
  7. {  
  8.     /** 
  9.      * 文章數組. key爲文章標題, 值爲相應的 
  10.      * 文章。 
  11.      */  
  12.     private $articles = array  
  13.     (  
  14.         //文章1  
  15.         'new' => array  
  16.         (  
  17.             'title' => 'New Website' ,  
  18.             'content' => 'Welcome to the site! We are glad to have you here.'  
  19.         )  
  20.         ,  
  21.         //2  
  22.         'mvc' => array  
  23.         (  
  24.             'title' => 'PHP MVC Frameworks are Awesome!' ,  
  25.             'content' => 'It really is very easy. Take it from us!'  
  26.         )  
  27.         ,  
  28.         //3  
  29.         'test' => array  
  30.         (  
  31.             'title' => 'Testing' ,  
  32.             'content' => 'This is just a measly test article.'  
  33.         )  
  34.     );  
  35.   
  36.     public function __construct()  
  37.     {  
  38.     }  
  39.   
  40.     /** 
  41.      * 根據標題獲取文章 
  42.      *  
  43.      * @param string $articleName 
  44.      *  
  45.      * @return array $article 
  46.      */  
  47.     public function get_article($articleName)  
  48.     {  
  49.         //從數組中獲取文章  
  50.         $article = $this->articles[$articleName];  
  51.       
  52.         return $article;  
  53.     }  
  54.   
  55. }?>  
如今修改controllers/news.php中的main函數:
[php]  view plain copy
 
  1. public function main(array $getVars)  
  2. {  
  3.     $newsModel = new News_Model;  
  4.       
  5.     //獲取一篇文章  
  6.     $article = $newsModel->get_article('test');  
  7.       
  8.     print_r($article);  
  9. }  
如今咱們並無考慮過濾用戶輸入的問題,由於咱們如今只是爲了儘快讓你們掌握PHP MVC的基本內容,因此咱們如今沒必要太關心這些。
若是訪問以下網址:

§ http://yourdomain.com/mvc/index.php?news&article=test

你會看到以下輸出:

 

[html]  view plain copy
 
  1. Array ( [title] => Testing [content] => This is just a measly test article. )   

建立視圖(VIEW

 

如今咱們已經有控制器和模型了,只差一個視圖。視圖是表現層,它是你的應用中,與用戶接觸最頻繁的部分。以前我提到過,視圖是提供與業務邏輯分離的用戶接口,有不少方法能夠作到這個。你可使用模板引擎Smarty或其它相似的。你也能夠寫一個本身的模板引擎,但那確定是至關艱鉅的任務。最後,你可使用原生php視圖。

對於目前來講,php視圖足夠了。這個就像之前php與html代碼混合編程同樣,可是有一點不一樣是,咱們的業務邏輯已經和視圖分離了。看一下以下代碼:

 

[php]  view plain copy
 
  1. <html>  
  2.     <head></head>  
  3.     <body>  
  4.         <h1>Welcome to Our Website!</h1>  
  5.         <hr/>  
  6.         <h2>News</h2>  
  7.         <h4><?=$data['title'];?></h4>  
  8.         <p><?=$data['content'];?></p>  
  9.     </body>  
  10. </html>  

 

注意,嵌入的php標籤利用了PHP 快捷操做符。這樣就可以把咱們的內容直接輸出到HTML裏面了。在views文件夾裏新建一個文件「news.php」,把上述代碼拷貝進來。如今咱們有了視圖文件,可是咱們須要一個與視圖交互的方法。在models文件夾裏新建一個文件「view.php」,添加以下代碼:

 

 

[php]  view plain copy
 
  1. <?php  
  2. /** 
  3.  * 在咱們的MVC框架中,處理視圖的功能 
  4.  */  
  5. class View_Model  
  6. {  
  7.     /** 
  8.      * 保存賦給視圖模板的變量 
  9.      */  
  10.     private $data = array();  
  11.   
  12.     /** 
  13.      * 保存視圖渲染狀態 
  14.      */  
  15.     private $render = FALSE;  
  16.   
  17.     /** 
  18.      * 加載一個視圖模板 
  19.      */  
  20.     public function __construct($template)  
  21.     {  
  22.         //構成完整文件路徑  
  23.         $file = SERVER_ROOT . '/views/' . strtolower($template) . '.php';  
  24.       
  25.         if (file_exists($file))  
  26.         {  
  27.             /** 
  28.              * 當模型對象銷燬時才能渲染視圖 
  29.              * 若是如今就渲染視圖,那麼咱們就不能給視圖模板賦予變量 
  30.              * 因此此處先保存要渲染的視圖文件路徑 
  31.              */  
  32.             $this->render = $file;  
  33.         }          
  34.     }  
  35.   
  36.     /** 
  37.      * 接受從控制器賦予的變量,並保存在data數組中 
  38.      *  
  39.      * @param $variable 
  40.      * @param $value 
  41.      */  
  42.     public function assign($variable , $value)  
  43.     {  
  44.         $this->data[$variable] = $value;  
  45.     }  
  46.   
  47.     public function __destruct()  
  48.     {  
  49.         //把類中的data數組變爲該函數的局部變量,以方便在視圖模板中使用  
  50.         $data = $this->data;  
  51.       
  52.         //渲染視圖  
  53.         include($this->render);  
  54.     }  
  55. }  

如今,最後一件要作的事就是從News_Controller里加載視圖。修改controllers/news.php:

 

[php]  view plain copy
 
  1. <?php  
  2. /* 
  3.  *這個文件處理文章的查詢,併產生新聞文章*  
  4.  */  
  5.  class News_Controller{   
  6.     /**  
  7.      * $template變量會保存與此控制器相關的"view(視圖)"的文件名,不包括.php後綴 
  8.      *  
  9.      */  
  10.      public $template = 'news';   
  11.     /** 
  12.      * 此方法爲route.php默認調用 
  13.      *  
  14.      * @param array $getVars 傳入到index.php的GET變量數組  
  15.      */   
  16.      public function main(array $getVars) {  
  17.          $newsModel = new News_Model;   
  18.          //獲取一片文章   
  19.          $article = $newsModel->get_article($getVars['article']);   
  20.          //建立一個視圖,並傳入該控制器的template變量   
  21.          $view = new View_Model($this->template);   
  22.          //把文章數據賦給視圖模板   
  23.          $view->assign('title' , $article['title']);   
  24.          $view->assign('content' , $article['content']);   
  25.      }  
  26. }  
  27. ?>  
再加載頁面,你就可以看到你的視圖模板中的變量,已經被正確的替換掉了。好了,你的簡單的MVC框架已經搭建好了,下面我會繼續講《開發本身PHP MVC框架(二)》
相關文章
相關標籤/搜索