WordPress函数:register_activation_hook 创建插件激活函数
编辑文章简介
register_activation_hook 用于在插件被激活时执行一次性初始化代码,如创建数据库表、设置默认选项、检查环境依赖等,确保插件功能能够正常启动。
语法
函数定义于 wp-includes/plugin.php。当插件通过 WordPress 后台激活时,它会注册一个回调函数,该函数在插件激活过程中被执行。
register_activation_hook( string $file, callable $callback )
| 参数 | 类型 | 必需 | 默认值 | 说明 |
|---|---|---|---|---|
$file |
字符串 | 是 | 无 | 主插件文件的路径。通常使用 __FILE__ 魔术常量来引用当前文件。 |
$callback |
可调用函数 | 是 | 无 | 插件激活时要执行的回调函数。该函数不接受参数。 |
返回值:null。此函数不返回任何值,仅用于注册激活钩子。
用法
基础用法
在插件的主文件中,使用 register_activation_hook 注册一个函数,该函数将在插件首次激活时执行。常用于创建自定义数据库表和设置默认配置。
<?php
/**
* Plugin Name: 用户活动追踪插件
* Description: 记录用户的登录和操作活动。
*/
// 注册激活钩子
register_activation_hook( __FILE__, 'user_activity_tracker_activate' );
/**
* 插件激活时执行的初始化函数
*/
function user_activity_tracker_activate() {
global $wpdb;
// 定义表名(带前缀)
$table_name = $wpdb->prefix . 'user_activity_logs';
$charset_collate = $wpdb->get_charset_collate();
// 创建表的 SQL 语句
$sql = "CREATE TABLE IF NOT EXISTS $table_name (
id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
user_id bigint(20) UNSIGNED NOT NULL,
activity_type varchar(50) NOT NULL,
ip_address varchar(45),
user_agent text,
created_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
KEY user_id (user_id),
KEY activity_type (activity_type)
) $charset_collate;";
// 引入 WordPress 升级 API(包含 dbDelta 函数)
require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
dbDelta( $sql );
// 检查表是否成功创建
if ( $wpdb->get_var( "SHOW TABLES LIKE '$table_name'" ) != $table_name ) {
// 如果表创建失败,可以记录错误或触发警告
error_log( '用户活动日志表创建失败。' );
}
// 设置插件默认选项
$default_options = array(
'log_duration' => 90, // 默认保留90天日志
'track_login' => true,
'track_logout' => true,
);
// 如果选项不存在,则添加(避免覆盖用户可能已经设置的值)
if ( false === get_option( 'user_activity_tracker_options' ) ) {
add_option( 'user_activity_tracker_options', $default_options );
}
// 为多站点网络的每个站点执行激活(如果需要)
if ( is_multisite() ) {
// 注意:网络激活的处理方式不同,见进阶用法
}
}
进阶用法
处理多站点网络激活、执行环境检查、数据迁移以及与其他插件的兼容性检查。
register_activation_hook( __FILE__, 'advanced_plugin_activation' );
function advanced_plugin_activation( $network_wide ) {
// 参数 $network_wide 仅在 WordPress 4.6.0+ 中传递,表示是否网络范围激活
// 1. 环境检查
if ( version_compare( PHP_VERSION, '7.4.0', '<' ) ) {
deactivate_plugins( plugin_basename( __FILE__ ) );
wp_die( '本插件需要 PHP 7.4 或更高版本。您的服务器当前运行的是 ' . PHP_VERSION . '。请联系主机提供商升级 PHP。' );
}
if ( version_compare( get_bloginfo( 'version' ), '5.6', '<' ) ) {
deactivate_plugins( plugin_basename( __FILE__ ) );
wp_die( '本插件需要 WordPress 5.6 或更高版本。请先升级 WordPress。' );
}
// 2. 检查必要扩展
if ( ! extension_loaded( 'json' ) ) {
deactivate_plugins( plugin_basename( __FILE__ ) );
wp_die( '本插件需要 JSON 扩展。请联系服务器管理员启用 PHP JSON 扩展。' );
}
// 3. 多站点处理
if ( is_multisite() && $network_wide ) {
// 获取所有站点的 ID
$site_ids = get_sites( array( 'fields' => 'ids' ) );
foreach ( $site_ids as $site_id ) {
switch_to_blog( $site_id );
setup_plugin_for_site(); // 为每个站点执行初始化
restore_current_blog();
}
} else {
// 单站点或非网络激活
setup_plugin_for_site();
}
// 4. 刷新固定链接规则(如果插件注册了自定义文章类型)
flush_rewrite_rules( false ); // 参数 false 表示不立即更新 .htaccess,仅更新数据库规则
// 5. 设置定时任务
if ( ! wp_next_scheduled( 'my_plugin_daily_maintenance' ) ) {
wp_schedule_event( time(), 'daily', 'my_plugin_daily_maintenance' );
}
}
/**
* 为单个站点执行插件初始化
*/
function setup_plugin_for_site() {
global $wpdb;
// 创建表(同上例,但使用独立函数以便复用)
$table_name = $wpdb->prefix . 'my_plugin_data';
$charset_collate = $wpdb->get_charset_collate();
$sql = "CREATE TABLE IF NOT EXISTS $table_name (
id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
data_key varchar(100) NOT NULL,
data_value longtext,
site_id bigint(20) UNSIGNED NOT NULL DEFAULT 0,
PRIMARY KEY (id),
UNIQUE KEY data_key_site (data_key, site_id)
) $charset_collate;";
require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
dbDelta( $sql );
// 设置当前站点版本号
update_option( 'my_plugin_db_version', '1.0.0' );
}
易错点
- 错误的主文件路径:
$file参数必须是插件主文件的绝对路径。使用__FILE__是最可靠的方法。如果错误地使用了其他文件路径,激活钩子可能永远不会触发。 - 在激活钩子中直接输出内容:激活钩子执行期间,不应该使用
echo或print直接输出 HTML 或文本。这可能会破坏 WordPress 后台的激活流程,导致白屏或显示错误。如果必须通知用户,应使用wp_die()并带上适当的消息。 - 忘记处理多站点网络:如果插件支持多站点,激活钩子默认只在当前站点执行。如果用户选择“网络激活”,你需要遍历所有站点进行初始化。从 WordPress 4.6.0 开始,激活钩子回调函数会接收
$network_wide参数,指示是否是网络范围激活。 - 重复执行初始化逻辑:激活钩子只应在插件首次激活时运行。如果初始化代码没有检查是否已经初始化(例如,检查表是否存在或选项是否已设置),可能会导致重复创建表或覆盖用户设置。务必使用
IF NOT EXISTS或先进行检查。 - 在激活钩子中调用未定义的函数:激活钩子执行时,插件的其他部分可能尚未加载。如果激活函数调用了插件内定义的另一个函数,必须确保该函数在激活钩子之前已经定义,或者使用条件判断函数是否存在。
- 未处理激活失败的情况:如果初始化失败(如创建表失败),插件可能处于半激活状态。应该检查关键操作的结果,并在失败时触发错误或回滚,防止插件处于不稳定状态。
最佳实践
幂等性设计
确保激活钩子中的操作是幂等的,即无论执行多少次,结果都相同。这对于多站点环境、插件重新激活或故障恢复至关重要。
function my_plugin_activate() {
global $wpdb;
$table_name = $wpdb->prefix . 'my_plugin_table';
// 检查表是否存在,而不是盲目创建
if ( $wpdb->get_var( "SHOW TABLES LIKE '$table_name'" ) != $table_name ) {
// 表不存在,创建它
$charset_collate = $wpdb->get_charset_collate();
$sql = "CREATE TABLE $table_name (...) $charset_collate;";
require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
dbDelta( $sql );
}
// 使用 update_option 而不是 add_option,因为它会检查是否存在
$defaults = array( 'option1' => 'value1' );
$current = get_option( 'my_plugin_options', array() );
// 只添加缺失的默认值,不覆盖现有设置
$updated = wp_parse_args( $current, $defaults );
update_option( 'my_plugin_options', $updated );
}
版本控制与升级路径
在选项中存储插件的数据库版本,便于后续升级时检测并执行数据迁移。
function my_plugin_activate() {
$current_db_version = get_option( 'my_plugin_db_version', '0' );
// 初次安装
if ( '0' === $current_db_version ) {
create_initial_tables();
update_option( 'my_plugin_db_version', '1.0.0' );
}
// 注意:实际的升级逻辑应该放在一个单独的升级函数中,
// 并在每次插件加载时检查版本号,而不是仅在激活时。
// 这可以确保用户通过直接上传文件更新插件时也能执行升级。
}
// 更好的做法:在插件主文件中添加版本检查
function my_plugin_check_upgrade() {
$current_version = get_option( 'my_plugin_db_version', '0' );
if ( version_compare( $current_version, '1.1.0', '<' ) ) {
// 执行从 1.0.0 到 1.1.0 的升级
upgrade_to_1_1_0();
update_option( 'my_plugin_db_version', '1.1.0' );
}
if ( version_compare( $current_version, '1.2.0', '<' ) ) {
upgrade_to_1_2_0();
update_option( 'my_plugin_db_version', '1.2.0' );
}
}
add_action( 'plugins_loaded', 'my_plugin_check_upgrade' );
环境检查与优雅降级
在激活前进行全面的环境检查,如果不满足要求,则禁用插件并提供清晰的错误信息,而不是让插件在损坏状态下运行。
function my_plugin_activation_check() {
$errors = array();
// 检查 PHP 版本
if ( version_compare( PHP_VERSION, '7.4.0', '<' ) ) {
$errors[] = '需要 PHP 7.4 或更高版本。';
}
// 检查 WordPress 版本
if ( version_compare( get_bloginfo( 'version' ), '5.6', '<' ) ) {
$errors[] = '需要 WordPress 5.6 或更高版本。';
}
// 检查必要插件
if ( ! is_plugin_active( 'woocommerce/woocommerce.php' ) ) {
$errors[] = '需要 WooCommerce 插件。';
}
// 检查函数或类是否存在
if ( ! function_exists( 'some_required_function' ) ) {
$errors[] = '缺少必要的函数。';
}
if ( ! empty( $errors ) ) {
// 停用插件
deactivate_plugins( plugin_basename( __FILE__ ) );
// 显示错误信息
$error_message = '<h3>插件无法激活</h3><ul>';
foreach ( $errors as $error ) {
$error_message .= '<li>' . esc_html( $error ) . '</li>';
}
$error_message .= '</ul>';
wp_die( $error_message, '激活错误', array( 'back_link' => true ) );
}
}
register_activation_hook( __FILE__, 'my_plugin_activation_check' );
与卸载钩子配合
为每个在激活钩子中创建的资源(数据库表、选项、定时任务)在卸载钩子中提供清理方案。
// 激活钩子
register_activation_hook( __FILE__, 'my_plugin_activate' );
function my_plugin_activate() {
// 创建表、设置选项、添加定时任务...
wp_schedule_event( time(), 'daily', 'my_plugin_cron' );
}
// 卸载钩子
register_uninstall_hook( __FILE__, 'my_plugin_uninstall' );
function my_plugin_uninstall() {
global $wpdb;
// 删除自定义表
$table_name = $wpdb->prefix . 'my_plugin_table';
$wpdb->query( "DROP TABLE IF EXISTS $table_name" );
// 删除插件选项
delete_option( 'my_plugin_options' );
delete_option( 'my_plugin_db_version' );
// 清理定时任务
$timestamp = wp_next_scheduled( 'my_plugin_cron' );
if ( $timestamp ) {
wp_unschedule_event( $timestamp, 'my_plugin_cron' );
}
}