视图类view主要用于页面内容的输出,模板调用等,用在控制器类中,可以使得控制器类把表现和数据结合起来。下面我们来看一下执行流程。

首先,在控制器类中保持着一个view类的对象实例,只要继承自控制器父类的类都可以使用这个实例,所以我们在控制器子类中就可以使用view类实例去很容易的调用模板,输出内容。

看Controller.class.php类的第22行和35行,分别声明了一个实例变量,在构造函数中实例化一个view实例。

// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2014 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace Think;
/**
* ThinkPHP 控制器基类 抽象类
*/
abstract class Controller { /**
* 视图实例对象
* @var view
* @access protected
*/
protected $view = null; /**
* 控制器参数
* @var config
* @access protected
*/
protected $config = array(); /**
* 架构函数 取得模板对象实例
* @access public
*/
public function __construct() {
Hook::listen('action_begin',$this->config);
//实例化视图类
$this->view = Think::instance('Think\View');
//控制器初始化
if(method_exists($this,'_initialize'))
$this->_initialize();
}

我们在控制器类中输出模板和内容主要调用控制器类中的display方法,但是我们查看此方法就会发现,此方法里面调用的还是view类的display方法,display的主要实现逻辑还在view类中。在view类的大概67行左后,我们就可以看到这个函数的实现过程,下面来分析一个display方法。

/**
* 加载模板和页面输出 可以返回输出内容
* @access public
* @param string $templateFile 模板文件名
* @param string $charset 模板输出字符集
* @param string $contentType 输出类型
* @param string $content 模板输出内容
* @param string $prefix 模板缓存前缀
* @return mixed
*/
public function display($templateFile='',$charset='',$contentType='',$content='',$prefix='') {
G('viewStartTime');
// 视图开始标签
Hook::listen('view_begin',$templateFile);
// 解析并获取模板内容
$content = $this->fetch($templateFile,$content,$prefix);
// 输出模板内容
$this->render($content,$charset,$contentType);
// 视图结束标签
Hook::listen('view_end');
}

我们可以看到在display方法中也是分别调用多个函数来协作完成模板处理工作的。

我们来整理分析一下流程。

dispaly方法接受一个templateFile参数,调用parseTemplate方法根据这个参数去侦测模板文件的位置,结合主题组合出一个模板的地址,执行view_parse标签行为,在行为类里面去调用模板引擎的fetch方法去解析模板,返回编译后的内容。

所以我们知道,对于视图类来说,大概分为以下几步,第一是根据参数去侦测出模板文件的地址,然后调用模板引擎去编译模板文件,形成编译缓存php文件,然后执行php文件返回内容。

下面我们来分析一下tp是怎样根据用户传进来的参数来侦测模板文件的地址的。核心代码逻辑在view类的140行左右。

/**
* 自动定位模板文件
* @access protected
* @param string $template 模板文件规则
* @return string
*/
public function parseTemplate($template='') {
//如果$template是一个文件地址的话,那么就直接返回该地址。这是最简单的一种使用方式。
//这里的文件地址是一个相对文件路径地址,要注意模板文件位置是相对于项目的入口文件
if(is_file($template)) {
return $template;
}
//获取模板文件的分隔符,模板文件CONTROLLER_NAME与ACTION_NAME之间的分割符
$depr = C('TMPL_FILE_DEPR');
//将冒号替换成分隔符
$template = str_replace(':', $depr, $template);
// 获取当前主题名称
$theme = $this->getTemplateTheme();
/*
我们来看一下tp的模板侦测逻辑。
首先tp要求我们传入一个地址表达式。格式如下:
[模块@][控制器:][操作] 比如: m@c:a 表示m模块下的c控制器下的a方法
我们只要给此方法传入模块,控制器和方法三个参数,这个方法就能给我们侦测出对应的模板文件地址。
具体来说,他会先定义出截止到模块这个阶段的目录,然后在得到控制器到最后的模板文件地址,最后组合起来形成最终的文件地址。
1:先定义模块的目录
首先解析地址表达式,如果有@,表示传入了模块地址,解析她,赋值给module变量。
我们的控制器文件夹和方法存放在哪里呢?
如果定义了视图目录,就存放在视图目录中,如果没有定义,就看看是否定义了模板路径,如果定义了就存放在该路径下的对应模块目录下,如果没有定义模板路径,默认就存放在应用文件夹下的对应模块文件夹下的默认视图层下。
最后我们会得到一个THEME_PATH表示控制器模板存放的目录。如果有主题的话加上主题 */
// 获取当前模块
$module = MODULE_NAME;
if(strpos($template,'@')){ // 跨模块调用模版文件
list($module,$template) = explode('@',$template);
}
// 获取当前主题的模版路径
if(!defined('THEME_PATH')){
if(C('VIEW_PATH')){ // 模块设置独立的视图目录
$tmplPath = C('VIEW_PATH');
}else{ // 定义TMPL_PATH 改变全局的视图目录到模块之外
$tmplPath = defined('TMPL_PATH')? TMPL_PATH.$module.'/' : APP_PATH.$module.'/'.C('DEFAULT_V_LAYER').'/';
}
define('THEME_PATH', $tmplPath.$theme);
} // 分析模板文件规则
/*
然后分析地址表达式中的模板规则,如果地址表达式为空,或者只是传了一个模块名,那么这里的template就为空,那么默认的地址就是默认控制器下的默认方法
*/
if('' == $template) {
// 如果模板文件名为空 按照默认规则定位
$template = CONTROLLER_NAME . $depr . ACTION_NAME;
}elseif(false === strpos($template, $depr)){
$template = CONTROLLER_NAME . $depr . $template;
}
//最后结合一下得到最后的模板文件地址
$file = THEME_PATH.$template.C('TMPL_TEMPLATE_SUFFIX');
if(C('TMPL_LOAD_DEFAULTTHEME') && THEME_NAME != C('DEFAULT_THEME') && !is_file($file)){
// 找不到当前主题模板的时候定位默认主题中的模板
$file = dirname(THEME_PATH).'/'.C('DEFAULT_THEME').'/'.$template.C('TMPL_TEMPLATE_SUFFIX');
}
return $file;
}

到这里为止,我们已经根据用户传入的模板地址表达式得到了模板文件地址,接下来就是调用模板引擎来解析这个模板。在view类的fetch方法中并没有直接调用模板引擎template类的的方法去解析模板,而是去调用了一个view_parse标签,在这个标签上绑定了行为模式扩展类ParseTemplateBehavior,模板的解析就是在这个类的run方法中进行的,这个类总我们不仅而已使用tp自带的模板引擎,还可以使用其他开源第三方的模板引擎类,具有很好的高扩展性。关于模板引擎解析模板的裸机,请看源码分析8

Thinkphp源码分析系列(九)–视图view类的更多相关文章

