概述
行为(Behavior)是ThinkPHP扩展机制中比较关键的一项扩展,行为既可以独立调用,也可以绑定到某个标签中进行侦听,官方提出的CBD模式中行为也占了主要的地位,可见行为在ThinkPHP框架中意义非凡。这里指的行为是一个比较抽象的概念,你可以想象成在应用执行过程中的一个动作或者处理,在框架的执行流程中,各个位置都可以有行为产生,例如路由检测是一个行为,静态缓存是一个行为,用户权限检测也是行为,大到业务逻辑,小到浏览器检测、多语言检测等等都可以当做是一个行为,甚至说你希望给你的网站用户的第一次访问弹出Hello,world!这些都可以看成是一种行为,行为的存在让你无需改动框架和应用,而在外围通过扩展或者配置来改变或者增加一些功能。
而不同的行为之间也具有位置共同性,比如,有些行为的作用位置都是在应用执行前,有些行为都是在模板输出之后,我们把这些行为发生作用的位置称之为标签(位),当应用程序运行到这个标签的时候,就会被拦截下来,统一执行相关的行为,类似于AOP编程中的“切面”的概念,给某一个切面绑定相关行为就成了一种类AOP编程的思想。
系统标签位
系统核心提供的标签位置包括下面几个(按照执行顺序排列):app_init | 应用初始化标签位 |
path_info | PATH_INFO检测标签位 |
route_check | 路由检测标签位 |
app_begin | 应用开始标签位 |
action_name | 操作方法名标签位 |
action_begin | 控制器开始标签位 |
view_begin | 视图输出开始标签位 |
view_template | 视图模板解析标签位 |
view_parse | 视图解析标签位 |
view_filter | 视图输出过滤标签位 |
view_end | 视图输出结束标签位 |
action_end | 控制器结束标签位 |
app_end | 应用结束标签位 |
除了这些系统内置标签之外,开发人员还可以在应用中添加自己的应用标签,例如我们给应用的公共Action类CommonAction添加一个action_init标签位。
Class CommonAction extends Action{
Public function _initialize(){
tag('action_init'); // 添加action_init 标签
}
}
注意:tag函数用于设置某个标签位,可以传入并且只接受一个参数,如果需要传入多个参数,请使用数组,该参数为引用传值,所以只能传入变量。Public function _initialize(){
tag('action_init'); // 添加action_init 标签
}
}
核心行为
新版系统的很多核心功能也是采用行为扩展组装的,虽然在开发过程中可能感觉不到这种变化,但正是由于这种架构设计的改变,让新版变得更加灵活和易扩展,这是一个里程碑式的改变,对于满足项目日益纷繁复杂的需求和定制底层框架提供了更多的方便和可能性。框架核心内置的行为包括如下:
行为名称 | 说明 | 对应标签位置 |
---|---|---|
checkRoute | 路由检测行为,完成内置的路由功能 | route_check |
LocationTemplate | 模板定位行为,完成模板文件自动定位和输出规则 | view_template |
ParseTemplate | 模板文件解析,并支持第三方模板引擎驱动 | view_parse |
ShowPageTrace | 页面Trace功能行为,完成页面Trace功能 | view_end |
ShowRuntime | 运行时间显示行为,完成运行时间显示 | view_filter |
TokenBuild | 令牌生成行为,完成表单令牌的自动生成 | view_filter |
ReadHtmlCache | 读取静态缓存行为 | app_init |
WriteHtmlCache | 生成静态缓存行为 | view_filter |
行为定义
行为类的命名采用:行为名称(驼峰法,首字母大写)+Behavior 行为类的定义方式如下:class TestBehavior extends Behavior {
// 行为参数定义
protected $options = array(
'test_param' => false, // 行为参数 会转换成TEST_PARAM配置参数
);
// 行为扩展的执行入口必须是run
public function run(&$params){
if(C('TEST_PARAM')) {
echo 'RUNTEST BEHAVIOR '.$params;
}
}
}
行为类必须继承Behavior类,并且必须定义执行入口方法run(&$params); 由于行为的调用机制影响,run方法不需要任何返回值,所有返回都通过引用返回。每个行为类可以定义options属性,该属性中的参数会自动转换成单独配置参数,也就是说该参数可以用全局的C方法直接读取或者修改,同时也意味着行为中定义的options参数只是提供一个默认值,你完全可以在配置文件中或者使用C方法动态配置生效,而不需要手动传入行为类。这是Behavior类提供的方便和全局配置对接的一个特性,当然你完全可以不依赖这个特性。// 行为参数定义
protected $options = array(
'test_param' => false, // 行为参数 会转换成TEST_PARAM配置参数
);
// 行为扩展的执行入口必须是run
public function run(&$params){
if(C('TEST_PARAM')) {
echo 'RUNTEST BEHAVIOR '.$params;
}
}
}
因此,上面的run方法实现也可以更改为:
public function run(&$params){
if($this->test_param) {
echo 'RUNTEST BEHAVIOR '.$params;
}
}
run方法的参数只有一个,但支持传入数组。if($this->test_param) {
echo 'RUNTEST BEHAVIOR '.$params;
}
}
行为类是支持自动加载的,其放置的位置可以包括如下位置,可以根据需要自行选择:
位置 | 所在目录 | 说明 |
核心行为目录 | ThinkPHP/Lib/Behavior/ | 通常用于放置一些核心的内置行为 |
扩展行为目录 | Extend/Behavior/ | 这是ThinkPHP扩展的行为扩展目录,第三方的扩展大都在此 |
项目的行为目录 | 项目目录/Lib/Behavior/ | 用于项目自身的行为扩展 |
独立分组的行为目录 | 独立分组目录/Behavior/ | 如果采用了独立分组的话,也可以把行为类放在这里 |
行为绑定
行为定义完成后,就需要绑定到某个标签位置才能生效,否则是不会执行的。我们需要在项目的行为定义文件tags.PHP文件中进行行为和标签的位置定义,格式如下:标签名称包括我们前面列出的系统标签和应用中自己定义的标签名称,比如你需要在app_init标签位置定义一个CheckLangBehavior行为类的话,可以使用:
return array(
'app_init'=>array('CheckLang'),
);
可以给一个标签位定义多个行为,行为的执行顺序就是定义的先后顺序,例如:'app_init'=>array('CheckLang'),
);
return array(
'app_init'=>array('CheckLang','CronRun'),
);
默认情况下tags.PHP中定义的行为会并入系统行为一起执行,也就是说如果系统的行为定义中app_init标签中已经定义了其他行为,则会首先执行系统行为扩展中定义的行为,然后再执行项目行为中定义的行为,如果你希望项目tags中定义的行为完全替换系统的行为扩展,可以使用:'app_init'=>array('CheckLang','CronRun'),
);
return array(
'app_init'=>array('CheckLang','CronRun','_overlay'=>1),
);
应用行为的定义没有限制,你可以把一个行为绑定到多个标签位置执行,例如:'app_init'=>array('CheckLang','CronRun','_overlay'=>1),
);
return array(
'app_begin'=>array('Test'), // 在app_begin 标签位添加Test行为
'app_end'=>array('Test'), // 在app_end 标签位添加Test行为
);
为了避免混淆,自定义的行为扩展尽量不要和核心的行为扩展重名。'app_begin'=>array('Test'), // 在app_begin 标签位添加Test行为
'app_end'=>array('Test'), // 在app_end 标签位添加Test行为
);
除了定义tags行为配置文件之外,框架还提供了动态添加行为到标签位的方法,例如我们可以使用下面的方式添加Test行为到app_end标签位,而无需在tags文件中添加定义:
add_tag_behavior('app_end','Test');
表示把Test行为添加到app_end标签位的最后,你可以把这个代码放到项目的公共函数文件中甚至直接放到行为类的最后(如果你确定这个行为扩展只有你的项目会用到的话)。单独执行
有时候,行为的调用不一定要放到标签才能调用,如果需要的话,我们可以在控制器中直接调用行为。例如,我们可以把用户权限检测封装成一个行为类,例如:class AuthCheckBehavior extends Behavior {
// 行为参数定义
protected $options = array(
'USER_AUTH_ON' =>false, // 是否开启用户认证
'USER_AUTH_ID' => 'user_id', // 定义用户的id为权限认证字段
);
// 行为扩展的执行入口必须是run
public function run(&$return){
if(C('USER_AUTH_ON ')) {
// 进行权限认证逻辑 如果认证通过 $return = true;
// 否则用halt输出错误信息
}
}
定义了AuthCheck行为后,然后在_initialize方法中直接用下面的方式调用:// 行为参数定义
protected $options = array(
'USER_AUTH_ON' =>false, // 是否开启用户认证
'USER_AUTH_ID' => 'user_id', // 定义用户的id为权限认证字段
);
// 行为扩展的执行入口必须是run
public function run(&$return){
if(C('USER_AUTH_ON ')) {
// 进行权限认证逻辑 如果认证通过 $return = true;
// 否则用halt输出错误信息
}
}
B('AuthCheck');
注意:因为这种方式的行为调用需要在相关位置添加代码,所以一般只有在应用代码才直接使用B方法调用。