开发自己PHP MVC框架(一)
本教程翻译自john squibb 的build a php mvc framework in an hour,但有所改动,原文地址:http://johnsquibb.com/tutorials
这个教程可以使大家掌握用mvc模式开发php应用的基本概念。此教程分为三个部分,现在这篇是第一部分。
现在市面上有很多流行的框架供大家使用,但是我们也可以自己动手开发一个mvc框架,采用mvc模式可以大大减少我们开发应用的时间,并且能够更好的组织项目源代码,而且其中的某些模块还可在其它项目中使用。现在我要教大家写一个简单的mvc框架。由于这个项目很简单,轻量,所以可能并不是最佳实践,也不具备安全性,还需要大家在实际应用中完善。
所用技术:php,面向对象开发方法。
开始
立即学习“PHP免费学习笔记(深入)”;
首先在网站根目录下建立三个文件夹
然后在根目录下新建一个文件:
现在项目结构应该像这样
§ 网站根目录
§ index.php
§ models/
§ views/
§ controllers/
index.php是整个web应用的入口点,所有的用户请求都会经过它。我们会写一些代码来把用户请求分派到相应的控制器中,这些控制器存放在controllers文件夹里。之后,我们就可以用下面的方式来实现页面跳转:
首先在index.php中定义网站根目录和网站域名,以便在整个应用中访问。
<?php
//应用的根目录就是index.php的父目录
define("SERVER_ROOT", dirname(__FILE__));
//你的域名.comm 是你的服务器域名
define('SITE_ROOT' , 'http://你的域名.com');定义了网站根目录后,在任何php文件中,都能很方便的引用其它目录的php文件,因为index.php是入口文件,这样就能够在整个应用中访问在它之中定义的这些变量。
在controllers目录下新建一个文件,名字为“router.php",这个文件用来处理所有页面请求。想像一下你家里的路由器,它负责把internet路由到家中的每个电脑。router.php文件将会获取传入到index.php的页面请求,然后把请求分派给不同的控制器(controllers)。
route.php中的代码:
<?php //获取所有请求 $request = $_SERVER['QUERY_STRING'];
上面的地址会在代码中得到”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."<?php
/**
* 定义文档路径
*/
define("SERVER_ROOT", dirname(__FILE__));
define('SITE_ROOT' , 'http://你的域名.com');
/**
* 引入router.php
*/
require_once(SERVER_ROOT . '/controllers/' . 'router.php');
?>
我们会看到如下输出
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."<?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';
}
}
现在,我们需要对新闻控制器稍做一些更改,打开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
//当类初始化时,自动引入相关文件
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.");
}
}
保存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;
}
}?>
public function main(array $getVars)
{
$newsModel = new News_Model;
//获取一篇文章
$article = $newsModel->get_article('test');
print_r($article);
}
如果访问如下网址:
§ http://yourdomain.com/mvc/index.php?news&article=test
你会看到如下输出:
Array ( [title] => Testing [content] => This is just a measly test article. )
现在我们已经有控制器和模型了,只差一个视图。视图是表现层,它是你的应用中,与用户接触最频繁的部分。之前我提到过,视图是提供与业务逻辑分离的用户接口,有很多方法可以做到这个。你可以使用模板引擎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);
}
}
<?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怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号