插件是扩展 WordPress 的最佳方式。WordPress 之所以如此容易定制,是因为源代码中遍布着各种钩子。
想要在文章发布时立即执行某些操作?想要修改摘要长度?想要创建自己的页面浏览统计?只需要找到合适的钩子即可。
那么在插件中做同样的事情呢?钩子不仅适用于 WordPress,它们本质上是一种可以在自己工作中实现的设计模式。
在本文中,我将向你展示如何创建自己的钩子,让你或其他人可以轻松地在你的工作基础上进行扩展。
为什么我们需要钩子
为了展示钩子的强大功能,让我们来看一个现实世界的例子:Advanced Custom Fields。这个插件允许你为文章添加灵活的自定义数据字段;包括 Google 地图、数字字段、多选项等等。
这一切都是通过友好的用户界面完成的,你只需要在前端使用 get_field() 或 the_field() 这样的函数即可。截至目前还不错。
假设你正在构建一个允许人们销售东西的工具,提供一个价格字段和一个货币字段。然而在内部,无论用户选择显示什么货币,你都希望以美元存储价格。
如果 ACF(Advanced Custom Fields)没有任何钩子,这将很难实现,你可能需要使用 ACF 之外的机制来保存这个字段。幸运的是,我们有 acf/save_post 操作可以使用。你可以使用此操作在保存之前修改 $_POST 数据——这是将价格转换为美元的最佳时机。
这是一个非常实用的钩子示例,但还有另一个原因:面向未来和可扩展性。钩子有点像 API,它们为你指明方向;一个框架可以说是。如果你正在保存重要数据,你应该自动创建一个钩子,以便其他人在需要时可以操作数据。如果你正在显示对应用程序至关重要的内容,例如图片,请使用一个过滤器,允许用户定义显示的图片数量。
钩子内部工作原理
要创建我们自己的钩子,我们要做的事情与 WordPress 内部所做的完全一样——使用 do_action() 和 apply_filters() 函数来执行所有挂载的函数。让我们来看看 WordPress 源代码以了解这些函数是如何工作的。
WordPress 在内部使用 wp_trim_excerpt() 函数来创建摘要。在 WordPress 4.1 中,这个函数定义在 wp-includes/formatting.php 中,从第 2542 行开始。如果你关注第 2560 行,应该看到以下内容:
$excerpt_length = apply_filters( 'excerpt_length', 55 );
这意味着:运行所有挂载到 excerpt_length 钩子的函数,并返回最终值。假设你使用 3 个插件来修改摘要长度,会发生什么?让我们把这些函数集中起来看看:
add_filter( 'excerpt_length', 'plugin_a_excerpt_modify', 20 );
plugin_a_excerpt_modify( $length ) {
return $length - 10;
}
add_filter( 'excerpt_length', 'plugin_b_excerpt_modify', 32 );
plugin_b_excerpt_modify( $length ) {
return 104;
}
add_filter( 'excerpt_length', 'plugin_c_excerpt_modify', 16 );
plugin_c_excerpt_modify( $length ) {
return 20;
}
最终,摘要长度将为 104 个单词。过滤器按其优先级顺序执行。因此,插件 C 的函数最先执行。如果没有其他函数挂载,长度现在将是 20 个单词。接下来,插件 A 介入,长度变为 10。最后,插件 B 加入,使长度变为 104。
do_action() 使用完全相同的机制,执行所有绑定到定义为第一个参数的钩子的函数。
请注意,这个模式是完全封闭的,它不依赖于任何特殊定义。你可以在任何地方使用 do_action( 'lol_hook' ),最坏的情况下,没有函数绑定到 lol_hook 。换句话说,我们已经有了创建自己钩子的基础设施!
创建我们自己的钩子
正如我上面提到的,我们只需要确保使用 do_action() 和/或 add_filter,并确保记录它。这样程序员和用户实际上会知道它的存在。
让我们假设我们正在创建一个从数据库直接提取的最新上传图片库,类似这样:
$args = array(
'post_type' => 'attachment',
'post_status' => 'any',
'orderby' => 'date',
'order' => 'DESC',
'posts_per_page' => 10,
'fields' => 'ids'
);
$images = new WP_Query( $args );
$gallery = '['.'gallery ids="' . implode( ',', $images ) . '"'.']';
echo do_shortcode( $gallery );
在这个示例中,我们创建了一个查询,确保只检索图片,按日期排序并限制为10个结果。使用 fields 参数我确保返回的是一个ID数组。然后我根据这些信息构建了一个普通的WordPress图库短代码。
在这里添加钩子有两种方法。你可以简单地将其添加到 $args 数组定义之前,像这样:
$posts_per_page = apply_filters( 'my_gallery/posts_per_page', 10 );
在这种情况下,你需要在数组中使用 $posts_per_page 变量。也许更好的解决方案是让整个数组被修改。这将允许其他插件(或你,在未来)添加分类、标签和其他限制——下面是完整代码:
$args = array(
'post_type' => 'attachment',
'post_status' => 'any',
'orderby' => 'date',
'order' => 'DESC',
'posts_per_page' => 10,
'fields' => 'ids'
);
$args = apply_filters( 'my_gallery/query_args', $args );
$images = new WP_Query( $args );
$gallery = '['.'gallery ids="' . implode( ',', $images ) . '"'.']';
echo do_shortcode( $gallery );
这就是全部内容。其他插件现在可以使用 my_gallery/query_args 钩子来修改创建的图库功能。
关于命名的一点说明:正斜杠根本不是必需的。你可以使用 ‘my_plugin_query_args’ 或任何其他格式。我更喜欢正斜杠,因为它清楚地表明了创建它的插件和它提供的功能。
结论
钩子非常强大,不仅可以用于扩展WordPress,还可以用于扩展你自己的插件甚至主题。你应该注意不要过度使用,在应用程序的每个角落都添加钩子。
我没有分别为订单、文章状态、post_type 和 posts_per_page 添加钩子,而是添加了一个可以一次修改整个数组的过滤器。其他数据片段可能没有意义添加钩子,这最终取决于你。
祝你在创建更模块化和可扩展的应用程序时交好运,如果你之前在某个很酷的地方使用过自己的钩子,请告诉我们!




