跳到主内容

WordPress 钩子(Hooks)怎么用:Action 与 Filter 的区别、优先级与自定义钩子实战

100%
WordPress 钩子(Hooks)怎么用:Action 与 Filter 的区别、优先级与自定义钩子实战

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() 挂一个自己的函数上去,函数就会在这个位置被执行——效果等同于把代码写进了核心文件,但核心文件一行没动。

Hook(钩子)是事件本身,由 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_postthe_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 报「参数太少」致命错误。

动手:注册一个自定义钩子


  1. 确定类型:想让别人「在某时刻做点事」用 Action;想让别人「改你的数据」用 Filter 。输出任何前端文本前优先包一层 Filter 。

  2. 起个带前缀的名字:钩子名全局唯一,撞名会造成极难排查的 bug 。用公司名 / 插件名做前缀,例如 jinyu_after_card,别用 email_body 这种通用词。

  3. 用 do_action / apply_filters 声明:Action 写 do_action( 'jinyu_after_settings_html' );;Filter 写 $params = apply_filters( 'jinyu_post_type_params', $params ); 并把返回值继续用下去。

  4. 写文档注释:注明钩子触发时机、传递哪些参数、 Filter 期望返回什么类型。没有文档的钩子等于没有。

  5. 验证:另写一个临时插件挂上去,确认触发时机与参数都符合预期,再删掉测试代码。

可扩展的 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
} );

调试钩子的两个实用手段

  1. 列出当前请求触发的所有钩子:临时加 add_action( 'all', function ( $tag ) { error_log( $tag ); } );,配合 WP_DEBUG_LOG 就能看到完整触发顺序。
  2. 查某钩子上挂了什么:用 global $wp_filter; print_r( $wp_filter['the_content'] ); 查看优先级与回调列表,定位「谁改了我的输出」。
移除回调时,remove_filter() / remove_action() 的优先级必须与注册时完全一致,且必须在回调注册之后执行。常见错误是在插件文件顶层直接 remove,此时对方插件还没加载,移除失败。包一层 add_action( 'init', ... ) 通常能解决。

相关阅读

延伸阅读:REST API 进阶实战

add_action 和 add_filter 是同一个函数吗?
底层是同一个。两者最终都调用 add_filter(),add_action() 只是它的别名封装,数据结构完全一致。区别只在语义和用法约定:Action 不关心返回值,Filter 必须返回。

Filter 里 echo 输出会怎样?
内容会直接打印到页面,且时机往往早于预期,破坏 HTML 结构;同时因为没 return,原数据丢失,页面出现空白或错乱。 Filter 只做「取数据、改数据、返回数据」三件事。

优先级该设多少?
默认 10 就够。想排在别人前面用更小的数(如 5),想覆盖别人的结果用更大的数(如 20 或 99)。不要为了「保险」一律设 999,会掩盖执行顺序问题,后期难维护。

子主题里能用钩子改父主题吗?
可以,而且是最推荐的方式。子主题的 functions.php 会在父主题之前加载,两者都会执行,因此用 remove_action() 卸载父主题的回调再挂自己的,比复制整个模板文件更稳、升级也更安全。

参考来源:WordPress 官方 Plugin Handbook(Hooks / Custom Hooks)、 Block Editor Handbook(Filters and hooks)与 Learn WordPress 钩子课程。本文为基于上述公开文档的原创改写与实战整理。

这篇有帮助吗?
云上的幻象
云上的幻象查看主页

七彩云博客,分享 WordPress 建站实战与 AI 工具测评,覆盖服务器运维、站长工具、软件资源与电商运营干货,专注原创实用的主题插件、网站加速与安全优化教程。

779文章4评论

相关文章

评论 (0)

欢迎你,新朋友,感谢参与互动!文明发言,理性交流 · 首次评论将在审核后展示