深入理解 WordPress `do_action(‘activate_plugin’)` 的源码:此钩子在何时被触发。

Alright, buckle up buttercups! 今天咱们来聊聊 WordPress 里一个挺重要但又有点神秘的钩子:do_action('activate_plugin')。它就像个低调的保安,默默守护着插件的激活过程。咱们要做的,就是把他从保安亭里拉出来,好好审视一番。

第一节:钩子,钩子,你是谁?

首先,得明确一点,do_action() 是 WordPress 钩子系统中的一个核心函数。 它负责触发一个动作钩子。想象一下,你在一条流水线上工作,每完成一个步骤,都会敲一下锣,告诉下一个工位的人:“嘿,我搞定了,轮到你了!” do_action() 就是那个敲锣的人,它告诉所有监听特定动作的函数:“该你们出场啦!”

do_action('activate_plugin') 就是一个特殊的锣。 当一个插件被成功激活后,这个锣就会被敲响。

第二节:activate_plugin 的身影:何时何地?

要搞清楚 do_action('activate_plugin') 何时被触发,最直接的方法就是… 钻到源码里去看! WordPress 的核心代码相对清晰,稍加耐心就能找到线索。

这个钩子的触发点主要集中在 wp-admin/includes/plugin.php 文件中的 activate_plugin() 函数里。 这个函数负责处理插件激活的逻辑。 让我们看看简化版的代码:

function activate_plugin( $plugin, $redirect = '', $network_wide = false ) {
    // ... 一堆安全检查和准备工作 ...

    do_action( 'activate_plugin', $plugin, $network_wide );

    // ... 一些清理和重定向工作 ...
}

看到了吗? do_action( 'activate_plugin', $plugin, $network_wide ); 这行代码正是触发钩子的关键。 它传递了两个参数:

  • $plugin: 激活插件的文件路径(例如:my-awesome-plugin/my-awesome-plugin.php)。
  • $network_wide: 一个布尔值,指示插件是在整个网络 (WordPress Multisite) 范围内激活,还是仅在当前站点激活。

重点: 这个钩子是在插件激活的核心逻辑执行完毕后,但在激活过程的最后清理和重定向之前触发的。 这意味着,你可以利用这个钩子执行一些在插件激活后需要立即执行的任务,比如:

  • 创建数据库表
  • 设置默认选项
  • 注册自定义文章类型
  • 等等…

第三节:监听锣声:如何利用 activate_plugin

现在我们知道 do_action('activate_plugin') 何时被敲响了,接下来就是如何监听这声锣响,并在钩子触发时执行我们自己的代码。 这就需要用到 add_action() 函数。

add_action() 函数的作用是把一个自定义函数(也称为回调函数)绑定到特定的动作钩子上。 当那个动作钩子被触发时,绑定的回调函数就会被执行。

让我们来写一个例子,假设我们想在插件激活时创建一个数据库表:

function my_awesome_plugin_activate( $plugin, $network_wide ) {
    global $wpdb;

    $table_name = $wpdb->prefix . 'my_awesome_table';
    $charset_collate = $wpdb->get_charset_collate();

    $sql = "CREATE TABLE $table_name (
        id mediumint(9) NOT NULL AUTO_INCREMENT,
        time datetime DEFAULT '0000-00-00 00:00:00' NOT NULL,
        name varchar(255) NOT NULL,
        value text,
        PRIMARY KEY  (id)
    ) $charset_collate;";

    require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
    dbDelta( $sql );
}
add_action( 'activate_plugin', 'my_awesome_plugin_activate', 10, 2 );

这段代码做了以下几件事:

  1. 定义了一个名为 my_awesome_plugin_activate 的函数。 这个函数接收 $plugin$network_wide 两个参数(即使我们在这个例子中没有用到它们,也必须接收,因为 activate_plugin 钩子会传递它们)。

  2. 在这个函数内部,我们构建了一个 SQL 语句,用于创建一个名为 wp_my_awesome_table 的数据库表(wp_ 是数据库表前缀,可以通过 $wpdb->prefix 获取)。

  3. 我们使用了 dbDelta() 函数来执行 SQL 语句。 dbDelta() 是 WordPress 提供的一个方便的函数,可以安全地创建或更新数据库表。

  4. 最后,我们使用 add_action() 函数把 my_awesome_plugin_activate 函数绑定到 activate_plugin 钩子上。 10 是优先级,2 是回调函数接收的参数数量(与 do_action 传递的参数数量一致)。

重要提示:

  • 确保你的 my_awesome_plugin_activate 函数定义在你的插件的主文件中,或者通过 require_once 引入。
  • 最好使用 dbDelta() 函数来创建或更新数据库表,因为它能处理数据库结构的更新,避免潜在的问题。
  • 不要在激活钩子中执行耗时的操作,因为这会影响用户体验。 如果需要执行耗时的操作,可以考虑使用 WP-Cron 或异步任务。

第四节: Multisite 的特殊性

在 WordPress Multisite 环境中,activate_plugin 钩子的行为会略有不同。 如果插件是在整个网络范围内激活的($network_widetrue),那么 activate_plugin 钩子会在每个站点上都触发一次。

这意味着,你的回调函数可能会被多次执行。 如果你需要在网络激活时执行一些全局性的操作,你需要检查 $network_wide 的值,以确保你的代码只执行一次。

例如:

function my_awesome_plugin_network_activate( $plugin, $network_wide ) {
    if ( $network_wide ) {
        global $wpdb;

        $current_blog = $wpdb->blogid;

        // Get all blog ids
        $blogids = $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" );

        foreach ( $blogids as $blog_id ) {
            switch_to_blog( $blog_id );
            // Do stuff for each site, for example:
            update_option( 'my_awesome_plugin_setting', 'default_value' );
        }

        switch_to_blog( $current_blog );
        return;
    }
    // Do stuff for single site activation
    update_option( 'my_awesome_plugin_setting', 'default_value' );
}
add_action( 'activate_plugin', 'my_awesome_plugin_network_activate', 10, 2 );

