本文行文思路及示例代码来自 WPMUDEV 同名文章,感谢原作者。禁止转载。
Hook 机制是 事件驱动型架构 中相当举足轻重的一部分。总的来说,Hook 机制实际上为我们深度定制「 WordPress 」提供了一条捷径。
简言之,「事件驱动型架构」是一种「通过监听事件状态并作出反应」的设计。发布文章、编辑主题、安装插件……任何一个动作实质上都对应着一个「事件」的发生。而 Hook 就如同刚刚所描述的那样——在代码和实际操作中修出一条捷径,令我们可以根据自己所想而不断丰富和拓展 WordPress。
来举个简单的例子,「发布文章」就是一个再常见不过的示例——
publish_post除此之外当然还有对应的
delete_post等等(可在 WordPress Codex 上详阅各种 Hook 的具体作用)。可别小看 WordPress 所定义的这几个小「短语」,它们就是 WordPress 可被拓展的重要基础。毫不夸张地说,正是 Hook 让 WordPress 拥有了这么完备的生态圈(Ecosystem),以及数以万计的插件及主题。
再来说说为什么我们非用 Hook 不可?
还记得我们是怎么更新 WordPress 的吗?通常只需要点击仪表盘上的「立即更新」按钮,喝杯茶的功夫一切就搞定了。开发者们甚至不需要过多考虑为新版 WordPress 做出适配和调整——很多时候,连一行代码都不需要修改。这就是 Hook 的魅力所在,正因如此,WordPress 团队才能够通过仅更新一份代码,就让世界上近 10% 的网站不用担心程序本身所带来的安全风险。
究其本质,钩子(Hooks)是 WordPress 各种主题/插件 中的一些公有事件。
可我们为什么要叫它……「钩子」呢?
来看看这些场景:在发布文章的同时通知百度来爬下网站,或是在保存草稿后自动帮我获取第一张图片作为特色图像等等……开发者们总想着让自己的代码能够伴随着某个操作一起执行,正如一个「钩子」一样。
钩子总共分为两种类型:
我们在之后会介绍它们之间的区别。
Actions 和 Filters 基本上可以互换着用——它们都是通过一定的「参数」来获取数据的,只是实现的逻辑和写法略有不同(如果你详细读过 WordPress 的核心代码,即会发现 Action Hooks 实际上就是由 Filter Hooks 实现的 )。
不过它们还有一项非常关键的区别:Filter hooks 必须返回一个实际值(value),Action Hooks 则不需要。
在实际场景中,Filter Hooks 通常更专注于「内容」方面。例如说,Filter Hook
title_save_pre是作用于文章标题的,
content_save_pre就是作用于文章内容的。另一方面,Action Hooks 顾名思义,它偏向于「在 WordPress 做某件事时执行相应的动作」,还是拿
publish_post为例,这个 Action Hook 就是在「任意一篇文章被发表时」被触发。
基本语句如下:
add_action( $hook, $function_to_add, $priority, $accepted_args );
其中的
$hook就是你所指定的钩子,
$function_to_add就是在「钩子满足条件被触发时」所执行的相关函数。后面的
$priority, $accepted_args先不管,我们以后再说。
稍安勿躁,等会我会介绍怎么把 Action Hook 用于具体的场景中。
Filter Hook 就是在「我只想获取/修改某个内容」时所使用。
基本语句如下:
add_filter( $hook, $function_to_add, $priority, $accepted_args );
和之前的
add_action超像对吧?同样地,
$hook就是你所指定的钩子,
$function_to_add就是在「钩子满足条件被触发时」所执行的相关函数。
诶等等,我干嘛要没事干删除掉好不容易定义的钩子?可别忘了 WordPress 许多核心功能就是由一个一个钩子所建立联系的。删除钩子好比带走了队伍中的某几个人,即使他们不在了——也不会影响到整个队伍的连续性。
remove_action( $hook, $function_to_remove, $priority); remove_filter( $hook, $function_to_remove, $priority);
注:所有 Filter Hooks 及 Action Hooks 都可以在 WordPress Codex 中被找到。Hookr.io 上收录了不少 Hooks 的使用示例,有时比官方还全哦。
在写完这篇文章时,WordPress 已有超过 1900 个可被使用的钩子……只有你搜不到,没有它做不到。
使用场景:给每篇文章后面加一条链接。
实现过程:
从 WordPress Codex 中了解到,
content_save_pre可以实现「动态更改内容」的目的(Allow you to sanitize content prior to saving it in the database)。
代码示例:
function add_attribution_backlink( $content ) { // Process content here and add the backlink $backlink = ‘<div class=”attribution”>This article first appeared on <a href=”http://premium.wpmudev.org/blog”>WPMU Dev Blog</a></div>’; $content = $content . $backlink; return $content; } add_filter( 'content_save_pre', ‘add_attribution_backlink’, 10, 1 );
大功告成啦!
等等!那
add_filter后面多出的
10,1是什么?这就是之前我们没介绍到的
$priority和
$accepted_args。
设想:如果有很多个地方都添加同一个 Hook ,他们会怎么决定执行的先后顺序呢?这时
$priority即起到作用。所设定的数值越小,则越靠前被执行;反之则越延后。这也就是为什么许多代码片段会给
$priority设定出莫名其妙的
9999——实际上就是令其最后一个被执行。
这个应该很好理解,就是所被 Hook 的函数中所需要传递的参数。例如本例子的参数就是
$content,在
add_filter被设置为 1。
使用场景:实现 SEO 插件中的「更改网站标题」功能
实现过程:
例如我的网站:「WP酷 – 最前沿的 WordPress 资源聚合平台」,如果我想更改其中的分隔符「 – 」呢?
通过查阅文档得知,
title_save_pre可以实现「动态更改标题」的目的。大同小异,仿照 「示例 #1」即可写出相应代码。不过需要注意的是:Filter Hook 必须返回一个有效的值哦!
代码示例:
add_filter( ‘title_save_pre’, ‘add_sitename_to_title’, 10, 2 ); add_sitename_to_title( $title, $sep ) { // You might want to check whether the title has been added already before appending the title (again) /* Retrieve site name. */ $name = get_bloginfo( ‘name’ ); /* Append site name to $title. */ $title .= $sep . ‘ ‘ . $name; /* Return the title. */ return $title; }
使用场景:在发布文章后自动发布微博、说说……
使用过程:
还是
publish_post……猜都可以知道这个钩子是在 WordPress 后台点击「发布」按钮时所执行的(An action triggered whenever a post is published, or if it is edited and the status is changed to publish.)。
function publish_post_to_social_media($post_ID) { global $post; // 发条说说 blabla... // 发个微博 blabla... } add_action('publish_post', 'publish_post_to_social_media');
就这么简单。
使用场景:在用户完成注册后发送一条欢迎邮件
使用过程:
after_signup_user是「在用户注册之后」所触发的动作,不再赘述。
代码示例:
function send_welcome_email( $user_data ) { global $service_locator; $firstName = $user_data['first_name']; $lastName = $user_data['last_name']; $email_content = " Hello $firstName,欢迎来到 WP酷。 "\r\n" 这是一封欢迎邮件。 …" } add_action(‘after_signup_user’, ‘send_welcome_email’, 10, get_userdata( $user_id ) );
注意到了吗?
add_action所指定的函数并没有像之前那样需要返回一个固定值。
如果你细心观察,许多的主题中都会出现
<?php wp_head(); ?>或是
<?php wp_footer(); ?>此类的身影。在看完本文后或许就能明白:这两个函数是为主题的可定制性所准备的——开发者们可以通过 Functions.php 直接将函数 Hook 到
wp_head等之类的位置上。包括各类 SEO 插件、编辑器插件等等等等,它们都是通过 Hooks 达到「不修改主题本身」却实现了许多新功能的效果。