挂钩背后的基本概念是什么?


120

我在PHP中级。为了提高我的技能,我开始学习Drupal7。在学习Drupal体系结构概念时,术语“ 钩子”和“ 引导”使我很困惑。我阅读了《 Pro Drupal开发》这本书以及有关drupal.org的一些文档,但是对我来说,了解钩子在Drupal中如何工作以显示网页是非常先进的。

谁能告诉我简单的钩子是什么?

Answers:


107

其他答案很好,准确,详细,但我不确定它们是“简单的单词”,解释了问询者所寻找的概念的基本内容。

我认为钩子是代码暂停并大喊“ 其他人要在这里添加任何东西吗? ”的地方。任何模块都可以具有对此功能的回复,并在代码中的那一刻被传递给它的适当数据触发。

一个很好的简单示例是hook_node_delete()。每次删除节点时,任何模块都可以使用它来使事情发生。文档告诉您此钩子将要删除的节点的对象传递给模块,并概述了其他有用的信息,例如有关调用它的确切时间(例如,实际上是在从数据库中删除节点数据之前)。 ,以及在Drupal的代码中调用该钩子的位置(可以在多个地方)。

您可以通过研究Drupal api中以“ hook_”开头的内容来探索存在哪些钩子,并找出传递给它们的数据。

挂钩按照名称约定工作:hook_node_delete以我们的示例为例,当节点删除过程到达调用挂钩的位置时,对于具有此类功能的每个模块,[modulename]_node_delete()挂钩名称中的单词hook都会被替换为模块名称(例如,my_amazing_module_node_delete()),这些函数将被调用。

为什么?因此,任何模块都可以在这些关键时刻执行任何操作:例如,您可以查看已删除的节点,并在满足特定条件时执行操作(例如,向管理员发送电子邮件或启动一些漫长的过程)。

一些挂钩可以让您更改在处理之前已生成的内容。例如,hook_menu_alter()向您传递系统已生成的当前菜单项。任何模块都可以定义一个函数some_modulename_menu_alter()并查看它们,可以有选择地对其进行更改(删除一些,添加一些,对其进行排序...),然后将新更改的菜单传递回去。

它简单,功能强大,是Drupal作为模块化系统工作的核心。挂钩的实现是大多数Drupal模块的核心。

当查看Drupal模块的代码时,您可以发现哪些函数来自钩子(与从模块代码本身内部简单调用的函数相反),因为Drupal社区实施了一个约定,即每个钩子实现都有一个像这样在其前面添加注释(请注意“ Implements hook _...”位):

/**
 * Implements hook_some_hook().
 *
 * Some descriptive summary of what this does
 */
function my_amazing_module_some_hook() {

一些充当API的模块定义了它们自己的钩子。例如,“ 视图”定义了许多挂钩,使您可以在创建或显示视图的过程中的各个点添加,读取和编辑数据。您可以从两个地方找到有关在自定义模块中创建的挂钩的信息(假设模块遵循约定等):

就像其他人解释的那样,引导程序基本上就是引导程序-我不会重复其他清楚的解释。


53

挂钩主要是VisitorObserver模式的实现。

hook_menu是最常见的钩子实现之一,它允许模块在Drupal系统中注册新路径。

function my_module_menu() {
  return array('myawesomefrontpage' => array(
    'page callback' => 'function_that_will_render_frontpage'
  ));
}

在Drupal中,一个非常常见的模式是有一个[DATATYPE]_info钩子和一个[DATATYPE]_info_alter钩子。如果要创建新的字段类型,则将实现相关的field_info -hook,如果要操作现有字段类型,则将实现对应的field_info_alter -hook。

编辑:正如Chx在评论中指出的那样,观察者模式是面向对象的,而Drupal 7仍然不是。但是,有一个Wiki页面,即从面向对象的角度进行Drupal编程(由JonBob在2005年4月4日创建),它解释了Drupal如何使用面向对象的代码模式。有趣的是,它提到的是观察者,但没有提到访客。

关于Drupal 8的说明这还很早,并且可能会发生变化,但是我想补充一点,尽管在相当长的一段时间里,挂钩一直是向Drupal添加功能的事实上的标准,但是插件的概念将变得更加明显。在Drupal 8中,将为我们提供与Core交互的新方法。相关问题文档


2
不要被OOP设计模式所束缚。这不是一个。Drupal钩是AOP。见下文。

@chx,虽然我自由地承认您比我更了解:)并且您的答案是正确的(我赞成),但我不认为这是我的答案是错误的。:)如果您不同意,我想了解一下我是如何误解了它们的实现方式。
Letharion 2012年