  1. Thinkphp源码分析系列–开篇

    目前国内比较流行的php框架由thinkphp,yii,Zend Framework,CodeIgniter等.一直觉得自己在php方面还是一个小学生,只会用别人的框架,自己也没有写过,当然不是自己不 ...

  2. spring源码分析系列 (8) FactoryBean工厂类机制

    更多文章点击--spring源码分析系列 1.FactoryBean设计目的以及使用 2.FactoryBean工厂类机制运行机制分析 1.FactoryBean设计目的以及使用 FactoryBea ...

  3. Thinkphp源码分析系列(二)–引导类

    在上一章我们说到,ThinkPHP.php在设置完框架所需要的变量和调教好环境后,在最后调用了  Think\Think::start();  即Think命名空间中的Think类的静态方法start ...

  4. Thinkphp源码分析系列(四)–Dispatcher类

    下面我们来分析一下Thinkphp中的url解析和路由调度类.此类主要功能是 // +--------------------------------------------------------- ...

  5. Thinkphp源码分析系列(五)–系统钩子实现

    Thinkphp的插件机制主要依靠的是Hook.class.php这个类,官方文档中在行为扩展也主要依靠这个类来实现.下面我们来具体看看tp是怎么利用这个类来实现行为扩展的. 首先,行为扩展是什么?有 ...

