zoukankan      html  css  js  c++  java
  • 开发自己的PHP MVC框架(一)

    这个教程能够使大家掌握用mvc模式开发php应用的基本概念。此教程分为三个部分。如今这篇是第一部分。

    如今市面上有非常多流行的框架供大家使用。可是我们也能够自己动手开发一个mvc框架。採用mvc模式能够大大降低我们开发应用的时间,并且能够更好的组织项目源码,并且当中的某些模块还可在其他项目中使用。如今我要教大家写一个简单的mvc框架。因为这个项目非常easy,轻量。所以可能并非最佳实践,也不具备安全性。还须要大家在实际应用中完好。

    所用技术:php,面向对象开发方法。


    開始

    首先在站点根文件夹下建立三个文件夹

    • 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 是你的server域名  
    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.       

    假设没有上述输出,请检查你的server配置是否正确。并检查代码是否有错误。

    如今来让我们加入一个页面到我们的站点里,这样就能够让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. ?>  



  • 相关阅读:
    雷林鹏分享:XML to HTML
    雷林鹏分享:XML DOM
    雷林鹏分享:XML 编码
    雷林鹏分享: XML CDATA
    雷林鹏分享:服务器上的 XML
    雷林鹏分享:XML 注意事项
    雷林鹏分享:现实生活中的 XML
    雷林鹏分享:XML 相关技术
    雷林鹏分享:XML
    Android 系统架构图
  • 原文地址:https://www.cnblogs.com/llguanli/p/7301249.html
Copyright © 2011-2022 走看看