WordPress 插件开发教程手册 — 钩子(Hooks)

钩子是用一段代码添加/修改另外一段代码的方式,是 WordPress 插件和主题与 WordPress 内核交互的基础,钩子在 WordPress 内核中也被广泛使用。WordPress 中有两种钩子,Action 和 Filter。使用钩子时,我们需要先编写一个自定义函数作为钩子的回调函数,然后使用 add_action 或 add_filter 函数将我们的回调函数挂载到指定的 Action 或 Filter 上。

Action 可以让我们在函数执行的某个时间点添加一些自定义操作(如输出内容到文章尾部),Filter 和 Action 类似,不同的是我们可以通过 Filter 修改并返回数据,因此,挂载到 Filter 上的函数会接受一些变量,并返回修改后的变量。简单来说,Action 用来添加功能,Filter 用来修改数据。

WordPress 内核提供了很多钩子,来帮助开发者开发 WordPress 主题或插件。通过创建自定义钩子,我们也可以让第三方开发者添加或修改我们的功能。

WordPress Action  和 Filter 钩子参考链接

Action 钩子

Action 是 WordPress 的两种钩子之一,提供了一种在 WordPress 核心、主题或插件执行的特性时间点运行附加函数的功能。Action 和 Filter 是不一样的。

添加 Action 的操作

我们可以通过两个步骤添加一个函数到某个 Action。首先,我们需要创建一个回调函数,这个函数在 Action 运行时会被调用。其次,我们需要把这个函数挂载到对应的 Action 钩子上面。使用 add_action() 函数,至少需要传递两个参数 $tag (钩子名称) 和 $function_to_add (回调函数名)。

下面的例子在 init 钩子执行时运行:

<?php
function wporg_custom() {
    // 执行某些操作
}
add_action('init', 'wporg_custom');

我们可以参考上文获取可用的钩子列表。随着开发经验的日益增长,我们可以通过查看 WordPress 核心、主题或插件的源代码找到更多合适的钩子。

其他参数

add_action() 也可以接受两个额外的参数,$priority (整数) 规定了回调函数执行的优先级,和 $accepted_args (整数) 规定了传递给回调函数的参数数量。

优先级

如果一个钩子上面挂载了多个回调函数,钩子就需要一个优先级,来确定这些回调函数的执行顺序。优先级为整数,默认值为 10,数字越小,优先级就越高。比如,优先级为 11 的函数将在优先级为 10 的函数之后执行,优先级为 9 的函数将在优先级为 10 的函数之前执行。

例如,下面的回调函数全部挂载到了 init 钩子上面,但他们有不同的优先级。

<?php
add_action('init', 'run_me_early', 9);
add_action('init', 'run_me_normal'); // 如果没有指定优先级,默认为 10
add_action('init', 'run_me_late', 11);

在上面的钩子运行时,第一个运行的函数是 run_me_early(),run_me_normal(), 最后一个运行的函数 run_me_late()。

参数个数

有时候,回调函数需要接收一些额外的数据作为函数的参数。例如,当 WordPress 保存一篇文章时,将会运行 save_post 钩子,这个钩子会传递两个参数给回调函数:保存的文章 ID 和 文章对象:

do_action('save_post', $post->ID, $post);

所以,当我们挂载函数到  save_post 钩子时,我们可以指定它需要接收这两个参数:

add_action('save_post', 'wporg_custom', 10, 2);

然后我们就可以在回调函数中使用钩子提供的参数了。

function wporg_custom($post_id, $post){
    // 执行某些操作
}

示例

假设我们需要在 WordPress 的前端文章查询中修改获取搜索结果的查询,我们可以使用 pre_get_posts 钩子。

<?php
function wporg_search($query) {
    if (!is_admin() && $query->is_main_query() && $query->is_search) {
        $query->set('post_type', ['post', 'movie']);
    }
}
add_action('pre_get_posts', 'wporg_search');

Filter 钩子