  6. 【Spring源码分析系列】启动component-scan类扫描加载过程

    原文地址:http://blog.csdn.net/xieyuooo/article/details/9089441/ 在spring 3.0以上大家都一般会配置一个Servelet,如下所示: &l ...

  7. Thinkphp源码分析系列(七)–控制器基类

    在mvc模式中,c代表的就是控制器,是是应用程序中处理用户交互的部分.通常控制器负责从视图读取数据,控制用户输入,并向模型发送数据.控制器是沟通视图和模型的桥梁,他接受用户请求,并调用模型层去处理用户 ...

  8. Thinkphp源码分析系列(三)– App应用程序类

    // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO ...

  9. Thinkphp源码分析系列(一)–入口文件

    正如官方文档上所介绍的,thinkphp使用单一入口,所有的请求都从默认的index.php文件进入.当然不是说一定非得从index.php进入,这应该取决于你的服务器配置,一般服务器都会有默认的首页 ...

随机推荐

  1. JS 控制CSS样式表

    JS控制CSS所使用的方法: <style> .rule{ display: none; } </style> 你想要改变把他的display属性由none改为inline.  ...

  2. JSBinding+Bridge.NET限制

    限制: 框架代码不可以访问逻辑代码.这是最基本的. 框架里的函数 f 带数组参数时,逻辑代码调用 f 的话,数组只能做为输入,也就是说,如果在框架函数 f 里修改了数组的内容,那么逻辑代码是无法取得新 ...

  3. Linux----LVM扩容磁盘空间

    Linux系统LVM扩容一个分区相对于Windows来说没有那么直观,但是熟悉命令后,扩容起来也是蛮方便的. 扩容场景如下: [root@rhel06 ~]# df -Th Filesystem Ty ...

  4. Sql Server Analysis Service 转换为UnknownMember的正确设置 (转载)

    转载: http://www.cnblogs.com/OpenCoder/p/4754447.html#commentform 在SSAS中事实表数据被归类到为UnknownMember 的时候分为两 ...

  5. 应用C#和SQLCLR编写SQL Server用户定义函数

    摘要: 文档阐述使用C#和SQLCLR为SQL Server编写用户定义函数,并演示用户定义函数在T-SQL中的应用.文档中实现的 Base64 编码解码函数和正则表达式函数属于标量值函数,字符串分割 ...

  6. nginx下rewrite参数超过9个的解决方法

    nginx 在处理多于9个参数的时候,是采用重命名的方法来实现的: /?m?([0-9,]*)h?(\d*)a?([0-9,]*)c?(\d*)s?(x?f?(?P<f>[0-9,]*)/ ...

  7. 浅谈mybatis中的#和$的区别

    1. #将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号.如:order by #user_id#,如果传入的值是111,那么解析成sql时的值为order by "111&qu ...

  8. 简单的聊天室代码php+swoole

    php swoole+websocket 客户端代码 <!DOCTYPE html> <html> <head> <title></title&g ...

  9. Best Practices for Performance_1、2 memory、Tips 性能和小的优化点、 onTrimMemory

    http://developer.android.com/training/articles/memory.htmlhttp://developer.android.com/tools/debuggi ...

  10. 实践总结 - 不可错过的Angular应用技巧

    angular的核心思想是通过数据驱动一切,其他东西都是数据的延伸. 套用Javascript一切皆对象的思想,在angular中可以说一切皆数据. 关于项目构建 (1) requirejs以及Yeo ...