搜索
首页后端开发php教程开发自各儿PHP MVC框架(一)

开发自己PHP MVC框架(一)

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

这个教程可以使大家掌握用mvc模式开发php应用的基本概念。此教程分为三个部分,现在这篇是第一部分。

现在市面上有很多流行的框架供大家使用,但是我们也可以自己动手开发一个mvc框架,采用mvc模式可以大大减少我们开发应用的时间,并且能够更好的组织项目源代码,而且其中的某些模块还可在其它项目中使用。现在我要教大家写一个简单的mvc框架。由于这个项目很简单,轻量,所以可能并不是最佳实践,也不具备安全性,还需要大家在实际应用中完善。

所用技术: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 //应用的根目录就是index.php的父目录define("SERVER_ROOT", dirname(__FILE__));//你的域名.comm 是你的服务器域名define('SITE_ROOT' , 'http://你的域名.com');

定义了网站根目录后,在任何php文件中,都能很方便的引用其它目录的php文件,因为index.php是入口文件,这样就能够在整个应用中访问在它之中定义的这些变量。

设置路由器router.php(转发用户请求到相应控制器)

在controllers目录下新建一个文件,名字为“router.php",这个文件用来处理所有页面请求。想像一下你家里的路由器,它负责把internet路由到家中的每个电脑。router.php文件将会获取传入到index.php的页面请求,然后把请求分派给不同的控制器(controllers)。

route.php中的代码:

<?php //获取所有请求$request = $_SERVER['QUERY_STRING'];

这句代码会获取传入到应用中的请求参数。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 '<br>';$vars = print_r($getVars, TRUE);print "The following GET vars were passed to the page:<pre class="brush:php;toolbar:false">".$vars."
"; 现在我们需要在index.php中引入route.php

<?php /** * 定义文档路径 */define("SERVER_ROOT", dirname(__FILE__));define('SITE_ROOT' , 'http://你的域名.com');/**  * 引入router.php  */ require_once(SERVER_ROOT . '/controllers/' . 'router.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",定义如下的类:

<?php /** * 这个文件处理文章的查询,并提供文章 */class News_Controller{    /**     * $template变量会保存与此控制器相关的"view(视图)"的文件名,不包括.php后缀      */    public $template = 'news';    /**     * 此方法为route.php默认调用     *      * @param array $getVars 传入到index.php的GET变量数组     */    public function main(array $getVars)    {        //测试代码,以后会删除        print "We are in news!";        print '<br/>';        $vars = print_r($getVars, TRUE);        print         (            "The following GET vars were passed to this controller:" .            "<pre class="brush:php;toolbar:false">".$vars."
" ); }}
注意我们把route.php中的测试代码复制过来了,并做了一些修改,我们把它放置在main函数里。现在让我们来修改route.php中的代码:

<?php /** * 此文件会把所有的传入参数分派到相应的控制器中 *///获取请求参数$request = $_SERVER['QUERY_STRING'];//解析请求页面和其它GET变量$parsed = explode('&' , $request);//页面是第一个元素$page = array_shift($parsed);//剩余的为GET变量,也把它们解析出来$getVars = array();foreach ($parsed as $argument){    //split GET vars along '=' symbol to separate variable, values    list($variable , $value) = split('=' , $argument);    $getVars[$variable] = $value;}//构成控制器文件路径$target = SERVER_ROOT . '/controllers/' . $page . '.php';//引入目标文件if (file_exists($target)){    include_once($target);    //修改page变量,以符合命名规范(如$page="news",我们的约定是首字母大写,控制器的话就在后面加上“<strong>_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”,代码如下:

<?php /** * 新闻模型为新闻控制器做复杂的后台操作 */class News_Model{    public function __construct()    {        print 'I am the news model';    }}

现在,我们需要对新闻控制器稍做一些更改,打开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:


<?php /** * 新闻模型为新闻控制器做复杂的后台操作 * */class News_Model{    /**     * 文章数组. key为文章标题, 值为相应的     * 文章。     */    private $articles = array    (        //文章1        'new' => 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代码混合编程一样,但是有一点不同是,我们的业务逻辑已经和视图分离了。看一下如下代码:

                <h1 id="Welcome-to-Our-Website">Welcome to Our Website!</h1>        <hr>        <h2 id="News">News</h2>        <h4 id="data-title-gt">=$data['title'];?></h4>        <p>=$data['content'];?></p>    

注意,嵌入的php标签利用了PHP 快捷操作符。这样就能够把我们的内容直接输出到HTML里面了。在views文件夹里新建一个文件“news.php”,把上述代码拷贝进来。现在我们有了视图文件,但是我们需要一个与视图交互的方法。在models文件夹里新建一个文件“view.php”,添加如下代码:


<?php /** * 在我们的MVC框架中,处理视图的功能 */class View_Model{    /**     * 保存赋给视图模板的变量     */    private $data = array();    /**     * 保存视图渲染状态     */    private $render = FALSE;    /**     * 加载一个视图模板     */    public function __construct($template)    {        //构成完整文件路径        $file = SERVER_ROOT . '/views/' . strtolower($template) . '.php';            if (file_exists($file))        {            /**             * 当模型对象销毁时才能渲染视图             * 如果现在就渲染视图,那么我们就不能给视图模板赋予变量             * 所以此处先保存要渲染的视图文件路径             */            $this->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:

<?php /* *这个文件处理文章的查询,并产生新闻文章*  */ class News_Controller{     /**      * $template变量会保存与此控制器相关的"view(视图)"的文件名,不包括.php后缀     *      */     public $template = 'news';     /**     * 此方法为route.php默认调用     *      * @param array $getVars 传入到index.php的GET变量数组      */      public function main(array $getVars) {         $newsModel = new News_Model;          //获取一片文章          $article = $newsModel->get_article($getVars['article']);          //创建一个视图,并传入该控制器的template变量          $view = new View_Model($this->template);          //把文章数据赋给视图模板          $view->assign('title' , $article['title']);          $view->assign('content' , $article['content']);      }}?>


再加载页面,你就能够看到你的视图模板中的变量,已经被正确的替换掉了。好了,你的简单的MVC框架已经搭建好了,下面我会继续讲《开发自己PHP MVC框架(二)》
声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
您如何防止与会议有关的跨站点脚本(XSS)攻击?您如何防止与会议有关的跨站点脚本(XSS)攻击?Apr 23, 2025 am 12:16 AM

要保护应用免受与会话相关的XSS攻击,需采取以下措施:1.设置HttpOnly和Secure标志保护会话cookie。2.对所有用户输入进行输出编码。3.实施内容安全策略(CSP)限制脚本来源。通过这些策略,可以有效防护会话相关的XSS攻击,确保用户数据安全。

您如何优化PHP会话性能?您如何优化PHP会话性能?Apr 23, 2025 am 12:13 AM

优化PHP会话性能的方法包括:1.延迟会话启动,2.使用数据库存储会话,3.压缩会话数据,4.管理会话生命周期,5.实现会话共享。这些策略能显着提升应用在高并发环境下的效率。

什么是session.gc_maxlifetime配置设置?什么是session.gc_maxlifetime配置设置?Apr 23, 2025 am 12:10 AM

thesession.gc_maxlifetimesettinginphpdeterminesthelifespanofsessiondata,setInSeconds.1)它'sconfiguredinphp.iniorviaini_set().2)abalanceIsiseededeedeedeedeedeedeedto to to avoidperformance andununununununexpectedLogOgouts.3)

您如何在PHP中配置会话名?您如何在PHP中配置会话名?Apr 23, 2025 am 12:08 AM

在PHP中,可以使用session_name()函数配置会话名称。具体步骤如下:1.使用session_name()函数设置会话名称,例如session_name("my_session")。2.在设置会话名称后,调用session_start()启动会话。配置会话名称可以避免多应用间的会话数据冲突,并增强安全性,但需注意会话名称的唯一性、安全性、长度和设置时机。

您应该多久再生一次会话ID?您应该多久再生一次会话ID?Apr 23, 2025 am 12:03 AM

会话ID应在登录时、敏感操作前和每30分钟定期重新生成。1.登录时重新生成会话ID可防会话固定攻击。2.敏感操作前重新生成提高安全性。3.定期重新生成降低长期利用风险,但需权衡用户体验。

如何在PHP中设置会话cookie参数?如何在PHP中设置会话cookie参数?Apr 22, 2025 pm 05:33 PM

在PHP中设置会话cookie参数可以通过session_set_cookie_params()函数实现。1)使用该函数设置参数,如过期时间、路径、域名、安全标志等;2)调用session_start()使参数生效;3)根据需求动态调整参数,如用户登录状态;4)注意设置secure和httponly标志以提升安全性。

在PHP中使用会议的主要目的是什么?在PHP中使用会议的主要目的是什么?Apr 22, 2025 pm 05:25 PM

在PHP中使用会话的主要目的是维护用户在不同页面之间的状态。1)会话通过session_start()函数启动,创建唯一会话ID并存储在用户cookie中。2)会话数据保存在服务器上,允许在不同请求间传递数据,如登录状态和购物车内容。

您如何在子域中分享会议?您如何在子域中分享会议?Apr 22, 2025 pm 05:21 PM

如何在子域名间共享会话?通过设置通用域名的会话cookie实现。1.在服务器端设置会话cookie的域为.example.com。2.选择合适的会话存储方式,如内存、数据库或分布式缓存。3.通过cookie传递会话ID,服务器根据ID检索和更新会话数据。

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

PhpStorm Mac 版本

PhpStorm Mac 版本

最新(2018.2.1 )专业的PHP集成开发工具

螳螂BT

螳螂BT

Mantis是一个易于部署的基于Web的缺陷跟踪工具,用于帮助产品缺陷跟踪。它需要PHP、MySQL和一个Web服务器。请查看我们的演示和托管服务。

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

MinGW - 适用于 Windows 的极简 GNU

MinGW - 适用于 Windows 的极简 GNU

这个项目正在迁移到osdn.net/projects/mingw的过程中,你可以继续在那里关注我们。MinGW:GNU编译器集合(GCC)的本地Windows移植版本,可自由分发的导入库和用于构建本地Windows应用程序的头文件;包括对MSVC运行时的扩展,以支持C99功能。MinGW的所有软件都可以在64位Windows平台上运行。

WebStorm Mac版

WebStorm Mac版

好用的JavaScript开发工具