在这个例子中,如果 $network_widetrue, 我们会遍历所有站点,并在每个站点上设置 my_awesome_plugin_setting 选项。 最后,我们使用 switch_to_blog() 函数切换回当前站点。

第五节: 举一反三: deactivate_plugin 和 uninstall_plugin

既然聊到了 activate_plugin, 顺便提一下另外两个相关的钩子: deactivate_pluginuninstall_plugin

  • do_action('deactivate_plugin'): 在插件被停用时触发。 你可以使用它来清理插件激活时创建的资源,例如删除数据库表、删除选项等等。

  • register_uninstall_hook(__FILE__, 'uninstall_function');: 在插件被卸载时触发。 注意,这里使用的是 register_uninstall_hook 而不是 add_action。 这个钩子通常用于删除插件创建的所有数据,以便彻底清除插件的影响。 __FILE__ 是插件主文件的路径, uninstall_function 是你定义的卸载函数。

下面是一个 deactivate_plugin 的例子:

function my_awesome_plugin_deactivate() {
    // Do something when the plugin is deactivated.
    delete_option( 'my_awesome_plugin_setting' );
}
register_deactivation_hook( __FILE__, 'my_awesome_plugin_deactivate' );

下面是一个 uninstall_plugin 的例子:

function my_awesome_plugin_uninstall() {
    global $wpdb;
    $table_name = $wpdb->prefix . 'my_awesome_table';

    $sql = "DROP TABLE IF EXISTS $table_name";
    $wpdb->query( $sql );

    delete_option( 'my_awesome_plugin_setting' );
}
register_uninstall_hook( __FILE__, 'my_awesome_plugin_uninstall' );

第六节:实战案例:一个完整的插件激活示例

让我们把上面学到的东西整合起来,创建一个简单的插件,它会在激活时创建一个数据库表,在停用时删除它,在卸载时删除所有数据。

<?php
/**
 * Plugin Name: My Awesome Plugin
 * Description: A simple plugin to demonstrate activation, deactivation, and uninstall hooks.
 * Version: 1.0.0
 * Author: Your Name
 */

// Activation hook
function my_awesome_plugin_activate() {
    global $wpdb;

    $table_name = $wpdb->prefix . 'my_awesome_table';
    $charset_collate = $wpdb->get_charset_collate();

    $sql = "CREATE TABLE $table_name (
        id mediumint(9) NOT NULL AUTO_INCREMENT,
        time datetime DEFAULT '0000-00-00 00:00:00' NOT NULL,
        name varchar(255) NOT NULL,
        value text,
        PRIMARY KEY  (id)
    ) $charset_collate;";

    require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
    dbDelta( $sql );

    add_option( 'my_awesome_plugin_setting', 'default_value' );
}
register_activation_hook( __FILE__, 'my_awesome_plugin_activate' );

// Deactivation hook
function my_awesome_plugin_deactivate() {
    delete_option( 'my_awesome_plugin_setting' );
}
register_deactivation_hook( __FILE__, 'my_awesome_plugin_deactivate' );

// Uninstall hook
function my_awesome_plugin_uninstall() {
    global $wpdb;
    $table_name = $wpdb->prefix . 'my_awesome_table';

    $sql = "DROP TABLE IF EXISTS $table_name";
    $wpdb->query( $sql );

    delete_option( 'my_awesome_plugin_setting' );
}
register_uninstall_hook( __FILE__, 'my_awesome_plugin_uninstall' );

// Example of using the plugin
function my_awesome_plugin_example() {
    $setting = get_option( 'my_awesome_plugin_setting' );
    echo '<p>My Awesome Plugin Setting: ' . esc_html( $setting ) . '</p>';
}
add_action( 'wp_footer', 'my_awesome_plugin_example' );

把这段代码保存为一个 PHP 文件(例如 my-awesome-plugin.php),然后上传到 WordPress 的 wp-content/plugins 目录下。 激活插件后,你会发现数据库中多了一个名为 wp_my_awesome_table 的表,并且页面底部会显示 "My Awesome Plugin Setting: default_value"。 停用插件后, my_awesome_plugin_setting 选项会被删除。 卸载插件后,数据库表会被删除,所有数据都会被清除。

第七节:总结和注意事项

好啦,讲了这么多,咱们来总结一下关于 do_action('activate_plugin') 以及 deactivate_pluginregister_uninstall_hook的一些关键点:

  • do_action('activate_plugin') 在插件激活的核心逻辑执行完毕后,但在激活过程的最后清理和重定向之前触发。
  • add_action('activate_plugin', 'my_function', 10, 2); 用于监听 activate_plugin 钩子,并执行自定义函数。
  • 在 Multisite 环境中,activate_plugin 钩子可能会在每个站点上都触发一次。
  • 使用 register_deactivation_hook 来执行插件停用时的清理工作。
  • 使用 register_uninstall_hook 来执行插件卸载时的清理工作,确保彻底清除插件的影响。
  • 避免在激活、停用或卸载钩子中执行耗时的操作。
  • 始终注意安全性,避免 SQL 注入等安全漏洞。

掌握了这些知识,你就可以自信地利用 activate_plugin 钩子来增强你的 WordPress 插件的功能,并提供更好的用户体验。 记住,熟能生巧,多写代码,多实践,你就能成为 WordPress 插件开发的高手!

希望这次的讲座对你有所帮助。 以后再遇到类似的问题,就可以胸有成竹,迎刃而解啦! Happy coding!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注