1. 基本概念

将框架的一个个执行的流程看作是一个个单独的行为,任意一个都可以作为一个行为。如用户的权限检测也是行为,单独抽离行为可以降低耦合,通过外围扩展来改变或增加一些功能

有些行为的作用位置是在应用执行之前,有些行为的作用位置是在模板输出之后。我们将发生作用的位置叫做钩子,当应用程序运行到这个钩子的时候,就会被拦截下来,统一执行相关的行为,类似于 AOP 编程的“切面”给钩子绑定行为及海慧寺一种类 AOP 的编程思想。

一个钩子可以对应多个行为,,执行这个位置后,按照注册的顺序依次执行相关的行为。可以设置一个钩子只注册一个行为,也可以设置一定的条件,在某一个行为中返回 false,来阻止其他行为继续进行下去。一个行为也可以绑定到不同的钩子上,这是一个多对多的关系,如何去设计去绑定需要自己去思考

钩子的位置需要先去定义好,要设置一个钩子。只需要在相关的位置添加一行代码(事先需要引入think\facade\Hook类):Hook::listen('钩子名称', '参数', '是否只有一次有效返回值')

其实也可以这样去理解这个问题:行为是要做的事,钩子是什么时候去做这件事。


2. 测试案例

2.1 案例一:使用框架自身的钩子

第一步:先定义一个行为类 Test,在入口方法中 执行一行简单的代码:
这里并没有使用 return 去返回这句话,这是因为在 tp 中 return 一般用于在控制器中去使用,如果这里使用 return 我们将不会看到任何结果。

  1. <?php
  2. namespace app\test2\behavior;
  3. class Test
  4. {
  5. public function run()
  6. {
  7. echo '我是一个行为';
  8. }
  9. }

第二步:注册行为(钩子中绑定行为):在应用目录的根目录下找到 tags.php , 在系统提供的位置下(钩子中),注册刚刚定义好的行为

  1. <?php
  2. // 应用初始化
  3. 'app_init' => [
  4. 'app\test2\behavior\Test',
  5. ],

由于这个行为定义的位置关系,我在任意控制下访问都可以获取到 如:我定义这样一个控制器

  1. <?php
  2. namespace app\test2\controller;
  3. class Demo3
  4. {
  5. // 测试行为的方法
  6. public function fun1()
  7. {
  8. }
  9. }

然后我通过 tp 默认的路由规则去访问:
image.png

可以看到,虽然我并没有在控制器中做任何动作,但是当我访问时,还是看到了行为中执行的结果,这也和注册的钩子相关,我将这个行为注册到了应用初始化的位置,所以这个行为的执行是在执行控制器中的方法之前就会去做的一件事。

2.2 案例二:使用自定义的钩子

第一步:先建立行为类

  1. <?php
  2. namespace app\test2\behavior;
  3. class Test2
  4. {
  5. public function run($param)
  6. {
  7. echo '进行了' . $param . '行为';
  8. }
  9. }

第二步:自定义钩子并且绑定刚刚的这个行为.

  1. <?php
  2. // 自定义行为
  3. 'eat' => [
  4. 'app\test2\behavior\Test2',
  5. ]

第三步:由于这个钩子是自定义的,所以需要我们自己设置触发条件
Demo3.php 中继续添加方法

  1. <?php
  2. // 自定义钩子
  3. public function fun2()
  4. {
  5. Hook::listen('eat', '吃饭');
  6. return '... ...';
  7. }

然后测试一下:
image.png

2.3 案例三:将多行为方法绑定到同一个行为类中

上面的代码每设计一个行为方法,就单独设置了一个行为类,这未免也太奢侈了。系统的钩子可以独立设置行为方法,自定义的钩子同样也是

重新定义的行为类方法

  1. <?php
  2. namespace app\test2\behavior;
  3. class Test
  4. {
  5. // 初始化行为方法
  6. public function appInit()
  7. {
  8. echo '我是初始化方法 <br/>';
  9. }
  10. // 自定义钩子
  11. public function eat($param)
  12. {
  13. echo $param . '</>';
  14. }
  15. }

现在的 tags.php 是这样的:

  1. <?php
  2. // 应用行为扩展定义文件
  3. return [
  4. // 应用初始化
  5. 'app_init' => [
  6. 'app\test2\behavior\Test',
  7. ],
  8. // 应用开始
  9. 'app_begin' => [],
  10. // 模块初始化
  11. 'module_init' => [],
  12. // 操作开始执行
  13. 'action_begin' => [],
  14. // 视图内容过滤
  15. 'view_filter' => [],
  16. // 日志写入
  17. 'log_write' => [],
  18. // 应用结束
  19. 'app_end' => [],
  20. // 自定义行为
  21. 'eat' => [
  22. 'app\test2\behavior\Test2',
  23. ]
  24. ];

依次调用 Demo3 中的两个方法:

  1. <?php
  2. // 测试行为的方法
  3. public function fun1()
  4. {
  5. }
  6. // 自定义钩子
  7. public function fun2()
  8. {
  9. Hook::listen('eat', '吃饭');
  10. return '... ...';
  11. }

postman 依次列出两个测试结果:

第一个方法,没有手动的绑定自定义的钩子,所以只会进行应用初始化的行为
image.png

第二个方法,手动的绑定自定义的钩子,所以会出现两句话
image.png

2.4 案例四:一个钩子注册多个行为方法,钩子与行为进行一对多的联系

tags.php 中:

  1. <?php
  2. // 自定义行为
  3. 'daily' => [
  4. 'app\\test2\\behavior\\Test2',
  5. 'app\\test2\\behavior\\Test3',
  6. 'app\\test2\\behavior\\Test4',
  7. ]

控制器中的方法:使用动态绑定的方式。

  1. <?php
  2. public function fun3()
  3. {
  4. Hook::add('daily', [
  5. 'app\\test2\\behavior\\Test2',
  6. 'app\\test2\\behavior\\Test3',
  7. 'app\\test2\\behavior\\Test4']
  8. );
  9. Hook::listen('daily', 'param1');
  10. return '完事了 ... ...';
  11. }

Test2:

  1. <?php
  2. namespace app\test2\behavior;
  3. class Test2
  4. {
  5. public function run($param)
  6. {
  7. echo $param . 'test2' . '</br>';
  8. }
  9. }

Test3:

  1. <?php
  2. namespace app\test2\behavior;
  3. class Test3
  4. {
  5. public function run($param)
  6. {
  7. echo $param . 'test3' . '</br>';
  8. }
  9. }

Test4:

  1. <?php
  2. namespace app\test4\behavior;
  3. class Test4
  4. {
  5. public function run($param)
  6. {
  7. echo $param . 'test4' . '</br>';
  8. }
  9. }

测试一下:
image.png


未完待续: