WordPress 钩子只有两种:Action 在特定时机「触发事件、不返回值」,Filter 把数据「过一道手、必须 return 」。记住 do_action() / add_action() 配 Action,apply_filters() / add_filter() 配 Filter,就掌握了 WordPress 全部扩展能力的入口。钩子也是为什么你能在不改一行核心代码的前提下,把博客改成电商站。
钩子到底是什么
WordPress 在核心代码的关键位置预埋了「挂钩点」。比如 wp-settings.php 里有这么一行:
do_action( 'init' );
这行代码定义了一个名为 init 的 Action 钩子。你用 add_action() 挂一个自己的函数上去,函数就会在这个位置被执行——效果等同于把代码写进了核心文件,但核心文件一行没动。
do_action() 或 apply_filters() 触发;挂上去的函数叫 callback(回调)。严格来说「 Hooks 」指事件,「 Actions/Filters 」指回调,官方文档里也经常混用,理解意思即可。Action 与 Filter 的核心区别
| 对比项 | Action(动作) | Filter(过滤器) |
|---|---|---|
| 声明方式 | do_action( 'hook_name', $arg ) | $val = apply_filters( 'hook_name', $val ) |
| 注册方式 | add_action() | add_filter() |
| 返回值 | 不需要,返回值被丢弃 | 必须 return,返回值会被继续使用 |
| 典型用途 | 输出 HTML 、发送邮件、注册文章类型、加载脚本 | 改标题、改正文、改查询结果、改配置数组 |
| 常见例子 | init、wp_enqueue_scripts、save_post | the_content、the_title、body_class |
最容易踩的坑就是 Filter 忘记 return:函数执行了,但数据原样返回,表现为「改了没效果」,排查半天。
三个参数:名字、回调、优先级与参数个数
add_filter( 'the_content', 'my_prefix_add_note', 10, 1 );
function my_prefix_add_note( $content ) {
if ( ! is_single() ) {
return $content; // Filter 必须 return,早返回也算
}
return $content . '<p>感谢阅读!</p>';
}
- 优先级(第三个参数):默认 10,数字越小越早执行。同一钩子上多个回调按优先级排序,同优先级按注册顺序。
- 参数个数(第四个参数):默认 1 。想用第 2 、 3 个参数必须显式声明,否则收不到——这是第二个高频坑。
多参数钩子的正确写法
save_post 为例,它会传 $post_id、$post、$update 三个参数。要用第三个,必须写 add_action( 'save_post', 'my_fn', 10, 3 ),回调签名写成 function my_fn( $post_id, $post, $update )。少写参数个数的后果是 PHP 报「参数太少」致命错误。动手:注册一个自定义钩子
- 确定类型:想让别人「在某时刻做点事」用 Action;想让别人「改你的数据」用 Filter 。输出任何前端文本前优先包一层 Filter 。
- 起个带前缀的名字:钩子名全局唯一,撞名会造成极难排查的 bug 。用公司名 / 插件名做前缀,例如
jinyu_after_card,别用email_body这种通用词。 - 用 do_action / apply_filters 声明:Action 写
do_action( 'jinyu_after_settings_html' );;Filter 写$params = apply_filters( 'jinyu_post_type_params', $params );并把返回值继续用下去。 - 写文档注释:注明钩子触发时机、传递哪些参数、 Filter 期望返回什么类型。没有文档的钩子等于没有。
- 验证:另写一个临时插件挂上去,确认触发时机与参数都符合预期,再删掉测试代码。
可扩展的 Filter 示例
function jinyu_create_post_type() {
$params = [
'public' => true,
'label' => '案例',
'supports' => [ 'title', 'editor', 'thumbnail' ],
];
// 注册前把参数交给 Filter,方便其他插件改写
register_post_type(
'jinyu_case',
apply_filters( 'jinyu_post_type_params', $params )
);
}
add_action( 'init', 'jinyu_create_post_type' );
另一个插件想改成层级结构,不用碰你的代码:
add_filter( 'jinyu_post_type_params', function ( $params ) {
$params['hierarchical'] = true;
return $params; // 别忘了 return
} );
调试钩子的两个实用手段
- 列出当前请求触发的所有钩子:临时加
add_action( 'all', function ( $tag ) { error_log( $tag ); } );,配合WP_DEBUG_LOG就能看到完整触发顺序。 - 查某钩子上挂了什么:用
global $wp_filter; print_r( $wp_filter['the_content'] );查看优先级与回调列表,定位「谁改了我的输出」。
remove_filter() / remove_action() 的优先级必须与注册时完全一致,且必须在回调注册之后执行。常见错误是在插件文件顶层直接 remove,此时对方插件还没加载,移除失败。包一层 add_action( 'init', ... ) 通常能解决。相关阅读
- WordPress Abilities API 是什么:让插件声明能力、被 AI 工具发现调用
- WordPress REST API 进阶实战:自定义端点、鉴权与批量操作
- WordPress theme.json 配置手册:区块主题一站式调校
add_action 和 add_filter 是同一个函数吗?
add_filter(),add_action() 只是它的别名封装,数据结构完全一致。区别只在语义和用法约定:Action 不关心返回值,Filter 必须返回。Filter 里 echo 输出会怎样?
优先级该设多少?
子主题里能用钩子改父主题吗?
functions.php 会在父主题之前加载,两者都会执行,因此用 remove_action() 卸载父主题的回调再挂自己的,比复制整个模板文件更稳、升级也更安全。参考来源:WordPress 官方 Plugin Handbook(Hooks / Custom Hooks)、 Block Editor Handbook(Filters and hooks)与 Learn WordPress 钩子课程。本文为基于上述公开文档的原创改写与实战整理。







评论 (0)