4
维基百科:“观察者模式是一种软件设计模式,其中一个对象(称为主题)维护着其从属者列表,称为观察者”。这里没有对象持有观察者。访客比较晦涩,但适用相同的通用原则:不是OOP的不能具有OOP模式。

33

用外行的术语来说,钩子是一种桥梁,为模块相互交互,改变彼此的结构和数据,提供新数据等提供了一种方式。

在大多数情况下,hook_函数名称中的单词会替换为模块名称,这为模块提供了一种利用其他模块操作的方法。例如,一个称为“节点”的drupal核心模块调用了各种钩子。其中之一是hook_node_update每次更新现有节点时都会调用。调用此钩子时,将调用您的模块(例如我们称其为mymodule)的实现hook_node_update,在这种情况下,该实现将是您模块的.module文件中的一个函数mymodule_node_update(显然,该函数可以在您模块的文件夹中的任何文件中,只要它也包含在.module文件中)。该挂钩还将被传递必要的参数(变量),可以使用,修改和/或返回到调用该挂钩的函数。

当我刚开始学习Drupal时,我和您现在处在同一条船上,一开始很难掌握,但是一旦掌握,它就会变得如此简单直观。祝好运。


1
谢谢您的解答。它对我有很大帮助。你能告诉什么是引导概念在Drupal以及如何钩子的Bootstrap为您解释前面的答案被视为简单的话..
吉尔

@Bayasa,请在这里输入您的想法。我只是添加自己的。您可以将boostrtapping视为启动计算机。Drupal有许多API,包括数据库,文件和表单。这些基于“平台”。在引导过程中,Drupal定义了这些功能和其他设置(数据库连接,文件夹等),因此系统的其余部分可以从其余部分继续进行。
AyeshK 2012年

32

一位核心开发人员不久前写了一篇文章“从面向对象的角度进行Drupal编程”。它很好地解释了如何将钩子视为实现许多常见的设计模式。钩子的最佳解释来自该文章:

Drupal的hook系统是其接口抽象的基础。挂钩定义了可以在模块上执行或由模块执行的操作。如果模块实现了挂钩,则该模块会签订合同以执行特定任务或在调用挂钩时返回特定类型的信息。调用代码不需要了解有关模块或挂钩的实现方式的任何信息,即可通过调用挂钩来完成有用的工作。



@chx,谢谢。我正在考虑立即删除此答案。我将其发布到b / c上,我曾经在普通C语言中做过很多OO工作,并且习惯了没有真实对象的OO概念。但是,您对切入点/编织器关系的看法是正确的。但是,我不确定引号是否是一个方面的准确描述(忽略“接口抽象”部分)。
mpdonadio

21

Bootstrap是Drupal生成页面的过程,基本上按顺序运行在所有核心,主题和模块代码上。
基本上,这是Drupal启动的方式,并准备作为CMS做它。

这很聪明,因为它允许我们将钩子放置在模块和主题中的任何位置,并且引导过程可确保它们在正确的位置运行。
例如,如果您使用“ hook_form_alter”将自定义复选框添加到表单,Drupal的引导程序将确保它在呈现表单之前运行该代码。

引导程序的一个问题是,即使您仅返回少量数据,整个过程也需要花费时间。当将Drupal与服务模块一起用作API并返回许多小的XHTML或JSON响应时,在整个引导程序中运行效果不佳。一些聪明的人正在为Drupal 8寻找巧妙的方法。

但是对于呈现普通的Drupal页面,引导过程非常有效,它使用Drupals缓存系统来加快处理速度,并可以完全控制站点的每个部分。如果确实发现网站运行缓慢,则可以始终使用APC或MemCached之类的方法来加快速度。

我希望我的回答是准确的,并且只是为您解释一些事情,我不是专家,但是我认为这是有效的。


15

Bootstrap是Drupal初始化自身的过程。该过程实际上包括:

  • 设置错误和异常处理程序
  • 初始化包含在其中的某些规范全局变量的值 $_SERVER
  • 用初始化一些变量 init_set()
  • 查找要投放的页面的缓存版本
  • 初始化数据库
  • 设置未找到类或接口时加载文件的处理程序
  • 初始化Drupal变量
  • 初始化PHP会话
  • 初始化语言变量
  • 加载启用的模块

我描述的某些操作特定于Drupal 7或更高版本,但大多数操作独立于Drupal版本。

挂钩是一个PHP函数,可以在需要执行任务时从Drupal或第三方模块中调用。该列表不是构建要调用的函数的前缀列表,而是在构建列表时检查启用的模块及其实现的功能。
例如,Drupal使用hook_node_update();当使用node_save()保存节点时,将执行以下代码。

