视图类view主要用于页面内容的输出,模板调用等,用在控制器类中,可以使得控制器类把表现和数据结合起来。下面我们来看一下执行流程。
首先,在控制器类中保持着一个view类的对象实例,只要继承自控制器父类的类都可以使用这个实例,所以我们在控制器子类中就可以使用view类实例去很容易的调用模板,输出内容。
看Controller.class.php类的第22行和35行,分别声明了一个实例变量,在构造函数中实例化一个view实例。
视图类view主要用于页面内容的输出,模板调用等,用在控制器类中,可以使得控制器类把表现和数据结合起来。下面我们来看一下执行流程。
首先,在控制器类中保持着一个view类的对象实例,只要继承自控制器父类的类都可以使用这个实例,所以我们在控制器子类中就可以使用view类实例去很容易的调用模板,输出内容。
看Controller.class.php类的第22行和35行,分别声明了一个实例变量,在构造函数中实例化一个view实例。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
namespace
Think
;
/**
* ThinkPHP 控制器基类 抽象类
*/
abstract
class
Controller
{
/**
* 视图实例对象
* @var view
* @access protected
/**
* 控制器参数
* @var config
* @access protected
*/
protected
$config
=
array
(
)
;
/**
* 架构函数 取得模板对象实例
* @access public
*/
public
function
__construct
(
)
{
Hook::
listen
(
'action_begin'
,
$this
->
config
)
;
//实例化视图类
//控制器初始化
if
(
method_exists
(
$this
,
'_initialize'
)
)
$this
->
_initialize
(
)
;
}
|
我们在控制器类中输出模板和内容主要调用控制器类中的display方法,但是我们查看此方法就会发现,此方法里面调用的还是view类的display方法,display的主要实现逻辑还在view类中。在view类的大概67行左后,我们就可以看到这个函数的实现过程,下面来分析一个display方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
/**
* 加载模板和页面输出 可以返回输出内容
* @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行左右。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
|
/**
* 自动定位模板文件
* @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自带的模板引擎,还可以使用其他开源第三方的模板引擎类,具有很好的高扩展性。关于模板引擎解析模板的裸机,请看源码分析