PHP的MVC模式实现原理分析(一相简单的MVC框架范例)


Posted in PHP onApril 29, 2014

他们的工作原理大家应该也比较感兴趣,下面我说说一个mvc框架长什么样。

路由机制

在互联网我们都是通过url提供服务,因此不同的url有不同的服务。用户访问不同的页面也就获得了不同的服务。那么我们的服务是如何通过url来区分不同的服务呢。

我们的web程序就要通过url寻找到不同的文件,进行不同的业务逻辑处理。我们的路由机制就是根据url,寻找到对应的controller,和action,然后由action进行具体的业务逻辑处理。

一个简单的controller

//定义一个controller
class UserControler extends Controller{
     //定义一个action方法,注意一定是public的
     public function index(){
          // do business code
     }
}

具体的对应规则不同的框架映射不同。以下是CodeIgniter框架的URL路由,它会尽力的尝试各种的可能,来分析URL的情况。

文件路径/system/core/URI.php

// 看看是否是从命令行运行的
if (php_sapi_name() == 'cli' or defined('STDIN')){
    $this->_set_uri_string($this->_parse_cli_args());
    return;
}// 首先尝试 REQUEST_URI 这个适应大部分的情况
if ($uri = $this->_detect_uri()){
    $this->_set_uri_string($uri);
    return;
}
// 看看PATH_INFO变量是否存在?nginx需要配置
// Note: some servers seem to have trouble with getenv() so we'll test it two ways
$path = (isset($_SERVER['PATH_INFO'])) ? $_SERVER['PATH_INFO'] : @getenv('PATH_INFO');
if (trim($path, '/') != '' && $path != "/".SELF){
    $this->_set_uri_string($path);
    return;
}
// 没有PATH_INFO,看看 QUERY_STRING?
$path =  (isset($_SERVER['QUERY_STRING'])) ? $_SERVER['QUERY_STRING'] : @getenv('QUERY_STRING');
if (trim($path, '/') != ''){
    $this->_set_uri_string($path);
    return;
}
//尝试去从 $_GET 获取信息
if (is_array($_GET) && count($_GET) == 1 && trim(key($_GET), '/') != ''){
    $this->_set_uri_string(key($_GET));
    return;
}
// 尽力了,放弃了路由
$this->uri_string = '';
return;

通过上面的尝试,接下来就是如何利用路由机制加载正确的controller了。

Controller加载机制

我们来看看Codeigniter框架是如何加载到controller并且调用action的。

