首页 > 编程语言 > PHP > TP中的Behavior(行为)总结!
2021
02-22

TP中的Behavior(行为)总结!

首先解释一下什么是钩子 (hook)?什么是行为?
看TP的源码看得出,hook是一个抽象的概念,是一套触发机制。钩子应该具有的基本方法应该有:
-设置钩子
-触发钩子
-执行行为

行为就是继承了Behavior的行为类,类名以Behavior结尾,实现run方法。也可以称之为 钩子函数。

第一步设置钩子,也就是埋设陷阱。把你的钩子想象成一个陷阱,放到系统流程可能经过的路上,如果陷阱被系统踩到,就会执行你的行为,当执行完后,系统会根据你的程序的结果继续运行。

在Thinkphp Behavior中,我们把这些行为发生作用的位置(陷阱)称之为标签(位)(tag),当应用程序运行到这个标签的时候,就会被拦截下来,统一执行相关的行为。

在tp中,典型的例子就是:thinkphp/conf/mode/common.php'tags'  =>  array(
'app_init'      =>  array(
),
'app_begin'     =>  array(
'Behavior\ReadHtmlCache', // 读取静态缓存
),
'app_end'       =>  array(
'Behavior\ShowPageTrace', // 页面Trace显示
),
'path_info'     =>  array(),
'action_begin'  =>  array(),
'action_end'    =>  array(),
'view_begin'    =>  array(),
'view_parse'    =>  array(
'Behavior\ParseTemplate', // 模板解析 支持PHP、内置模板引擎和第三方模板引擎
),
'template_filter'=> array(
'Behavior\ContentReplace', // 模板输出替换
),
'view_filter'   =>  array(
'Behavior\WriteHtmlCache', // 写入静态缓存
),
'view_end'      =>  array(),
),
第二部,触发钩子。 在需要添加行为的函数里 ,直接Hook::Listen(tags,prarm),注意param一定要传变量,不需要传常量。触发行为的关键方法是Hook类中的listen方法,它通过遍历某个行为标签下的所有行为,依次实例化并调用run方法.
在tp中,典型的例子就是:App.class.phpstatic public function run() {
// 应用初始化标签
Hook::listen('app_init');
App::init();
// 应用开始标签
Hook::listen('app_begin');
// Session初始化
if(!IS_CLI){
session(C('SESSION_OPTIONS'));
}
// 记录应用初始化时间
G('initTime');
App::exec();
// 应用结束标签
Hook::listen('app_end');
return ;
}
第三部,执行行为。 找到行为类,类名以Behavior结尾,实现run方法。class ReadHtmlCacheBehavior {
// 行为扩展的执行入口必须是run
public function run(&$params){
// 开启静态缓存
if(IS_GET && C('HTML_CACHE_ON'))  {
$cacheTime = $this->requireHtmlCache();
if( false !== $cacheTime && $this->checkHTMLCache(HTML_FILE_NAME,$cacheTime)) { //静态页面有效
// 读取静态页面输出
echo Storage::read(HTML_FILE_NAME,'html');
exit();
}
}
}
钩子在MVC模式下十分重要,他实现了在不改变源代码的前提下提升系统的灵活性,如,在文章输出前打印版权信息,在文章输出后生成二维码信息,app运行前检查用户权限,还有更多产品经理提出的变态要求,都可以。
--------------------------------------------下面讲一下tp内部实现-------------------------------------------------------

源代码位于ThinkPHP/Library/Think/Hook.class.php,Hook类中全是静态方法,其中有唯一静态属性$tags,他是一个数组,键为钩子,值为行为。
其中有两个方法可以用于绑定,前者是单个,后者是是批量。static private  $tags       =   array();

/**
* 动态添加插件到某个标签
* @param string $tag 标签名称
* @param mixed $name 插件名称
* @return void
*/
static public function add($tag,$name) {
if(!isset(self::$tags[$tag])){
self::$tags[$tag]   =   array();
}
if(is_array($name)){
self::$tags[$tag]   =   array_merge(self::$tags[$tag],$name);
}else{
self::$tags[$tag][] =   $name;
}
}

/**
* 批量导入插件
* @param array $data 插件信息
* @param boolean $recursive 是否递归合并
* @return void
*/
static public function import($data,$recursive=true) {
if(!$recursive){ // 覆盖导入
self::$tags   =   array_merge(self::$tags,$data);
}else{ // 合并导入
foreach ($data as $tag=>$val){
if(!isset(self::$tags[$tag]))
self::$tags[$tag]   =   array();
if(!empty($val['_overlay'])){
// 可以针对某个标签指定覆盖模式
unset($val['_overlay']);
self::$tags[$tag]   =   $val;
}else{
// 合并模式
self::$tags[$tag]   =   array_merge(self::$tags[$tag],$val);
}
}
}
}TP Hook::listen方法,该方法会查找$tags中有没有绑定app_start事件的方法,然后用foreach遍历$tags属性,并执行Hook:exec方法。
Hook:exec方法会检查行为名称,如果包含Behavior关键字,那么入口方法必须为run方法,而执行run方法的参数在调用Hook::listen时指定。/**
* 监听标签的插件
* @param string $tag 标签名称
* @param mixed $params 传入参数
* @return void
*/
static public function listen($tag, &$params=NULL) {
if(isset(self::$tags[$tag])) {
if(APP_DEBUG) {
G($tag.'Start');
trace('[ '.$tag.' ] --START--','','INFO');
}
foreach (self::$tags[$tag] as $name) {
APP_DEBUG && G($name.'_start');
$result =   self::exec($name, $tag,$params);
if(APP_DEBUG){
G($name.'_end');
trace('Run '.$name.' [ RunTime:'.G($name.'_start',$name.'_end',6).'s ]','','INFO');
}
if(false === $result) {
// 如果返回false 则中断插件执行
return ;
}
}
if(APP_DEBUG) { // 记录行为的执行日志
trace('[ '.$tag.' ] --END-- [ RunTime:'.G($tag.'Start',$tag.'End',6).'s ]','','INFO');
}
}
return;
}

/**
* 执行某个插件
* @param string $name 插件名称
* @param string $tag 方法名(标签名)
* @param Mixed $params 传入的参数
* @return void
*/
static public function exec($name, $tag,&$params=NULL) {
if('Behavior' == substr($name,-8) ){
// 行为扩展必须用run入口方法
$tag    =   'run';
}
$addon   = new $name();
return $addon->$tag($params);
}

最后编辑:
作者:搬运工
这个作者貌似有点懒,什么都没有留下。

留下一个回复

你的email不会被公开。