Filter 是 WordPress 钩子两种类型中的另外一个,可以让我们通过注册到某个 Filter 钩子上的回调函数来修改某些函数产生的数据。与 Action 不同,Filter 应该以独立的方式运行,不应该有影响全局变量和输出的副作用。

添加 Filter

我们可以通过两个步骤挂载一个回调函数到某个 Filter 上。首先,我们需要创建一个回调函数,这个函数在 Action 运行时被调用。其次,我们需要把这个函数挂载到对应的 Action 钩子上面。

我们可以使用 add_filter() 函数挂载一个回调函数到 Filter 钩子上面,add_filter 函数至少需要两个参数:$tag (字符) 和 $function_to_add (回调函数名)。下面的例子将在 the_title Filter 执行时运行。

<?php
function wporg_filter_title($title) {
    return '文章:' . $title . '已被修改。';
}
add_filter('the_title', 'wporg_filter_title');

假设我们有一篇标题为“学习 WordPress 插件开发” 的文章,上面的例子将会在显示标题时把标题修改为 “文章:学习 WordPress 插件开发已被修改”,我们可以上文获取可用的钩子列表。随着开发经验的日益增长,我们可以通过查看 WordPress 核心、主题或插件的源代码找到更多有用的钩子。

其他参数

add_filter() 也可以接受两个额外的参数,$priority (整数) 规定了回调函数执行的优先级,和 $accepted_args (整数) 规定了传递给回调函数的参数数量。有关这些参数的详细说明,请阅读关于 Action 的章节。

示例

在 <body> 满足特定条件时,向标签添加 CSS 类:

<?php
function wporg_css_body_class($classes) {
    if (!is_admin()) {
        $classes[] = 'wporg-is-awesome';
    }
    return $classes;
}
add_filter('body_class', 'wporg_css_body_class');

自定义钩子

一个非常重要但经常被忽略的做法是在我们可以在插件中使用自定义钩子,以便其他开发者可以扩展或修改我们的插件。自定义钩子的创建和使用方式和 WordPress 核心钩子相同。

创建一个自定义钩子

我们使用 do_action() 为创建 Action 钩子,使用 apply_filters() 创建 Filter 钩子。

建议在输出到浏览器的文本上使用 apply_filters(),特别是在前端,这可以方便用户根据需求修改插件输出。

挂载回调函数到自定义钩子

我们使用add_action() 添加回调函数到自定义 Action 钩子上,使用 add_filter() 添加回调函数到自定义 Filter 钩子上。

命名冲突

由于任何主题或插件都可以创建自定义钩子,为了避免与钩子名称冲突,我们应该在钩子名称前添加一个自定义前缀,这一点非常重要。例如,名为 email_body 的 Filter 就非常容易产品冲突,因为其他插件开发人员可能发会选择相同 Filter 名称,如果用户同时安装了这两个插件,就可能会导致难以追踪的错误。

给 Filter 名称加一个前缀,如 wporg_email_body(其中 wporg_ 是我们插件的唯一前缀),会避免和其他插件产生 Filter 名称冲突。

示例

可扩展 Action:设置表单

如果我们的插件添加了一个设置表单到 “仪表盘” 中,我们可以使用自定义 Action 来允许其他插件开发者添加他们自己的设置到我们的插件中。

<?php
function wporg_settings_page_html()
{
   ?>
    Foo: <input id=foo name=foo type=text>
    Bar: <input id=bar name=bar type=text>
   <?php
   do_action('wporg_after_settings_page_html');
}

如下,另外一个插件开发者挂载了一个回调函数到 wporg_after_settings_page_html  Action 上面,来添加新设置。

<?php
function myprefix_add_settings()
{
   ?>
    New 1: <input id=new_setting name=new_settings type=text>
   <?php
}
add_action('wporg_after_settings_page_html', 'myprefix_add_settings');

可扩展 Filter:自定义文章类型

在下面的例子中,当我们注册自定义文章类型时,把文章类型的参数传递给了一个自定义 Filter,另外一个插件开发者可以在创建自定义文章类型之前修改这个参数。

