增强 WordPress 的 Hook 机制,支持运行前检测和只执行一次!
先简单介绍一下 WordPress 机制,这是 WordPress 最牛逼的地方,也是 WordPress 为什么那么灵活的原因,所有的插件和主题都是基于 Hook 机制来实现定制化,这种机制解耦了核心代码与扩展功能,使插件/主题能非侵入式地增强系统,同时保持高效维护性。
WordPress Hook 机制
WordPress Hook 主要由 Action(动作) 和 Filter(过滤器) 两类钩子构成,开发者可通过 add_action()
和 add_filter()
将自定义代码挂载到特定节点(如文章发布、页面渲染等),实现功能扩展或修改数据。例如:
- Filter:动态修改数据(比如
the_content
过滤器可修改文章内容)。 - Action:在关键流程节点插入操作(比如
publish_post
动作触发时发送邮件)。
以 add_filter
为例:
add_filter(
string $hook_name, // 钩子名称
callable $callback, // 回调函数
int $priority = 10, // 执行优先级
int $accepted_args = 1 // 接收参数数量
);
add_action 也是一样,它们都有四个参数,第一个是 Hook 名称(或者说:钩子名称),第二个是自定义代码的回调函数,第三个是优先级(priority
),默认是 10,数值越大执行越晚,最后一个是参数数量(accepted_args
),表示回调函数接受参数的个数,默认是 1 个。
比如下面代码就是把相关代码添加到文章内容后面:
add_filter('the_content', fn($content)=> $content.wpjam_get_related_posts(get_the_ID(), $args));
Hook 机制的问题
我们在使用 WordPress filter 和 action 的时候,就是在运行之前没有检测,无法在回调执行前进行条件判断,需要在回调内部实现条件逻辑,导致代码冗余,然后没有只执行一次的选项,如果有这方面的需求,都只能写在回调函数中。
什么意思呢?还是以上面的相关代码为例,上面只是简单将相关文章添加到文章内容后面,这样可能会造成一些问题,为什么?因为一般来说,相关文章只能在文章详情页加,比如博客首页,或者文章列表页,就不会展示相关文章:
add_filter('the_content', function($content){
if(is_single()){
return $content.wpjam_get_related_posts(get_the_ID(), $args);
}
return $content;
}
可能要更加精确一下,当前文章是详情页的哪篇文章才行:
add_filter('the_content', function($content){
if(is_single(get_the_ID())){
return $content.wpjam_get_related_posts(get_the_ID(), $args);
}
return $content;
}
如果详情页,在文章后面还会显示其他文章列表,由于某种原因也调用了 the_content
的 filter,那么我们可能希望当前的回调函数只执行一次,当前文章后面显示之后,就可以将该 filter 移除了。
WordPress 也提供了 remove_filter
函数实现删除 filte:
remove_filter( string $hook_name, callable|string|array $callback, int $priority = 10 )
但是 remove_filter
的时候第二个参数是回调函数名称,如果上面 add_filter
的时候使用的是匿名或者箭头函数,就很难移除了(需要你将匿名函数赋值给变量),那么 add_filter
的时候就要具体的函数名:
function wpjam_add_related_posts_to_content($content){
if(is_single(get_the_ID())){
remove_filter('the_content', 'wpjam_add_related_posts_to_content');
return $content.wpjam_get_related_posts(get_the_ID(), $args);
}
return $content;
}
add_filter('the_content', 'wpjam_add_related_posts_to_content');
这样就保证了代码只执行一次。
wpjam_add_filter / wpjam_add_action
上面这些需求,如果我们都在回调函数中实现,当面没有问题,那么有没有办法把这个过程简化呢?有的,我在 #WPJAM Basic# 写了个 add_filter
的增强函数 wpjam_add_filter
,比如上面的相关文章的处理,就可以这样实现:
wpjam_add_filter('the_content', [
'callback' => fn($content)=> $content.wpjam_get_related_posts(get_the_ID(), $args),
'check' => fn()=> is_single(get_the_ID()),
'nonce' => true
]);
wpjam_add_filter
相比 add_filter
,就是第二个参数改成 $args
,原来的回调函数,放到 $args
的 callback
中,然后增加 check
和 nonce
这两个参数,此外一样也有第三个参数优先级(priority
),和第四个是参数数量(accepted_args
),和原版函数 add_filter
是一样的,简单理解就是使用的时候,把第二个参数从 $callback
替换换成 $args
即可。
然后我们具体说说增加的 check
和 nonce
这两个参数:
check
参数用于运行前检查,只有返回 true
的时候,才真正去执行 callback
对应的回调参数,check
参数也是一个回调函数,对应的参数是和 callback
一致。
nonce
参数很简单,如果为 true
就是只执行一次,不用自己去 remove_filter
。
* add_action
也有对应的增强函数 wpjam_add_action
,使用方法和参数和 wpjam_add_filter
一样,这里就不再重复说明。
这样可以把实际执行的业务逻辑和条件检查分离,并且简化单次执行模式(简单通过选项设置即可),看起来也更加清晰,更优雅的代码组织结构,使用起来更加方便,代码的可维护性和可读性也得到了增强,最重要的是功能也更加强大!