阐述 WordPress `register_activation_hook()` 函数的源码:它如何将钩子函数注册到插件激活时执行。

WordPress 插件激活钩子:register_activation_hook() 源码解剖

各位观众,晚上好!我是你们今晚的 WordPress 钩子解剖师,今天我们来一起深入研究 WordPress 插件开发中一个非常重要的函数:register_activation_hook()。 别担心,虽然名字听起来有点吓人,但只要我们一层一层地剥开它的“外衣”,你会发现它其实也没那么神秘。

插件激活:故事的开端

首先,让我们先来聊聊“插件激活”这个概念。想象一下,你安装了一个新的 WordPress 插件,就像给你的网站安装了一个新的“零件”。但是,仅仅安装还不够,你还需要“激活”它,才能让这个“零件”开始工作。激活插件通常意味着执行一些初始化操作,比如创建数据库表、设置默认选项等等。

register_activation_hook() 函数就是用来注册一个“激活钩子”的,这个钩子会在插件激活时被触发,从而执行你想要执行的初始化操作。

register_activation_hook():它的庐山真面目

好了,废话不多说,让我们直接来看看 register_activation_hook() 的源码。你可以在 wp-includes/plugin.php 文件中找到它。

/**
 * Registers a plugin's activation hook.
 *
 * @since 2.0.0
 *
 * @param string   $file      The filename of the plugin requesting the hook.
 * @param callable $function  The function to call on activation.
 */
function register_activation_hook( string $file, callable $function ) {
    global $wp_filter;

    $file = plugin_basename( $file );

    if ( ! isset( $wp_filter['activate_' . $file] ) ) {
        $wp_filter['activate_' . $file] = new WP_Hook();
    }

    add_action( 'activate_' . $file, $function );
}

看起来是不是很简单?让我们来逐行分析一下:

  1. 函数签名: function register_activation_hook( string $file, callable $function )

    • $file:插件的主文件路径,通常是插件目录下的主 PHP 文件,例如 my-awesome-plugin/my-awesome-plugin.php。 注意,这里要使用插件主文件的完整路径,从WordPress根目录开始。
    • $function:一个可调用的函数(callable),也就是你要在插件激活时执行的函数。它可以是一个普通函数、一个类的方法,甚至是一个匿名函数(闭包)。
  2. global $wp_filter;

    • 这行代码声明 $wp_filter 是一个全局变量。 $wp_filter 是 WordPress 核心用来存储所有已注册的过滤器和动作钩子的一个非常重要的数据结构。 你可以把它想象成一个巨大的“钩子登记簿”,记录着所有等待被触发的钩子。
  3. $file = plugin_basename( $file );

    • 这行代码使用 plugin_basename() 函数从 $file 路径中提取出插件的基本名称。例如,如果 $filemy-awesome-plugin/my-awesome-plugin.php,那么 $file 经过这行代码处理后就会变成 my-awesome-plugin/my-awesome-plugin.php。 这样做是为了创建一个唯一的钩子名称,避免与其他插件的钩子冲突。
  4. if ( ! isset( $wp_filter['activate_' . $file] ) ) { ... }

    • 这部分代码检查是否已经存在名为 'activate_' . $file 的钩子。如果不存在(也就是第一次注册这个插件的激活钩子),它会创建一个新的 WP_Hook 对象,并将其添加到 $wp_filter 数组中。
    • WP_Hook 类是 WordPress 用来管理钩子的核心类。它负责存储所有与特定钩子关联的函数,并在钩子被触发时按优先级顺序执行这些函数。
  5. add_action( 'activate_' . $file, $function );

    • 这行代码是整个函数的关键所在。它使用 add_action() 函数将 $function 注册到 'activate_' . $file 动作钩子上。
    • add_action() 函数是 WordPress 中用来注册动作钩子的标准函数。它会将 $function 添加到与 'activate_' . $file 钩子关联的函数列表中。

register_activation_hook() 的工作原理:一步一步来

