# 什么是钩子和行为
官方手册

行为

【行为定义】:可以把行为想象成在应用执行过程中的一个动 作。例如,在框架的执行流程中,路由检测、静态缓存、用户权限检测是一个行为,大到业务逻辑,小到浏览器检测、多语言检测等都可以当做是一个行为。

【行为作用】:把这些行为抽离出来的目的则是为了无需修改框架和应用,而在外围通过扩展或者配置来改变或者添加一些功能

钩子

【定义】:不同的行为之间也具有着位置共同性,比如,有些行为的作用位置都是在应用执行前,有些行为则是在模板输出之后,我们把这些行为发生作用的位置称之为钩子。
当应用程序运行到这个钩子的时候则会被拦截下来,统一执行绑定到该钩子中的相关行为,这类似于AOP编程中的切面概念,给某个钩子绑定相关行为就成了一种类AOP编程的思想。

特点

一个钩子可以绑定多个行为,执行到某个钩子位置后,会按照绑定的顺序依次执行相关的行为。

在某种特殊情况下,可以设置某个钩子只能执行一次行为,或者是设置在一个钩子的某个行为返回false来强制终止后续的行为执行。

一个行为同样可以被绑定到多个不同钩子。

通俗理解
可以将行为理解为是一个行为类的方法,在框架中行为类、行为方法则是有一定的规则约定;而钩子则是这些行为方法被调用执行的位置点。注意了,要想执行某个钩子中的行为,那行为一定要在应用程序执行到该钩子之前进行绑定。

# Tp5.1 如何设置一个钩子
设置钩子

#1. 引入think\facade\Hook类
use think\facade\Hook;
#2. 在需要的位置设置钩子
Hook::listen(‘钩子名称’,’参数’,’是否只有一次有效返回值’)

– 钩子名称:必须,钩子名称最好由小写字母加下划线’_’组成
– 参数:可选,5.1不支持引用传值,所绑定的行为方法的参数
– 是否只有一次有效返回:可选

Tp5.1系统预留钩子

Tp5.1系统核心预先设计了一些可能会需要的钩子,方便应用的扩展而不必改动框架核心,按照执行顺序如下:

钩子 描述 参数
app_init 应用初始化标签位 无
app_dispatch 应用调度标签位 无
app_begin 应用开始标签位 无
module_init 模块初始化标签位 无
action_begin 控制器开始标签位 当前的callback参数
view_filter 视图输出过滤标签位 当前模板渲染输出内容
app_end 应用结束标签位 当前响应对象实例
log_write 日志write方法标签位 当前写入的日志信息
log_write_done 日志写入完成标签位
response_send 响应发送标签位 当前响应对象
response_end 输出结束标签位 当前响应对象实例
# 行为定义
行为类、行为方法定义

# 若是该行为类中只有一个行为,只需要定义一个行为入口方法`run`即可

namespace app\index\behavior;
class Test
{
// 当该行为类被绑定到钩子的时候,run行为方法则会在该钩子被触发的时候执行
public function run($params)
{
// 行为逻辑
}
}

若是想要修改行为的入口方法,可以在应用公共文件中修改。

# application\common.php文件进行添加
# 注意,portal函数中的参数自定义,在修改了之后,run方法名就要改为你所定义的了。

use think\Hook;
use think\App;
$hook = new Hook(new App());
$hook->portal(‘portal’);

注意:如果一个行为类中有多个行为方法,那么在该行为类绑定到钩子的时候,钩子怎么知道要调用执行哪个行为方法呢?

# 上面的问题,Tp5.1对行为方法名进行一定规则约定来解决
# 即钩子在被触发的时候,会调用以钩子名称的驼峰命名(首字母小写)为方法名的行为方法。
namespace app\index\behavior;

class Test
{
// 该行为方法绑定的钩子是**app_init**
public function appInit($params){}

阿里云-推广AD

// 该行为方法绑定的钩子是**app_end**
public function appEnd($params){}
}

# 行为绑定
在看手册的时候,我觉得行为绑定这里是最容易让人迷糊的,Tp5.1.17钩子绑定行为有两种方式:

动态绑定(这个最让新手懵逼)
静态绑定
动态绑定

# 动态绑定是通过函数Hook::add()完成的,但新手容易出现的问题是,这个函数要写在哪里,
# 其实手册前面已经说过了,行为绑定一定要在钩子被触发之前,也就是要写在你所
# 要绑定的钩子位置点之前,只不过新手刚接触很容易忽略这点。

# 例子
public function test()
{
// 行为绑定到钩子,并不需要在同一个函数,只要保证在所绑定的钩子位置点前面就行了
Hook::add(‘test’,’app\\test\\behavior\\BehaviorTest’);
// 钩子的位置点
Hook::listen(‘test’);

}

静态绑定
在application\tags.php或者application\moduleName\tags.php文件中设置即可。

总结
从上面可以看出对于系统预留的钩子,不建议使用动态绑定,因为你必须清楚知道钩子的位置点,对于自己设置的钩子,两种方式都可以,不过静态绑定个人觉得更加规范,所有的绑定都在统一的配置文件中。

其余的闭包支持、直接执行行为直接看手册即可。传送门

# 例子
下面的例子针对是行为类中只拥有一个行为方法。

# application\test\behavior\BehaviorTest.php
<?php
namespace app\test\behavior;

class BehaviorTest
{
public function portal($params) {

echo $params[‘name’].”钩子执行了方法”.”<br>”;
}
}

# application\common.php
<?php
// 应用公共文件
use think\Hook;
use think\App;

$hook = new Hook(new App());
$hook->portal(‘portal’);

# application\test\controller\Index.php
<?php
namespace app\test\controller;

use think\facade\Hook;

class Index
{
public function index()
{
echo ‘start’.”<br>”;

// 在触发钩子之前,绑定行为到指定钩子
Hook::add(‘test_1′,’app\\test\\behavior\\BehaviorTest’);
Hook::add(‘test_2′,’app\\test\\behavior\\BehaviorTest’);

// 调用设置了钩子的函数来触发钩子,进行测试
$this->test_1();
$this->test_2();
}

public function test_1() {

// 设置钩子test_1
Hook::listen(‘test_1’,[‘name’=>’test_1’]);
}

public function test_2() {

// 设置钩子test_2
Hook::listen(‘test_2’,[‘name’=>’test_2’]);
}
}