函数文档

register_deactivation_hook()

💡 云策文档标注

概述

register_deactivation_hook() 函数用于为插件设置停用钩子,当插件被停用时触发指定的回调函数。它基于插件文件名生成特定的 'deactivate_PLUGINNAME' 动作钩子,允许开发者在插件停用前执行清理或准备任务。

关键要点

  • 函数参数:$file(插件文件路径,必需)和 $callback(回调函数,必需)。
  • 钩子命名:自动生成 'deactivate_' 前缀加上插件基本名称(通过 plugin_basename() 获取),例如 'deactivate_sampleplugin/sample.php'。
  • 内部实现:调用 add_action() 将回调函数绑定到生成的停用钩子上。
  • 使用场景:适合在插件仍处于激活状态时执行复杂清理任务,如移除元数据或瞬态数据。
  • 与卸载钩子区别:register_deactivation_hook 在插件停用时运行,而 register_uninstall_hook 或 uninstall.php 在插件卸载时运行,后者更适合简单、广泛的清理操作。

代码示例

// 基本用法:在主插件文件中注册停用钩子
register_deactivation_hook( __FILE__, 'myplugin_deactivate' );

// 使用命名空间时的示例
namespace MYNAMESPACE;
register_deactivation_hook( __FILE__, __NAMESPACE__ . 'deactivate_plugin' );

// 使用类方法的示例,支持网络停用
register_deactivation_hook( __FILE__, array( 'MyPlugin', 'deactivate' ) );

class MyPlugin {
    public static function deactivate( $network_deactivating = false ) {
        if ( $network_deactivating && ! wp_is_large_network() ) {
            $_ids = get_sites( array( 'fields' => 'ids', 'number' => -1 ) );
            foreach ( $_ids as $_id ) {
                switch_to_blog( $_id );
                self::cleanup();
                restore_current_blog();
            }
        } else {
            self::cleanup();
        }
    }
    public static function cleanup() {
        // 清理插件特定数据
    }
}

注意事项

  • 在命名空间环境中,必须使用 __NAMESPACE__ 关键字指定回调函数完整路径,否则可能导致回调函数找不到的警告。
  • 停用钩子运行时插件仍处于激活状态,这为执行依赖插件内部功能的复杂任务提供了最后机会。
  • 对于自定义重写规则的清理,flush_rewrite_rules() 可能无效,因为插件仍处于激活状态,需要手动移除通过 add_rewrite_rule() 添加的规则。
  • 建议结合使用停用钩子和卸载钩子(或 uninstall.php),前者处理插件内部复杂任务,后者处理更广泛的清理如删除数据库表或文件。

📄 原文内容

Sets the deactivation hook for a plugin.

Description

When a plugin is deactivated, the action ‘deactivate_PLUGINNAME’ hook is called. In the name of this hook, PLUGINNAME is replaced with the name of the plugin, including the optional subdirectory. For example, when the plugin is located in wp-content/plugins/sampleplugin/sample.php, then the name of this hook will become ‘deactivate_sampleplugin/sample.php’.

When the plugin consists of only one file and is (as by default) located at wp-content/plugins/sample.php the name of this hook will be ‘deactivate_sample.php’.

Parameters

$filestringrequired
The filename of the plugin including the path.
$callbackcallablerequired
The function hooked to the 'deactivate_PLUGIN' action.

Source

function register_deactivation_hook( $file, $callback ) {
	$file = plugin_basename( $file );
	add_action( 'deactivate_' . $file, $callback );
}

Changelog

Version Description
2.0.0 Introduced.

User Contributed Notes

  1. Skip to note 4 content

    Examples
    If you have a function called myplugin_deactivate() in the main plugin file at either

    wp-content/plugins/myplugin.php or
    wp-content/plugins/myplugin/myplugin.php
    use this code:

    register_deactivation_hook( __FILE__, 'myplugin_deactivate' );

    This will call the myplugin_deactivate() function on deactivation of the plugin.

  2. Skip to note 5 content

    If you are using a namespace in the main plugin file
    namespace MYNAMESAPCE;

    you will need to use the __NAMESPACE__ keyword in your code for register_deactivation_hook.
    register_deactivation_hook( __FILE__ , __NAMESPACE__ . 'deactivate_plugin' );

    Otherwise, the code will be unable to find the function deactivate_plugin() and will produce a warning:
    PHP Warning: call_user_func_array() expects parameter 1 to be a valid callback, function ‘deactivate_plugin’ not found.

  3. Skip to note 6 content

    It is important to note that despite deactivation and uninstall hooks being available since WP 2.0 and 2.7 respectively, there are still a lot of plugins around that do not properly clean up after themselves, cluttering the database with useless data.

    A useful difference between register_deactivation_hook and both register_uninstall_hook and uninstall.php is that our deactivation hook callback is run when the plugin is still active. This means this is the last opportunity to run code that is integrally part of the plugin, without having to purposely load specific parts of the plugin or write very large uninstall routines.

    So preparing a plugin for a clean uninstall, you might need both these tools: (1) the deactivation action for complicated plugin-specific tasks that need plugin internals, for example removing metadata or transients named variably on user settings and (2) the uninstall action (or better: uninstall.php) to run broader / less complicated tasks like removing a known list of options with delete_option(), removing plugin specific database tables or (cache) files.

    An example code of an deactivation callback using a plugin internal method, prepared for both regular and network deactivation:

    register_deactivation_hook( __FILE__, array( 'MyPlugin', 'deactivate' ) );
    
    class MyPlugin {
    
    	// ...
    
    	/**
    	 * Clear plugin data that might be hard to find without the plugin being active.
    	 *
    	 * @since x.x
    	 */
    	public static function cleanup() {
    		// ...
    	}
    
    	/**
    	 * Plugin deactivation.
    	 *
    	 * @since x.x
    	 *
    	 * @param bool $network_deactivating Whether the plugin is network deactivated or not.
    	 */
    	public static function deactivate( $network_deactivating = false ) {
    		if ( $network_deactivating && ! wp_is_large_network() ) {
    			$_ids = get_sites(
    				array(
    					'fields' => 'ids',
    					'number' => -1,
    				)
    			);
    
    			foreach ( $_ids as $_id ) {
    				switch_to_blog( $_id );
    
    				self::cleanup();
    
    				restore_current_blog();
    			}
    		} else {
    			self::cleanup();
    		}
    	}
    }

    Note: one specific problem occurs when your plugin has custom rewrite rules and you need to do revert those on deactivation. A simple flush_rewrite_rules() will not work here, due exactly to the fact that the plugin is still active. You’ll need to take care to undo all plugin rewrite rules added with add_rewrite_rule() before flushing but sadly, a simple function like remove_rewrite_rule() does not exist (yet?)…