在/system/core/Codeigniter.php中有如下的代码。Codeigniter在这之前会根据$_SERVER['PATH_INFO]里面的值来进行赋值(这个都是靠自己的设定的,默认的话CI他会有许多的if分支进行判断)。

//大约在250行
include(APPPATH.'controllers/'.$RTR->fetch_directory().$RTR->fetch_class().'.php');$class  = $RTR->fetch_class();
$method = $RTR->fetch_method();
//大约在308行
$CI = new $class();
//大约在359行
call_user_func_array(array(&$CI, $method), array_slice($URI->rsegments, 2));

就这样,通过这个就调用到了我们的controller及其方法了,接下来就是编写自己的业务逻辑代码了。

视图view的显示

当我们的业务逻辑代码写完后,就需要页面的展示了。很多常见的MVC框架在页面的调用是这么写的。

//controller中action的方法
public function index(){
    // ... 许多的业务逻辑代码
    $data = array('name'=>'abc', 'age'=>12, .... );
    return $this->render('view/path/file.html',$data);
}

接着在视图文件view/path/file.html里写上一下代码。
<div>
姓名 : <?=$name ?>
年龄 : <?php echo $age; ?>
</div>

这段如何将数据渲染到视图中,这段代码以前我一直很好奇,现在我明白了,我们来看看是如何实现的。
protected function render($template, array $var = array() )
{
    extract($var);   // 抽取数组中的变量
    ob_end_clean (); //关闭顶层的输出缓冲区内容
    ob_start ();     // 开始一个新的缓冲区
    require TEMPLATE_ROOT . $template . '.html';  //加载视图view
    $content = ob_get_contents ();             // 获得缓冲区的内容
    ob_end_clean ();           // 关闭缓冲区    //ob_end_flush();      // 这个是直接输出缓冲区的内容了,不用再次缓存起来。
    ob_start();            //开始新的缓冲区,给后面的程序用
    return $content;       // 返回文本,此处也可以字节echo出来,并结束代码。
}

在这短短的几行代码中,全都是精华,就是这些非常重要的,全是php的内置函数,接下来我们来具体分析分析。

看看第一个extract($var)。这个函数从数组中将变量导入到当前的符号表。刚刚就将$data数组里面的name、age抽取出来,这样就可以在视图view中使用$name $age。更详细的请参考http://www.php.net/manual/zh/function.extract.php

第二个ob_end_clean()的作用是关闭顶层的缓冲区,为了是之前的程序不小心echo出的一些文字给清楚了,为了下一行的重新开辟一块缓冲区。

第三个ob_start()是开启一块新的缓冲区,为了是将视图的内容放到缓冲区。当然了,缓冲区有一定的大小,如果内容超出了缓冲区的设定值,那么会自动的发送给server。

第四个require file,这个就是第一个参数,根据自己的规则去加载视图的文件。其中文件里可以夹杂php、html的代码。你在这个render()函数声明的任何局部变量或者这里能访问到的任何全局变量,都可以在require的file文件中访问到。

第五个$content = ob_get_contents ()很重要,是为了将缓冲区的内容取出来,但不清除它。

第七个ob_start()是重新开启一个缓冲区,为了是下面的程序需要使用缓冲区。有写框架可能不用对$content的内容进行操作了,那么直接ob_end_flush()将缓冲区的内容输出出来就行了。

这个是一个很简单的展示视图的过程。如果直接使用这个不方便对视图view进行模块化,因此一些框架都不会这么直接用的。

我们从这个函数也可以看到程序有点类似程序中断保护现场的感觉。只不过中断保护现场会先保存数据,然后在返回的时候恢复回来。这里只有关闭上一个缓冲区,开启一个新的缓冲区,关闭这个缓冲哦过去,开启另外一个缓冲区。

至此,我们看到一个简单的PHP的MVC框架。如果你有兴趣可以自己开发一个MVC框架,或者更深入点的HMVC。

PHP 相关文章推荐
利用PHP和AJAX创建RSS聚合器的代码
Mar 13 PHP
php采集速度探究总结(原创)
Apr 18 PHP
php mysql 判断update之后是否更新了的方法
Jan 10 PHP
php文件打包 下载之使用PHP自带的ZipArchive压缩文件并下载打包好的文件
Jun 13 PHP
自己在做项目过程中学到的PHP知识收集
Aug 20 PHP
PHP 实现类似js中alert() 提示框
Mar 18 PHP
PHP之正则表达式捕获组与非捕获组(详解)
Jul 29 PHP
PHP插件PHPMailer发送邮件功能
Feb 28 PHP
php格式文件打开的四种方法
Feb 24 PHP
thinkphp5框架结合mysql实现微信登录和自定义分享链接与图文功能示例
Aug 13 PHP
Laravel项目中timeAgo字段语言转换的改善方法示例
Sep 16 PHP
thinkPHP+LayUI 流加载实现功能
Sep 27 PHP
php中使用getimagesize获取图片、flash等文件的尺寸信息实例
Apr 29 #PHP
PHP include任意文件或URL介绍
Apr 29 #PHP
php调用google接口生成二维码示例
Apr 28 #PHP
php将字符串转化成date存入数据库的两种方式
Apr 28 #PHP
php使用array_rand()函数从数组中随机选择一个或多个元素
Apr 28 #PHP
删除html标签得到纯文本可处理嵌套的标签
Apr 28 #PHP
php通过ajax实现双击table修改内容
Apr 28 #PHP
You might like
PHP:风雨欲来 路在何方?
2006/10/09 PHP
php&amp;java(二)
2006/10/09 PHP
PHP的范围解析操作符(::)的含义分析说明
2011/07/03 PHP
用mysql_fetch_array()获取当前行数据的方法详解
2013/06/05 PHP
Yii框架ACF(accessController)简单权限控制操作示例
2019/04/26 PHP
Laravel获取当前请求的控制器和方法以及中间件的例子
2019/10/11 PHP
Laravel框架数据库迁移操作实例详解
2020/04/06 PHP
一些mootools的学习资源
2010/02/07 Javascript
javascript encodeURI和encodeURIComponent的比较
2010/04/03 Javascript
高性能WEB开发 flush让页面分块,逐步呈现 flush让页面分块,逐步呈现
2010/06/19 Javascript
深入理解JavaScript定时机制
2010/10/29 Javascript
IE6中使用position导致页面变形的解决方案(js代码)
2011/01/09 Javascript
点击A元素触发B元素的事件在IE8下会识别成A元素
2014/09/04 Javascript
20个实用的JavaScript技巧分享
2014/11/28 Javascript
JS实现评价的星星功能
2017/08/20 Javascript
简单谈谈关于Angular Cli打包的事
2017/09/05 Javascript
Vue项目部署的实现(阿里云+Nginx代理+PM2)
2019/03/26 Javascript
基于NodeJS开发钉钉回调接口实现AES-CBC加解密
2020/08/20 NodeJs
python显示生日是星期几的方法
2015/05/27 Python
python线程池(threadpool)模块使用笔记详解
2017/11/17 Python
python 图片二值化处理(处理后为纯黑白的图片)
2019/11/01 Python
CSS3混合模式mix-blend-mode/background-blend-mode简介
2018/03/15 HTML / CSS
Sofmap官网:日本著名的数码电器专卖店
2017/05/19 全球购物
iPad和Surface Pro蓝牙键盘:Brydge
2018/11/10 全球购物
橄榄树药房:OLIVEDA
2019/09/01 全球购物
欧姆龙医疗保健与医疗产品:Omron Healthcare
2020/02/10 全球购物
法国在线药房:DoctiPharma
2020/10/21 全球购物
关于元旦的广播稿
2014/02/16 职场文书
送餐员岗位职责范本
2014/02/21 职场文书
2014年医务科工作总结
2014/12/18 职场文书
高考作弊检讨书1500字
2015/02/16 职场文书
三八节活动简报
2015/07/20 职场文书
毕业生入职感言
2015/07/31 职场文书
2016年教师党员承诺书范文
2016/03/24 职场文书
php中pcntl_fork详解
2021/04/01 PHP
Python+OpenCV实现图片中的圆形检测
2022/04/07 Python