云客Drupal8源码分析之钩子、模块处理器、函数定义

本文探讨Drupal8的模块处理器如何加载开启模块的函数库,并介绍了系统默认的钩子信息函数system_hook_info和views_hook_info。这些信息被缓存在bootstrap的hook_info缓存项中。此外,文章还提到了模块实现的钩子信息存储在module_implements.bin中。
摘要由CSDN通过智能技术生成
元旦刚刚过去,继续保持一周一篇的速度,这是Drupal8云客源码分析系列在2017年的开篇,来讲几个极其重要的内容,它们是钩子、模块处理器、函数定义。
钩子:
如果是开发新手可能对这个慨念陌生而好奇,许多工作多年的工程师可能对它也缺乏深度理解,不止是在php中,它其实是软件工程中的重要慨念,什么意思呢?为什么要叫做钩子?既然叫做钩子直觉的就会联想到钩什么?怎么钩?简单而直接的回答就是在软件运行的某时刻去钩起一段代码来执行,这个“钩起”也可以称为调用、获取等,但总不能叫做“调子”“获子”吧,所以形象起见叫做钩子,可以将钩子理解为一种软件实现机制,函数调用就是钩子最简单的情况,执行函数时调用它就是钩起它,函数就是钩子实现,函数名就是钩子名,怎么钩就是通过函数名来钩。

以上是对钩子宏观的理解,有了这个慨念后来看一看drupal中钩子的概念:
在drupal系统中钩子是一种非常重要的互动机制,系统通过它在系统和模块间、模块和模块间互动,通过特别命名的函数来实现,具体说就是执行流程中在某一个点去执行满足某特定命名的函数,可能是一个函数也可能是一些函数,其他部分可以定义这些函数,在这里“特定命名”这个词就很重要了!drupal是怎么特定这个命名的呢?在实现中为某种功能执行取个名字(流程中某个点会执行这个功能)就是钩子名,各模块可以自定义一个函数,函数名为“模块名_钩子名”,这些函数有共同特性:为完成同一性质的事情而定义(也就是前文说的某功能执行),在流程中的某点一起依次执行它们,模块需要参与就定义相应函数,反之则不定义,在php中函数是超全局的,在任何地方都可以调用。钩子是不是和软件设计中的事件订阅设计模式类似呢?没错,本质上它们是一回事,钩子实现中在执行点调用钩子函数集这个动作就相当于派发了一个事件,实现了满足钩子实现命名的函数就相当于订阅了这个事件!
在drupal8中系统已经定义了许多钩子名,它们在系统运行的各个点被执行,这样允许模块参与到核心中来,下文将列出默认安装时的所有钩子名。模块只要实现了满足钩子命名的函数,这些函数将在钩子调用时执行,通常将这些函数叫做钩子实现。模块也可以自定义钩子名。

模块怎么定义一个钩子名呢?
比如模块名叫“yunke”,要定义一个钩子(名)叫做“sendMail”那么在模块的主目录中建立一个名为“yunke.api.php”的文件,里面定义一个样板函数叫做:hook_sendMail(...),加上注释,说明在yunke这个模块中怎么运用这个钩子的,然后在yunke这个模块的代码中通过模块处理器(下文讲)来执行这个钩子,其他模块实现了这个钩子就会被yunke这个模块执行到,其实“yunke.api.php”这个文件是不会被系统执行的,它的作用完全形同一个文档,指导其他模块开发者去实现钩子,在系统中你可以看到许多这样的文件,他们位于模块的主目录,在drupal官方的API文档站点就是通过自动解析它们来获取钩子注释文档的,所以样板函数和注释越详细越好,它的作用就是一个文档。
模块怎么实现上面定义的sendMail钩子呢?定义一个函数,将函数名“hook_sendMail”中的hook换为自己的模块名即可,函数放置在.module文件中,比如实现这个钩子的模块叫做“yunke_php”,那么就放置在主目录的yunke_php. module文件中。
关于钩子实现函数的文件位置还有另外一种放置方法:定义这个钩子的模块可以定义一个钩子信息函数,放置在.module文件中(比如上文就是yunke .module),函数名格式为:"模块名_hook_info",如上文就是“yunke _hook_info”,它通过返回值来申明钩子实现可以放置的位置,返回一个数组,键名为钩子名,对应值为一个数组:Array ("group"=> $var),以前文的yunke模块为例:钩子信息函数名为:“yunke_hook_info”,如果他返回内容如下:
$hooks[' sendMail '] = array('group' => 'user');
那么其他模块对sendMail这个钩子的实现还可以放置在自己主目录的“模块名.user.inc”文件里,这样的设计是为了将很少使用、很庞大的钩子独立放置在$module.$group.inc文件中,不需要时可不加载,提高性能,模块的“.module”文件默认是每个请求都全部加载的。关于钩子信息函数"模块名_hook_info"的具体实现可以参考:\core\modules\system\system.module中的system_hook_info()
模块提供的钩子信息函数会被模块处理器执行,结果合并后存放在缓存中以备后用。
明白了钩子的概念,就不难理解系统的模块处理器了,继续。

模块处理器:
模块处理器是容器中的一个服务,服务名为“module_handler”,在任何地方均可以通过\Drupal::moduleHandler()得到,它负责保存系统开启的模块信息、加载模块的函数、执行钩子等,在系统中非常多的地方都用到了它,可以说它是系统和模块、模块和模块之间的重要桥梁之一。
文件位于:\core\lib\Drupal\Core\Extension\ModuleHandler.php
它有三个构造参数:
$root(app.root):
是一个SplString对象,该对象代表一个字符串值,是系统的根目录,此值也保存在常量DRUPAL_ROOT中
$module_list(container.modules):
系统开启的模块列表,是容器参数,在容器构建时产生
$cache_backend(cache.bootstrap)
是一个缓存,默认实现中从数据库bootstrap缓存表获取数据

在drupal中模块类型有两种:module(普通模块)、profile(配置模块)

在系统初始阶段(容器形成后的核心堆栈中间层里)将调用模块处理器加载所有开启模块的“.module”文件,这个文件相当于模块用到的函数库。

模块处理器的实现比较简单,这里只列出一些常用或要点来介绍:
加载模块的其他文件:
\Drupal::moduleHandler()->loadInclude($module, $type, $name = NULL)
加载模块$module主目录下文件名为$name扩展名为$type的文件,模块必须是开启的,文件名为空则用模块名代替,这常用于加载函数库,当然也可以是其他文件

系统默认有两个模块定义的钩子信息函数:
system_hook_info 位于:\core\modules\system\ system.module
views_hook_info 位于\core\modules\views\ views.module
他们的返回结果被合并后存放于缓存表bootstrap的hook_info缓存项中

function system_hook_info() {
  $hooks['token_info'] = array(
    'group' => 'tokens',
  );
  $hooks['token_info_alter'] = array(
    'group' => 'tokens',
  );
  $hooks['tokens'] = array(
    'group' => 'tokens',
  );
  $hooks['tokens_alter'] = array(
    'group' => 'tokens',
  );

  return $hooks;
}

模块提供的钩子在模块的*.api.php file中介绍,那是快速参考文档
<
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值