现在,让我们把上面的代码拆解成更易于理解的步骤,来理清 register_activation_hook() 的工作原理:

  1. 你调用 register_activation_hook() 函数,并传入插件文件路径和要执行的函数。

    例如:

    register_activation_hook( __FILE__, 'my_awesome_plugin_activate' );
    
    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(200) NOT NULL,
            text text NOT NULL,
            PRIMARY KEY  (id)
        ) $charset_collate;";
    
        require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
        dbDelta( $sql );
    
        add_option( 'my_awesome_plugin_version', '1.0' );
    }
  2. register_activation_hook() 函数会提取插件的基本名称,并创建一个唯一的钩子名称 'activate_' . $file

    在上面的例子中,如果你的插件主文件是 my-awesome-plugin/my-awesome-plugin.php,那么钩子名称就是 activate_my-awesome-plugin/my-awesome-plugin.php

  3. register_activation_hook() 函数会检查是否已经存在该钩子。如果不存在,则创建一个新的 WP_Hook 对象。

  4. register_activation_hook() 函数使用 add_action() 函数将你的函数注册到该钩子上。

  5. 当用户在 WordPress 后台激活你的插件时,WordPress 核心会触发 'activate_' . $file 钩子。

  6. WordPress 核心会遍历与该钩子关联的所有函数,并按优先级顺序执行它们。

    在我们的例子中,my_awesome_plugin_activate() 函数会被执行,从而创建数据库表并设置插件版本选项。

深入理解 WP_Hook 类:钩子的幕后英雄

正如前面提到的,WP_Hook 类是 WordPress 中用来管理钩子的核心类。它负责存储所有与特定钩子关联的函数,并在钩子被触发时按优先级顺序执行这些函数。

虽然我们不需要深入了解 WP_Hook 类的所有细节,但了解它的一些关键属性和方法可以帮助我们更好地理解 register_activation_hook() 的工作原理。

以下是 WP_Hook 类的一些关键属性:

  • callbacks: 一个多维数组,用于存储与钩子关联的所有函数。数组的键是函数的优先级,数组的值是具有相同优先级的所有函数的数组。
  • doing_action: 一个布尔值,指示当前钩子是否正在被执行。这可以防止无限循环。
  • merged_filters: 一个布尔值,指示是否已经将全局过滤器合并到当前钩子中。

以下是 WP_Hook 类的一些关键方法:

  • add_filter( $callback, $priority = 10, $accepted_args = 1 ): 将一个函数添加到钩子中。
  • remove_filter( $callback, $priority = 10 ): 从钩子中移除一个函数。
  • has_filter( $callback = false, $priority = false ): 检查钩子是否包含指定的函数。
  • apply_filters( $value ): 触发钩子,并按优先级顺序执行所有关联的函数。

register_activation_hook() 的使用场景:用武之地

register_activation_hook() 函数在插件开发中有很多用武之地。以下是一些常见的场景:

  • 创建数据库表: 如果你的插件需要使用自定义数据库表,你可以在激活钩子中创建这些表。

    register_activation_hook( __FILE__, 'my_plugin_create_database_table' );
    
    function my_plugin_create_database_table() {
        global $wpdb;
        $table_name = $wpdb->prefix . 'my_plugin_data';
        $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(200) NOT NULL,
          value text NOT NULL,
          PRIMARY KEY  (id)
        ) $charset_collate;";
    
        require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
        dbDelta( $sql );
    }
  • 设置默认选项: 你可以在激活钩子中设置插件的默认选项。

    register_activation_hook( __FILE__, 'my_plugin_set_default_options' );
    
    function my_plugin_set_default_options() {
        add_option( 'my_plugin_option_1', 'default_value_1' );
        add_option( 'my_plugin_option_2', 'default_value_2' );
    }
  • 创建 WordPress 页面: 你可以在激活钩子中创建一些 WordPress 页面,用于显示插件的内容。

    register_activation_hook( __FILE__, 'my_plugin_create_pages' );
    
    function my_plugin_create_pages() {
        $page_title = 'My Plugin Page';
        $page_content = 'This is the content of my plugin page.';
    
        $page = array(
            'post_title'    => $page_title,
            'post_content'  => $page_content,
            'post_status'   => 'publish',
            'post_type'     => 'page',
        );
    
        wp_insert_post( $page );
    }
  • 注册自定义文章类型: 你可以在激活钩子中注册自定义文章类型。

    register_activation_hook( __FILE__, 'my_plugin_register_custom_post_type' );
    
    function my_plugin_register_custom_post_type() {
        $labels = array(
            'name'               => _x( 'My Custom Posts', 'post type general name', 'my-plugin' ),
            'singular_name'      => _x( 'My Custom Post', 'post type singular name', 'my-plugin' ),
            'menu_name'          => _x( 'My Custom Posts', 'admin menu', 'my-plugin' ),
            'name_admin_bar'     => _x( 'My Custom Post', 'add new on admin bar', 'my-plugin' ),
            'add_new'            => _x( 'Add New', 'my-custom-post', 'my-plugin' ),
            'add_new_item'       => __( 'Add New My Custom Post', 'my-plugin' ),
            'new_item'           => __( 'New My Custom Post', 'my-plugin' ),
            'edit_item'          => __( 'Edit My Custom Post', 'my-plugin' ),
            'view_item'          => __( 'View My Custom Post', 'my-plugin' ),
            'all_items'          => __( 'All My Custom Posts', 'my-plugin' ),
            'search_items'       => __( 'Search My Custom Posts', 'my-plugin' ),
            'parent_item_colon'  => __( 'Parent My Custom Posts:', 'my-plugin' ),
            'not_found'          => __( 'No my custom posts found.', 'my-plugin' ),
            'not_found_in_trash' => __( 'No my custom posts found in Trash.', 'my-plugin' ),
        );
    
        $args = array(
            'labels'             => $labels,
            'public'             => true,
            'publicly_queryable' => true,
            'show_ui'            => true,
            'show_in_menu'       => true,
            'query_var'          => true,
            'rewrite'            => array( 'slug' => 'my-custom-post' ),
            'capability_type'    => 'post',
            'has_archive'        => true,
            'hierarchical'       => false,
            'menu_position'      => null,
            'supports'           => array( 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'comments' ),
        );
    
        register_post_type( 'my-custom-post', $args );
    
        flush_rewrite_rules(); // 刷新固定链接规则
    }

    注意: 在注册自定义文章类型后,需要调用 flush_rewrite_rules() 函数来刷新固定链接规则,否则你的自定义文章类型可能无法正常访问。

