增强 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,原来的回调函数,放到 $argscallback 中,然后增加 checknonce 这两个参数,此外一样也有第三个参数优先级(priority),和第四个是参数数量(accepted_args),和原版函数 add_filter 是一样的,简单理解就是使用的时候,把第二个参数从 $callback 替换换成 $args 即可。

然后我们具体说说增加的 checknonce 这两个参数:

check 参数用于运行前检查,只有返回 true 的时候,才真正去执行 callback 对应的回调参数,check 参数也是一个回调函数,对应的参数是和 callback 一致。

nonce 参数很简单,如果为 true 就是只执行一次,不用自己去 remove_filter

* add_action 也有对应的增强函数 wpjam_add_action,使用方法和参数和 wpjam_add_filter 一样,这里就不再重复说明。

这样可以把实际执行的业务逻辑和条件检查分离,并且简化单次执行模式(简单通过选项设置即可),看起来也更加清晰,更优雅的代码组织结构,使用起来更加方便,代码的可维护性和可读性也得到了增强,最重要的是功能也更加强大!


©我爱水煮鱼,本站推荐使用的主机:阿里云,国外主机建议使用BlueHost

本站长期承接 WordPress 优化建站业务,请联系微信:「chenduopapa」。