开发自己的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
- //应用的根文件夹就是index.php的父文件夹
- define("SERVER_ROOT", dirname(__FILE__));
- //你的域名.comm 是你的server域名
- 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
- //解析$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>".$vars."</pre>";
如今我们须要在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
- )
假设没有上述输出,请检查你的server配置是否正确。并检查代码是否有错误。
- <?
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);
- (
- "The following GET vars were passed to this controller:" .
- "<pre>".$vars."</pre>"
- );
- }
- }
注意我们把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”</strong>,即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';
- }
- }
- 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寻找包括此类的文件的位置。如果你遵循了这篇文章中类和文件名称的命名约定,那么每当你初始化一个类时,你就不必手动去引入包括此类的文件了!
- I am the news model
- <?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代码混合编程一样,可是有一点不同是,我们的业务逻辑已经和视图分离了。看一下例如以下代码:
- <html>
- <head></head>
- <body>
- <h1>Welcome to Our Website!</h1>
- <hr/>
- <h2>News</h2>
- <h4><?
=$data['title'];?></h4>
- <p><?
=$data['content'];?></p>
- </body>
- </html>
注意,嵌入的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']);
- }
- }
- ?>
开发自己的PHP MVC框架(一)的更多相关文章
- 封装:简要介绍自定义开发基于WPF的MVC框架
原文:封装:简要介绍自定义开发基于WPF的MVC框架 一.目的:在使用Asp.net Core时,深感MVC框架作为页面跳转数据处理的方便,但WPF中似乎没有现成的MVC框架,由此自定义开发一套MVC ...
- 分享一实战性开源MVC框架<Linux、Windows跨平台开发so easy>
一.引子 开源地址 https://github.com/564064202/Moon.Mvc 欢迎加入开发 .NET Core微软还在发力,但作为商用还有一段距离,很多开发库尚不能用于.NET ...
- ASP.NET MVC框架开发系列课程 (webcast视频下载)
课程讲师: 赵劼 MSDN特邀讲师 赵劼(网名“老赵”.英文名“Jeffrey Zhao”,技术博客为http://jeffreyzhao.cnblogs.com),微软最有价值专家(ASP.NET ...
- [Java] 使用 Spring 2 Portlet MVC 框架构建 Portlet 应用
转自:http://www.ibm.com/developerworks/cn/java/j-lo-spring2-portal/ Spring 除了支持传统的基于 Servlet 的 Web 开发之 ...
- Android的MVC框架
http://www.cnblogs.com/wanghafan/archive/2012/07/20/2600786.html MVC是当前比较流行的框架,随便Google下,就可以发现几乎所有的应 ...
- 全端开发必备!10个最好的 Node.js MVC 框架
Node.js 是最流行的 JavaScript 服务端平台,它允许建立可扩展的 Web 应用程序.Node.js 包含不同类型的框架,如 MVC 框架.全栈框架.REST API 以及大量的服 ...
- 【原创】三分钟教你学会MVC框架——基于java web开发(2)
没想到我的上一篇博客有这么多人看,还有几位看完之后给我留言加油,不胜感激,备受鼓励,啥都别说了,继续系列文章之第二篇.(如果没看过我第一篇博客的朋友,可以到我的主页上先浏览完再看这篇文章,以免上下文对 ...
- cocos2dx之lua项目开发中MVC框架的简单应用
**************************************************************************** 时间:2015-03-31 作者:Sharin ...
- 开发自己PHP MVC框架(一)
本教程翻译自John Squibb 的Build a PHP MVC Framework in an Hour,但有所改动,原文地址:http://johnsquibb.com/tutorials 这 ...
随机推荐
- Django快速搭建博客
准备工作: 1.Python 2.Django 3.Git 安装Python: 官网下载 安装Django: #安装最新版本的Django $ pip install django #或者指定安装版本 ...
- 洛谷 P1480 A/B Problem
P1480 A/B Problem 题目描述 输入两个整数a,b,输出它们的商(a<=10^5000,b<=10^9) 输入输出格式 输入格式: 两行,第一行是被除数,第二行是除数. 输出 ...
- 20款PHP版WebMail开源项目
20款PHP版WebMail开源项目 如今互联网巨头提供的企业应用套件中邮件托管是必备服务,而且还始终秉承免费的优良光荣传统,最为让人熟识的恐怕非"瘟多死里屋管理中心"和" ...
- 使用dotcloud免费ssh
使用dotcloud免费ssh https://www.dotcloud.com一个项目在线托管网站,注册后可以免费托管两个项目. 注册帐号,在ubuntu中执行下面命令,安装dotcloud环境 s ...
- COGS——C66. [HAOI2004模拟] 数列问题
http://www.cogs.pro/cogs/problem/problem.php?pid=66 ★☆ 输入文件:dfs3.in 输出文件:dfs3.out 简单对比 时间限制:1 ...
- [Angular] Update FormArray with patchValue
Currently, patchValue doesn't support update FormArray. The workarround is you need to empty the for ...
- 无状态会话bean(1)---定义
无状态会话bean用于完毕在单个方法的生命周期内的操作.无状态bean能够实现很多业务操作,可是每一个方法都不能假定不论什么其它的方法会在它之前调用.后半句的意思是如今的你可能不是刚才的你.明天的你可 ...
- 例说linux内核与应用数据通信(一):加入一个系统调用
[版权声明:尊重原创.转载请保留出处:blog.csdn.net/shallnet,文章仅供学习交流,请勿用于商业用途] 应用不能訪问内核的内存空间.为了应用和内核交互信息,内核提供一 ...
- WebApp调用手机相册或摄像头、拨打电话
WebApp调用手机相册或摄像头.拨打电话 一.总结 一句话总结:input标签,指定type为file,选择好对应的accept即可.camera——相机,相应的accept为image : cam ...
- python课程:python3的数字与字符串
一下是基于python2的教程的 python中有 多个数据类型,和,两种字符串类型 他们都是不可变的.