// Call the node specific callback (if any). This can be
// node_invoke($node, 'insert') or
// node_invoke($node, 'update').
node_invoke($node, $op);

什么node_invoke()做的是以下几点:

  • 获取所有已启用模块的列表
  • 检查启用的模块是否具有名称以“ _node_update”结尾并以模块的短名称开头的功能
  • 调用该函数,$node作为参数传递

挂钩可以将自己的数据保存在数据库中,也可以更改从函数返回的值。最后一种情况是,例如,hook_form_alter()会发生什么,这会更改$form传递给drupal_prepare_form()的引用的值。

Drupal钩子通常使用以下三个函数来调用:

drupal_alter()是用于调用特定挂钩的函数,其目的是更改作为参考传递给它们的数据,例如hook_form_alter()hook_hook_info_alter()hook_tokens_alter()

还有其他用于调用挂钩的函数,例如node_invoke(),但是这些函数实质上使用了我之前列出的函数之一。



6

如果您想查看Drupal允许您调用的钩子,请转到api.drupal.org,在标签页上转到搜索框,然后键入“ hook_”。这将为您提供Drupal定义的大多数挂钩的一大清单。对'_alter'做同样的事情,然后看更多。

节点API挂钩”页面提供了按时间顺序排列的列表,列出了在节点操作期间调用的所有挂钩。您可以看到Node模块以及Entity和Field系统在调用钩子时彼此交替。

例如,如果您向下滚动并查看以下部分node_load():Node模块将为您提供一个hook_load(),然后将控制权传递给加载某些字段的实体系统。有大量的字段挂钩未列出,然后在实体系统调用结束时hook_entity_load(),将控制权交回给调用的Node hook_node_load()

这使您的代码有机会在有问题的节点上逐步加载。学习这些钩子以及何时以及为什么调用它们是Drupal编码冒险的一部分。:-)

其他系统也有挂钩。如hook_init()hook_boot()。这是您问题的引导部分。hook_boot()在缓存系统加载之前由Drupal调用。因此,如果您的模块需要在Drupal真正启动之前做点什么,并且希望无论缓存如何都可以运行代码,则可以实现hook_boot()。否则,如果您只关心未缓存的页面,则可以实现hook_init()

这使您可以选择在Drupal完全启动之前在加载过程的早期实施某些操作,同时还可以灵活选择要拦截的过程中的哪一点。

如果您需要确保Drupal在启动之前已经启动到某个特定点,可以致电drupal_bootstrap()。如果单击该文档,则可以看到可用的引导程序级别,从无到有。

最后,您可以在Examples项目上看到针对任何给定子系统的一些详尽记录的代码。


OP要求定义挂钩,而不是Drupal使用的挂钩列表。
kiamlaluno

6

挂钩是php函数,是基于“ yourmodulename_hookname”命名约定的构建基块,它们旨在减轻开发人员创建模块的能力。

模块是真正的交易,因为它们在您的Drupal系统中同时启用了CORE和自定义功能。因此,模块是由钩子组成的,当在您的Drupal安装中激活模块时,由于使用module.inc函数module_invoke_all($ hook)或module_invoke ,可以从其他模块中调用其钩子函数。

因此,要正确理解什么是钩子,您应该真正动手并尝试进行模块开发。为此,从下载并尝试一些Drupal的Developers实例开始,您也应该熟悉模块的创建

这是上面提到的对开发人员有用的Drupal示例:

在block_example模块中的hook_block_view()实现示例

/**
 * @file examples/block_example/block_example.module line 127
 *
 * Implements hook_block_view().
 *
 * This hook generates the contents of the blocks themselves.
 */