register_deactivation_hook():有始有终

register_activation_hook() 对应的是 register_deactivation_hook() 函数,它用于注册一个“停用钩子”,这个钩子会在插件停用时被触发。你可以在停用钩子中执行一些清理操作,例如删除数据库表、删除选项等等。

/**
 * Registers a plugin's deactivation hook.
 *
 * @since 2.0.0
 *
 * @param string   $file      The filename of the plugin requesting the hook.
 * @param callable $function  The function to call on deactivation.
 */
function register_deactivation_hook( string $file, callable $function ) {
    global $wp_filter;

    $file = plugin_basename( $file );

    if ( ! isset( $wp_filter['deactivate_' . $file] ) ) {
        $wp_filter['deactivate_' . $file] = new WP_Hook();
    }

    add_action( 'deactivate_' . $file, $function );
}

register_deactivation_hook() 函数的源码与 register_activation_hook() 函数非常相似,唯一的区别是它注册的是 deactivate_ 前缀的钩子。

以下是一个使用 register_deactivation_hook() 函数的例子:

register_deactivation_hook( __FILE__, 'my_plugin_delete_database_table' );

function my_plugin_delete_database_table() {
    global $wpdb;
    $table_name = $wpdb->prefix . 'my_plugin_data';

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

    delete_option( 'my_plugin_option_1' );
    delete_option( 'my_plugin_option_2' );
}

重要提示: 虽然你可以在停用钩子中删除数据库表,但这通常不是一个好主意。因为用户可能会重新激活你的插件,如果你的插件需要使用这些表,那么它们就不存在了。 更好的做法是保留数据库表,并在插件卸载时删除它们(使用 register_uninstall_hook() 函数)。

register_uninstall_hook():彻底告别

register_uninstall_hook() 函数用于注册一个“卸载钩子”,这个钩子会在插件被卸载时被触发。你可以在卸载钩子中执行一些彻底的清理操作,例如删除数据库表、删除选项等等。

重要提示: 卸载钩子只能通过一个名为 uninstall.php 的文件来定义。你需要创建一个名为 uninstall.php 的文件,放在插件的主目录下,并在该文件中定义你的卸载钩子。

以下是一个 uninstall.php 文件的例子:

<?php
// 如果 uninstall 不是从 WordPress 调用,则退出
if ( ! defined( 'WP_UNINSTALL_PLUGIN' ) ) {
    exit;
}

global $wpdb;
$table_name = $wpdb->prefix . 'my_plugin_data';

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

delete_option( 'my_plugin_option_1' );
delete_option( 'my_plugin_option_2' );

注意: register_uninstall_hook() 函数不需要显式调用。当用户从 WordPress 后台卸载你的插件时,WordPress 核心会自动查找并执行 uninstall.php 文件。

总结:钩子的力量

总而言之,register_activation_hook()register_deactivation_hook()register_uninstall_hook() 函数是 WordPress 插件开发中非常重要的工具。它们允许你在插件激活、停用和卸载时执行自定义代码,从而实现各种各样的功能。

理解这些函数的工作原理,可以帮助你更好地开发 WordPress 插件,并为用户提供更好的体验。

希望今天的讲座对你有所帮助! 谢谢大家!

发表回复

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