WordPress插件钩子add_action和add_filter区别

文章导读
add_action 和 add_filter 的差别不在函数签名上,而在回调的职责和 WordPress 对返回值的处理上。写插件时先看目标钩子是怎么触发的:如果它是 do_action,通常只要求你执行一个动作,比如注册资源、发邮件、跳转或写日志;如果它是 apply_filters,通常要求你接收一个值、修改后再返回。选错类型最常见的后果是回调执行了但数据没变,或者过滤器返回空值把内容清掉。
📋 目录
  1. Ⅰ 先确认当前场景和目标
  2. Ⅱ 按这个顺序处理
  3. Ⅲ 改完后怎么验证
  4. Ⅳ 容易忽略的边界
A A

add_action 和 add_filter 的差别不在函数签名上,而在回调的职责和 WordPress 对返回值的处理上。写插件时先看目标钩子是怎么触发的:如果它是 do_action,通常只要求你执行一个动作,比如注册资源、发邮件、跳转或写日志;如果它是 apply_filters,通常要求你接收一个值、修改后再返回。选错类型最常见的后果是回调执行了但数据没变,或者过滤器返回空值把内容清掉。

判断方法可以先从钩子调用点入手:看目标是 do_action 还是 apply_filters,再看回调是否需要改写数据并返回。只做副作用用 add_action,不依赖返回值;要改标题、内容、查询参数等值用 add_filter,并且回调必须 return。两者优先级和参数数机制接近,但返回值处理不同;改完要检查目标输出、错误日志以及移除钩子时优先级和参数数是否一致。

先确认当前场景和目标

这个问题通常出现在你已经找到某个钩子名,但不确定该挂 action 还是 filter 的时候。先确认四件事:钩子名是否准确;它在当前主题或插件源码里是用 do_action 还是 apply_filters 调用的;调用时传了几个参数;你的回调是只想产生副作用,还是要改变某个值。不要只看钩子名字里有没有 action 或 filter,名字只是约定,真正决定用法的是调用方式。

  • 场景一:要在 wp_enqueue_scripts 里注册 CSS/JS,或 init 里注册自定义文章类型,这类是动作,用 add_action。
  • 场景二:要改 the_title、the_content、woocommerce_product_get_price 这类返回值,用 add_filter,并确保回调返回修改后的值。
  • 判断条件:如果你的回调不需要返回值也能完成目标,优先考虑 action;如果下游还要使用这个值,优先考虑 filter。
  • 还要确认钩子是否已被其他插件占用,以及你的回调是否需要比默认优先级更早或更晚执行。

按这个顺序处理

先定位调用点,再写回调,最后处理优先级和移除逻辑。这个顺序能减少“回调跑了但结果不对”的排查成本。

WordPress插件钩子add_action和add_filter区别
  1. 在目标插件或主题中搜索钩子名,看到 do_action('hook_name') 就用 add_action;看到 apply_filters('hook_name', $value) 就用 add_filter。
  2. 写回调时区分职责:action 回调专注执行副作用,不要靠 return 改变数据;filter 回调必须接收原值并返回新值,不需要修改时也要原样 return。
  3. 核对参数数量。add_action 和 add_filter 的第四个参数是 accepted_args,默认只接收 1 个参数;钩子传两个以上参数时,要显式写清楚。
  4. 设置优先级。默认是 10,数字越小越早执行;只有在确实需要抢在其他回调前后时才调整,调整后要记录原因。
  5. 需要移除时用 remove_action 或 remove_filter,并且优先级和参数数量要与注册时一致,否则可能移除不掉。
// 只执行副作用:注册资源,不需要返回值
add_action('wp_enqueue_scripts', 'myplugin_enqueue_assets');

function myplugin_enqueue_assets() {
    wp_enqueue_style('myplugin', plugins_url('style.css', __FILE__));
}

// 修改数据:必须接收原值并返回新值
add_filter('the_title', 'myplugin_mark_title', 10, 2);

function myplugin_mark_title($title, $post_id) {
    if (is_admin()) {
        return $title;
    }
    return $title . '(示例标记)';
}

// 移除时要保持优先级和参数数量一致
remove_action('wp_enqueue_scripts', 'myplugin_enqueue_assets');
remove_filter('the_title', 'myplugin_mark_title', 10, 2);

改完后怎么验证

验证要围绕“副作用是否发生”和“值是否按预期改变”分开看。动作钩子重点看执行结果是否出现且只出现一次;过滤器重点看目标位置的数据是否变化,同时确认没有影响其他位置。

  • 触发目标页面或后台操作,检查资源是否加载、邮件是否进入队列、自定义内容是否出现;如果动作重复执行,先查是否重复注册了钩子。
  • 对照过滤器修改前后的输出,确认只改动了预期字段;如果整段内容消失,优先检查回调是否漏写 return 或返回了 null。
  • 临时在回调里用 error_log 记录进入和返回值,配合 WP_DEBUG_LOG 查看;确认后移除调试代码,不要长期保留。
  • 检查 PHP 错误日志中是否有参数数量不匹配、回调类型错误或未定义变量;这些信号通常比页面表现更早暴露问题。
  • 移除自己注册的钩子后再触发一次,确认行为恢复,说明注册和移除逻辑是对称的。

容易忽略的边界

add_action 在实现上也会走 add_filter 的钩子机制,但 do_action 不会使用回调的返回值,所以给 action 回调写 return 通常不会改变数据。反过来,filter 回调如果返回 null 或 false,可能把标题、内容或价格清空,这类问题在页面上不一定立刻报错。

  • accepted_args 默认是 1,钩子传了多个参数却没有声明,回调可能拿不到后续参数,或者出现参数相关的警告。
  • remove_action 和 remove_filter 必须在注册之后执行,并且优先级、参数数量一致;在主题和插件加载顺序不确定时,移除时机要单独确认。
  • 不同 WordPress 版本或第三方插件对同一钩子的调用参数可能不同,不能只凭钩子名判断,要以当前环境里的 do_action 或 apply_filters 调用为准。
  • 高频钩子里不要做重查询或远程请求;后台权限判断要结合 is_admin 和 current_user_can 的实际场景,钩子本身不会替你处理权限。
  • 如果目标逻辑并不是标准钩子机制,或者必须直接改模板才能完成,不要硬套 add_action/add_filter;回退时先注释掉自己的注册代码,再恢复默认优先级和参数数量,确认原有行为。