<?php
function wporg_create_post_type()
{
   $post_type_params = [/* ... */];

   register_post_type(
      'post_type_slug',
      apply_filters('wporg_post_type_params', $post_type_params)
   );
}

现在,另外一个插件开发者可以挂载一个回调函数到 wporg_post_type_params Filter 上面,来修改自定义文章类型的参数。

<?php
function myprefix_change_post_type_params($post_type_params)
{
   $post_type_params['hierarchical'] = true;
   return $post_type_params;
}
add_filter('wporg_post_type_params', 'myprefix_change_post_type_params');

外部资源

高级主题

删除挂载到 Action 和 Filter 上的回调函数

有时候,我们需要删除一个注册到一个插件、主题甚至是 WordPress 核心的钩子上的回调函数。这时,我们可以使用 remove_action() 删除挂载到 Action 上的回调函数,使用 remove_filter() 删除挂载到 Filter 上的回调函数。传递给 remove_action 和 remove_filter 的参数应该和使用 add_action 和 add_filter 函数注册他们的参数相同。

要成功删除回调函数,必须在注册回调函数后执行删除操作,执行顺序很重要。

示例

举个例子,我们需要删除不必要的功能来提高大型主题的性能。让我们来看一下主题代码的 function.php

<?php
function my_theme_setup_slider()
{
   // ...
}
add_action('template_redirect', 'my_theme_setup_slider', 9);

这个主题的 my_theme_setup_slider 函数添加了一个我们不需要的幻灯模块,这个模块会加载一个非常大的 CSS 文件,然后初始化一个 JavaScript 文件,这个文件使用了 1MB 的自定义库,我们可以通过卸载这个功能来移除这个幻灯模块。

由于我们需要在 my_theme_setup_slider 回调函数注册后 (在 functions.php 中执行)卸载这个函数,执行这个卸载的最好的时机就是after_setup_theme钩子。

<?php
function wporg_disable_slider()
{
   // make sure all parameters match the add_action() call exactly
   remove_action('template_redirect', 'my_theme_setup_slider', 9);
}
// make sure we call remove_action() after add_action() has been called
add_action('after_setup_theme', 'wporg_disable_slider');

删除所有回调

我们可以使用 remove_all_actions() 和 remove_all_filters() 来删除挂载到某个钩子上面的所有回调。

确定当前钩子

有时候,我们需要在多个钩子上挂载同一个回调函数,在回调函数中,我们需要根据它所挂载到的钩子来确定对应的操作。我们可以使用  current_action() / current_filter() 来确定当前的 Action 或 Filter。

<?php
function wporg_modify_content($content)
{
   switch (current_filter()) {
      case 'the_content':
         // do something
         break;
      case 'the_excerpt':
         // do something
         break;
   }
   return $content;
}
add_filter('the_content', 'wporg_modify_content');
add_filter('the_excerpt', 'wporg_modify_content');

检查钩子是否已经运行了

一些钩子在执行过程中可能会被多次调用,但是我们只希望它被调用一次,在这种情况下,我们可以使用 did_action() 来检查钩子运行了多少次。

<?php
function wporg_custom()
{
   if (did_action('save_post') !== 1) {
      return;
   }
   // ...
}
add_action('save_post', 'wporg_custom');

使用 “全部” 钩子进行调试

如果我们想要一个回调函数在每一个钩子上面都被触发,我们可以挂载回调函数到 ‘all’ 钩子上面。在进行调试时,这个技巧非常有用,可以帮助我们确定某个事件在什么时候发生,页面在什么时候崩溃等等。

本站提供 WordPress 主题定制开发服务

本站长期承接 WordPress 主题、插件、基于 WooCommerce 的商店商城开发业务。 我们有 8 年 WordPress 开发经验,如果你想 用 WordPress 开发网站, 请联系 QQ: 470266798,或邮箱: 4626395@gmail.com 咨询。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*