WordPress函数:remove_action 移除已注册的动作钩子
编辑文章简介
remove_action 用于从特定的动作钩子(Action Hook)中移除之前通过 add_action 注册的回调函数,允许你禁用或修改其他插件、主题或核心功能的行为。
语法
函数定义于 wp-includes/plugin.php。它内部会调用 WP_Hook 类的相关方法来移除指定的回调函数。
remove_action( string $hook_name, callable $callback, int $priority = 10 )
| 参数 | 类型 | 必需 | 默认值 | 说明 |
|---|---|---|---|---|
$hook_name |
字符串 | 是 | 无 | 要从中移除回调函数的动作钩子名称。 |
$callback |
可调用函数 | 是 | 无 | 要移除的回调函数。必须与 add_action 注册时的函数完全相同(函数名或对象方法引用)。 |
$priority |
整数 | 否 | 10 | 要移除的回调函数的优先级。必须与 add_action 注册时的优先级完全一致。 |
返回值:布尔值。成功移除返回 true,失败返回 false。
用法
基础用法
移除 WordPress 核心、主题或其他插件添加的特定功能。常见场景是移除不必要的脚本、样式或管理界面元素。
例如,你想移除 WordPress 后台管理栏中的”我的站点”菜单(针对多站点网络):
// 在主题的 functions.php 或自定义插件中
add_action( 'init', 'remove_admin_bar_items' );
function remove_admin_bar_items() {
// 确保用户已登录且有管理权限
if ( ! current_user_can( 'manage_options' ) ) {
return;
}
// 移除"我的站点"菜单
remove_action( 'admin_bar_menu', 'wp_admin_bar_my_sites_menu', 20 );
}
移除 WordPress 默认的 Emoji 支持,以提高前端性能:
// 在主题的 functions.php 中
add_action( 'init', 'disable_emoji_support' );
function disable_emoji_support() {
// 移除前端相关的 Emoji 动作
remove_action( 'wp_head', 'print_emoji_detection_script', 7 );
remove_action( 'admin_print_scripts', 'print_emoji_detection_script' );
remove_action( 'wp_print_styles', 'print_emoji_styles' );
remove_action( 'admin_print_styles', 'print_emoji_styles' );
// 移除 feed 中的 Emoji
remove_filter( 'the_content_feed', 'wp_staticize_emoji' );
remove_filter( 'comment_text_rss', 'wp_staticize_emoji' );
// 移除电子邮件中的 Emoji
remove_filter( 'wp_mail', 'wp_staticize_emoji_for_email' );
}
进阶用法
在特定条件下动态移除回调函数,或者在插件/主题开发中提供可配置的钩子移除机制。
场景:你正在开发一个插件,需要根据用户设置移除 WooCommerce 的某些默认功能:
class WooCommerce_Customizer {
public function __construct() {
add_action( 'wp_loaded', array( $this, 'setup_customizations' ) );
}
public function setup_customizations() {
// 获取用户设置
$settings = get_option( 'my_woocommerce_settings', array() );
// 根据设置移除相应的动作
if ( isset( $settings['disable_product_reviews'] ) && $settings['disable_product_reviews'] ) {
$this->remove_product_reviews();
}
if ( isset( $settings['disable_related_products'] ) && $settings['disable_related_products'] ) {
$this->remove_related_products();
}
if ( isset( $settings['custom_checkout_fields'] ) && $settings['custom_checkout_fields'] ) {
$this->customize_checkout_fields();
}
}
private function remove_product_reviews() {
// 移除 WooCommerce 的评论功能
remove_action( 'woocommerce_after_shop_loop_item', 'woocommerce_template_loop_rating', 5 );
remove_action( 'woocommerce_single_product_summary', 'woocommerce_template_single_rating', 10 );
remove_action( 'woocommerce_product_tabs', 'woocommerce_reviews_tab', 30 );
remove_action( 'woocommerce_product_tab_panels', 'woocommerce_reviews_panel', 30 );
// 还需要从产品类型中移除评论支持
add_filter( 'woocommerce_product_tabs', array( $this, 'remove_reviews_tab' ), 98 );
}
private function remove_related_products() {
// 移除相关产品展示
remove_action( 'woocommerce_after_single_product_summary', 'woocommerce_output_related_products', 20 );
}
private function customize_checkout_fields() {
// 移除默认的结账字段,以便添加自定义字段
add_filter( 'woocommerce_checkout_fields', array( $this, 'override_checkout_fields' ), 999 );
}
public function remove_reviews_tab( $tabs ) {
unset( $tabs['reviews'] );
return $tabs;
}
public function override_checkout_fields( $fields ) {
// 移除账单地址中的公司字段
unset( $fields['billing']['billing_company'] );
// 移除送货地址中的地址2字段
unset( $fields['shipping']['shipping_address_2'] );
return $fields;
}
}
// 初始化自定义器
new WooCommerce_Customizer();
另一个场景:在主题开发中,根据页面模板或条件移除某些动作:
add_action( 'template_redirect', 'conditionally_remove_actions' );
function conditionally_remove_actions() {
// 只在单篇文章页面执行
if ( is_single() ) {
// 移除文章作者信息显示
remove_action( 'twenty_twenty_one_entry_meta_header', 'twenty_twenty_one_posted_by', 10 );
// 添加自定义的作者显示
add_action( 'twenty_twenty_one_entry_meta_header', 'custom_posted_by', 10 );
}
// 在首页移除侧边栏
if ( is_front_page() ) {
remove_action( 'twenty_twenty_one_sidebar', 'twenty_twenty_one_sidebar', 10 );
}
// 在 WooCommerce 产品页面移除特定的脚本
if ( is_product() ) {
// 移除 WooCommerce 的某些脚本
add_action( 'wp_enqueue_scripts', 'remove_woocommerce_scripts', 100 );
}
}
function custom_posted_by() {
// 自定义作者显示逻辑
printf(
'<span class="byline">%s <span class="author vcard"><a class="url fn n" href="%s">%s</a></span></span>',
esc_html_x( '由', 'post author', 'my-theme' ),
esc_url( get_author_posts_url( get_the_author_meta( 'ID' ) ) ),
esc_html( get_the_author() )
);
}
function remove_woocommerce_scripts() {
// 移除 WooCommerce 的灯箱脚本
wp_dequeue_script( 'prettyPhoto' );
wp_dequeue_script( 'prettyPhoto-init' );
wp_dequeue_style( 'woocommerce_prettyPhoto_css' );
}
易错点
- 移除时机不正确:最常见的问题是尝试在回调函数执行之后才移除它。必须在回调函数执行之前调用
remove_action。通常需要在init、wp_loaded或更早的钩子中执行移除操作,具体取决于目标回调函数被添加的时间。 - 优先级不匹配:如果
add_action时指定了优先级(第三个参数),那么remove_action时必须使用完全相同的优先级。默认优先级是10,但如果目标回调函数使用了不同的优先级,你必须指定相同的值。 - 回调函数引用不一致:当移除类方法时,必须使用与添加时完全相同的格式。如果使用数组(
array($object, 'method_name')),移除时也必须使用相同的数组格式。匿名函数(闭包)无法被移除,因为无法获得相同的引用。 - 移除未添加的回调函数:尝试移除一个从未添加过的回调函数不会产生错误,但会返回
false。这通常不是问题,但如果你依赖于返回值进行逻辑判断,需要注意。 - 移除核心功能导致意外后果:移除 WordPress 核心、主题或其他插件的功能可能导致意外行为或错误。在移除前,确保你了解该功能的作用,并测试移除后的兼容性。
- 在多站点环境中考虑不周:某些动作可能只在特定条件下添加,或者根据多站点配置有所不同。移除操作前应进行适当的条件检查。
最佳实践
确定正确的移除时机
了解目标回调函数是在何时被添加的,以确保在正确的时间移除它。使用 WordPress 的标准加载顺序作为参考。
add_action( 'after_setup_theme', 'remove_unwanted_theme_features' );
function remove_unwanted_theme_features() {
// 主题功能通常在 after_setup_theme 钩子中设置
// 所以在此之后移除是安全的
// 移除主题自定义背景支持
remove_theme_support( 'custom-background' );
// 移除主题的某种布局支持
remove_action( 'init', 'theme_specific_layout_setup' );
}
add_action( 'wp_loaded', 'remove_plugin_functionality' );
function remove_plugin_functionality() {
// 大多数插件在 init 或 wp_loaded 钩子中注册功能
// wp_loaded 在 init 之后,确保所有插件已加载
if ( class_exists( 'Some_Plugin_Class' ) ) {
// 移除插件添加的某个前端功能
remove_action( 'wp_enqueue_scripts', array( 'Some_Plugin_Class', 'enqueue_styles' ), 10 );
}
}
安全地移除回调函数
在移除回调函数之前进行检查,确保目标回调函数确实存在,避免不必要的操作或错误。
add_action( 'wp_loaded', 'safely_remove_actions' );
function safely_remove_actions() {
global $wp_filter;
// 检查钩子是否存在以及回调函数是否已注册
if ( isset( $wp_filter['wp_head'] ) ) {
// 检查特定的回调函数是否在优先级10处被添加
$callbacks = $wp_filter['wp_head']->callbacks;
if ( isset( $callbacks[10] ) ) {
foreach ( $callbacks[10] as $key => $callback ) {
// 查找并移除特定的函数
if ( is_string( $callback['function'] ) && 'unwanted_function' === $callback['function'] ) {
remove_action( 'wp_head', 'unwanted_function', 10 );
break;
}
}
}
}
// 更简单的方法:使用 has_action 检查
if ( has_action( 'wp_footer', 'some_function_to_remove' ) ) {
remove_action( 'wp_footer', 'some_function_to_remove' );
}
}
创建可配置的移除机制
在插件或主题开发中,提供设置选项让用户选择要移除的功能,而不是硬编码移除逻辑。
class Configurable_Action_Remover {
private $removals = array();
public function __construct() {
// 从数据库获取配置
$this->removals = get_option( 'custom_action_removals', array() );
// 在适当的时机应用移除
add_action( 'wp_loaded', array( $this, 'apply_removals' ) );
}
public function apply_removals() {
foreach ( $this->removals as $hook => $callbacks ) {
foreach ( $callbacks as $callback => $priority ) {
// 使用 has_action 验证回调函数存在
if ( false !== has_action( $hook, $callback ) ) {
remove_action( $hook, $callback, $priority );
// 可选的:记录移除操作
if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
error_log( sprintf(
'Removed action: %s - %s (priority: %d)',
$hook,
is_string( $callback ) ? $callback : 'callback',
$priority
) );
}
}
}
}
}
// 在管理界面提供配置选项
public function register_settings() {
register_setting( 'reading', 'custom_action_removals' );
add_settings_section(
'action_removal_section',
'动作钩子移除设置',
array( $this, 'render_section_description' ),
'reading'
);
add_settings_field(
'action_removals',
'要移除的动作',
array( $this, 'render_removal_settings' ),
'reading',
'action_removal_section'
);
}
public function render_section_description() {
echo '<p>选择要从系统中移除的默认功能。</p>';
}
public function render_removal_settings() {
$options = array(
'wp_head' => array(
'print_emoji_detection_script' => '移除 Emoji 脚本',
'wp_generator' => '移除 WordPress 版本信息',
),
'admin_bar_menu' => array(
'wp_admin_bar_wp_menu' => '移除 WordPress 标志菜单',
'wp_admin_bar_my_sites_menu' => '移除"我的站点"菜单',
),
);
$current = get_option( 'custom_action_removals', array() );
foreach ( $options as $hook => $callbacks ) {
echo '<h4>' . esc_html( $hook ) . '</h4>';
foreach ( $callbacks as $callback => $label ) {
$checked = isset( $current[ $hook ][ $callback ] ) ? 'checked' : '';
echo '<label><input type="checkbox" name="custom_action_removals[' . esc_attr( $hook ) . '][' . esc_attr( $callback ) . ']" value="10" ' . $checked . '> ' . esc_html( $label ) . '</label><br>';
}
}
}
}
// 初始化
if ( is_admin() ) {
$remover = new Configurable_Action_Remover();
add_action( 'admin_init', array( $remover, 'register_settings' ) );
}
与现代开发模式结合
在区块编辑器(Gutenberg)或 REST API 开发中,remove_action 仍然有重要作用,例如移除默认的 REST API 端点或修改区块行为。
// 移除 WordPress 核心的某些 REST API 端点
add_action( 'rest_api_init', 'customize_rest_api', 20 );
function customize_rest_api() {
// 移除用户端点(如果需要)
remove_action( 'rest_api_init', 'create_initial_rest_routes', 0 );
// 但实际上,更常见的做法是通过权限控制来限制访问
// 或者使用 rest_endpoints 过滤器
add_filter( 'rest_endpoints', function( $endpoints ) {
// 移除 WordPress 核心的用户端点
if ( isset( $endpoints['/wp/v2/users'] ) ) {
unset( $endpoints['/wp/v2/users'] );
}
// 移除特定文章的修订版本端点
if ( isset( $endpoints['/wp/v2/posts/(?P<id>[\d]+)/revisions'] ) ) {
unset( $endpoints['/wp/v2/posts/(?P<id>[\d]+)/revisions'] );
}
return $endpoints;
} );
}
// 在区块编辑器中移除某些功能
add_action( 'enqueue_block_editor_assets', 'customize_block_editor' );
function customize_block_editor() {
// 移除核心区块的某些功能
add_filter( 'block_editor_settings_all', function( $settings ) {
// 通过 JavaScript 移除功能
wp_add_inline_script(
'wp-blocks',
'wp.hooks.addFilter("blocks.registerBlockType", "my-plugin/remove-block-supports", function(settings, name) {
if (name === "core/paragraph") {
// 移除段落区块的颜色支持
settings.supports = {
...settings.supports,
color: false
};
}
return settings;
});'
);
return $settings;
} );
}