开发本身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框架(二)》
相关文章
相关标签/搜索