function block_example_block_view($delta = '') {
  //The $delta parameter tells us which block is being requested.
  switch ($delta) {
    case 'example_configurable_text':
      // The subject is displayed at the top of the block. Note that it
      // should be passed through t() for translation. The title configured
      // for the block using Drupal UI supercedes this one.
      $block['subject'] = t('Title of first block (example_configurable_text)');

该挂钩使您可以访问Drupal的块创建,以在您的网站上显示自定义块。可能是因为block.module具有_block_render_block函数,该函数使所有模块都可以定义其hook_block视图(请注意module_invoke最后一行):

/**
 * @file modules/block/block.module, line 838
 *
 * Render the content and subject for a set of blocks.
 *
 * @param $region_blocks
 *   An array of block objects such as returned for one region by _block_load_blocks().
 *
 * @return
 *   An array of visible blocks as expected by drupal_render().
 */
function _block_render_blocks($region_blocks) {
  ...
  foreach ($region_blocks as $key => $block) {
    ...
    $array = module_invoke($block->module, 'block_view', $block->delta);

render_example模块中的hook_menu()实现示例

/**
 * @file examples/render_example/render_example.module line 22
 * 
 * Implements hook_menu().
 */
function render_example_menu() {
  ...
  $items['examples/render_example/arrays'] = array(
    'title' => 'Render array examples',
    'page callback' => 'render_example_arrays',
    'access callback' => TRUE,
  );

这个钩子链接到Drupal的URL路由系统,并使用模块使用的关联渲染回调定义URL模式。从system.module调用。

关于引导程序,基本上,您只需要知道它在每个页面请求上执行。我真的建议您阅读这个stackoverflow答案,它说明了引导程序和挂钩是如何相关但分离的。

关于网页的显示,Drupal的网站html显示主要通过渲染数组和主题实现。


3

任何模块调用module_implements()http://api.drupal.org/api/drupal/includes%21module.inc/function/module_implements/7 Drupal都会根据权重以正确的顺序触发所有正确命名的函数。这些称为挂钩函数,因为在使用module_implements的模块的文档中,您会看到类似hook_menu的内容(当菜单调用所有旨在返回菜单项的函数时)。只需将“ hook”一词替换为实现它的模块的名称,其余的由Drupal完成。

还有一个drupal_alter()函数可触发所有正确命名的alter函数,目的是让您更改以前由另一个钩子注册的内容。

通常,alters将通过引用传递参数,因此您可以直接编辑对象,而“ normal”钩子通常使您返回新事物。

这个想法是,可以通过要求Drupal调用所有必需的钩子函数并返回它们返回要处理的内容来轻松扩展任何模块(包括您自己的模块)。调用钩子函数的模块不需要了解有关实现钩子的模块的任何信息,实现钩子的模块也不需要了解有关调用钩子的模块的任何信息。这两个模块唯一需要知道的是要返回或更改的数据的结构。

实际上,钩子最常用于:

  • 响应事件,例如在用户登录时调用hook_user_login
  • 注册可以用于扩展系统的新内容,例如hook_menu
  • 主题/渲染html或构建/验证/提交表单

1

上面您有很多答案,但是我想以一种更简单的方式给出答案,以了解挂钩背后的基本概念。挂钩实际上是在drupal核心中内置的函数,用于管理不同的事物并在核心中完成了不同的工作,您可以将自己的函数与drupal核心的这些内置函数进行同步,以通过调用不同的挂钩将其功能添加到自己的函数中。

希望您能理解!


1

对我来说,关于钩子和核心(D7)的全部都是关于module_implements函数。我认为至关重要的一件事是,通过编写钩子来修改某些内容,您决不会对要处理的数据结构发生什么拥有最终决定权。您的钩子只是进入了同样作用于相同数据结构的函数行(队列),无论这些函数是菜单,menu_links,块,节点,用户还是任何实体或渲染元素。

因此,要真正看到您的钩子以预期的方式使用,您需要知道或知道您(钩子)的排列位置。这取决于您的小月饼的重量。Drupal核心只是按照权重递增的顺序调用正确命名的钩子,无论发生什么数据。

在此之前,我已经写过钩子没有任何作用,只是在经过数小时的猛烈抨击后才知道我的模块重量太轻,随后的钩子实际上在做我所做的事情,或者完全忽略了它们。

编写良好的钩子不会“手动”或“强迫”自己处于最后位置,而是通过确保它们保持其余钩子所期望的数据结构来“与他人友好”。

并谈到钩的“线”。多年来,我一直在Google上搜寻Drupal的东西,这张图片似乎很好地表示了可能的预处理和过程挂钩列表。
在此处输入图片说明


1

钩子以更简单的方式帮助开发人员根据需求更改现有功能,而无需更改现有代码。更像php中的Abstract函数。

示例:您已经创建了用于预订公交车票的模块。根据您的代码,如果门票已被预订,那么接送地点是不可编辑的,这是您对该项目的要求。假设您的朋友需要相同的模块来满足类似的要求,但用户可以更改接送地点。他以某种方式必须使用您的模块,并且您不希望他进行任何代码更改。因此,您提供一个接口(在我们的例子中为钩子),他可以在其中进行更改而无需在模块中进行更改。

在drupal-7之前的drupal中,我们具有模块和主题的挂钩。要知道钩子是如何工作的,请检查drupal.org 钩子以创建自定义钩子,请检查此链接


0

钩子。允许模块与Drupal核心交互。Drupal的模块系统基于“挂钩”的概念。挂钩是一个名为foo_bar()的PHP函数,其中“ foo”是模块的名称(因此,其文件名是foo.module),而“ bar”是挂钩的名称。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.