好的,各位听众,欢迎来到今天的“WordPress源码深度解析”小课堂!我是你们今天的讲师,老码农一枚。今天咱们聊点有意思的,关于WordPress插件加载的两个关键action:plugins_loaded 和 init。 这俩哥们儿,初学者容易搞混,但它们在WordPress启动过程中扮演的角色可是大相径庭。
开场白:别被表象迷惑!
很多新手朋友觉得,插件加载嘛,不就是把插件里的代码执行一遍吗? 看起来好像是这样,但WordPress内部的处理机制远比我们想象的要复杂。 就像看魔术一样,台上光鲜亮丽,台下可都是精密的机关。 plugins_loaded 和 init 这两个action,就是这些“机关”中的关键齿轮。
第一幕:WordPress的启动大戏
要理解plugins_loaded 和 init 的区别,我们先要对WordPress的启动流程有个大致的了解。 想象一下,WordPress的启动过程就像一场盛大的演出,各个角色(文件、函数、插件)按照剧本依次登场。
wp-config.php:奠定基石- 这是演出的总策划书,定义了数据库连接信息、调试模式等关键配置。
- WordPress首先读取这个文件,建立与数据库的连接。
wp-settings.php:加载核心文件- 这是演出的导演,负责加载核心文件、注册全局变量等。
- 它会加载
wp-load.php,然后依次加载各种核心函数和类。
wp-load.php:启动引擎- 这是演出的总调度,定义了WordPress的根目录、编码方式等。
- 它主要负责加载
wp-config.php,为后续的加载做好准备。
wp-includes/plugin.php:插件管理中心- 这是插件的登记处,负责激活、停用、加载插件。
- 它定义了
get_option('active_plugins')函数,获取已激活的插件列表。
- 开始执行主题和插件
- 主题和插件开始“登台演出”
- 主题的
functions.php和插件的代码会被加载和执行
第二幕:plugins_loaded:插件的集体亮相
好,现在重点来了。 plugins_loaded 这个action,就发生在核心文件加载完毕,但在主题和插件完全启动之前。 它的主要作用是:让所有已激活的插件加载它们的核心代码。
- 触发时机: 在
wp-settings.php中,require( ABSPATH . WPINC . '/plugin.php' );之后,do_action( 'plugins_loaded' );被调用。 - 作用: 插件可以在这个
action中注册全局变量、定义常量、加载核心类等。 - 特点: 这个
action的触发时机非常早,此时WordPress的核心功能(比如用户认证、文章类型)可能还没有完全加载完毕。
代码示例:
<?php
/**
* Plugin Name: My Awesome Plugin
* Description: A plugin that does awesome things.
*/
add_action( 'plugins_loaded', 'my_awesome_plugin_loaded' );
function my_awesome_plugin_loaded() {
// 定义常量
define( 'MY_AWESOME_PLUGIN_VERSION', '1.0.0' );
// 加载核心类
require_once plugin_dir_path( __FILE__ ) . 'includes/class-my-awesome-plugin.php';
// 创建插件实例
$my_plugin = new My_Awesome_Plugin();
}
注意事项:
- 由于
plugins_loaded触发时机较早,因此不要在这个action中依赖WordPress的核心功能。 比如,不要尝试获取当前用户的信息,因为用户认证可能还没有完成。 - 尽量在这个
action中完成插件的基础设置,为后续的操作做好准备。
第三幕:init:万事俱备,只欠东风
init 这个action,发生在WordPress核心功能加载完毕之后,但在页面渲染之前。 它的主要作用是:让插件和主题执行更复杂的操作,比如注册文章类型、添加自定义字段、处理用户请求等。
- 触发时机: 在
wp()函数中,do_action( 'init' );被调用。 - 作用: 插件和主题可以在这个
action中执行各种操作,比如注册文章类型、添加自定义字段、处理用户请求等。 - 特点: 这个
action的触发时机相对较晚,此时WordPress的核心功能已经完全加载完毕。
代码示例:
<?php
/**
* Plugin Name: My Awesome Plugin
* Description: A plugin that does awesome things.
*/
add_action( 'init', 'my_awesome_plugin_init' );
function my_awesome_plugin_init() {
// 注册自定义文章类型
register_post_type( 'my_awesome_post', array(
'labels' => array(
'name' => __( 'Awesome Posts', 'my-awesome-plugin' ),
'singular_name' => __( 'Awesome Post', 'my-awesome-plugin' ),
),
'public' => true,
'has_archive' => true,
'supports' => array( 'title', 'editor', 'thumbnail' ),
) );
// 添加自定义字段
add_meta_box(
'my_awesome_meta_box',
__( 'Awesome Meta Box', 'my-awesome-plugin' ),
'my_awesome_meta_box_callback',
'my_awesome_post',
'normal',
'default'
);
}
function my_awesome_meta_box_callback( $post ) {
// 显示自定义字段
echo '<label for="my_awesome_field">' . __( 'Awesome Field:', 'my-awesome-plugin' ) . '</label>';
echo '<input type="text" id="my_awesome_field" name="my_awesome_field" value="' . esc_attr( get_post_meta( $post->ID, 'my_awesome_field', true ) ) . '" />';
}
注意事项:
- 由于
init触发时机较晚,因此可以在这个action中安全地依赖WordPress的核心功能。 - 尽量在这个
action中完成插件的业务逻辑处理。
第四幕:源码分析:追根溯源
光说理论还不够,我们来扒一扒WordPress的源码,看看plugins_loaded 和 init 到底是怎么被触发的。
plugins_loaded 的触发:
打开wp-settings.php,你会发现:
<?php
// Loads the WordPress environment
if ( !isset( $wp_did_header ) ) {
$wp_did_header = true;
require_once( dirname(__FILE__) . '/wp-load.php' );
wp();
require_once( ABSPATH . WPINC . '/template-loader.php' );
}
再打开wp-load.php,你会发现
<?php
/**
* Bootstrap file for loading the WordPress environment.
*
* @package WordPress
*/
if ( ! isset( $table_prefix ) )
$table_prefix = 'wp_';
define('WPINC', 'wp-includes');
require( dirname(__FILE__) . '/wp-config.php' );
require_once( ABSPATH . 'wp-includes/load.php' );
require_once( ABSPATH . 'wp-includes/default-constants.php' );
require_once( ABSPATH . 'wp-includes/plugin.php' );
// Sets up the WordPress vars and included files.
require_once( ABSPATH . 'wp-settings.php' );
关键的代码是require_once( ABSPATH . WPINC . '/plugin.php' );之后,在wp-settings.php中,你会看到:
do_action( 'plugins_loaded' );
这行代码就是触发plugins_loaded 的关键! 它告诉WordPress: “嘿,所有插件,该加载你们的核心代码了!”
init 的触发:
init 的触发稍微复杂一点,它藏在wp()函数里面。 wp()函数位于wp-includes/functions.php文件中。 打开这个文件,找到wp()函数,你会看到:
function wp() {
global $wp, $wp_query, $wp_the_query, $wp_rewrite, $wp_did_header;
$wp->main();
if ( ! isset( $wp_did_header ) ) {
$wp_did_header = true;
$wp->send_headers();
}
$wp_query->init();
$wp_the_query->init();
/**
* Fires after WordPress has finished loading but before any headers are sent.
*
* @since 1.5.0
*/
do_action( 'init' );
/**
* Fires before the HTTP headers are sent to the browser.
*
* @since 2.1.0
*/
do_action( 'wp_loaded' );
}
看到do_action( 'init' );了吗? 这就是init 被触发的地方! 它告诉WordPress:“嘿,所有插件和主题,核心功能已经加载完毕,你们可以开始执行更复杂的操作了!”
第五幕:表格对比:一目了然
为了让大家更清晰地理解plugins_loaded 和 init 的区别,我们用一个表格来总结一下:
| 特性 | plugins_loaded |
init |
|---|---|---|
| 触发时机 | 核心文件加载完毕,但在主题和插件完全启动之前 | WordPress核心功能加载完毕之后,但在页面渲染之前 |
| 主要作用 | 加载插件的核心代码 | 执行更复杂的操作,比如注册文章类型、添加自定义字段 |
| 依赖核心功能 | 尽量避免依赖 | 可以安全地依赖 |
| 典型应用 | 定义常量、加载核心类、注册全局变量 | 注册文章类型、添加自定义字段、处理用户请求 |
| 源码位置 | wp-settings.php |
wp-includes/functions.php 中的 wp() 函数 |
| 触发时机相对顺序 | 更早 | 更晚 |
第六幕:实战演练:避免踩坑
了解了plugins_loaded 和 init 的区别,我们再来看几个实际的例子,避免大家在开发过程中踩坑。
坑1:在plugins_loaded 中获取当前用户信息
<?php
add_action( 'plugins_loaded', 'my_plugin_get_current_user' );
function my_plugin_get_current_user() {
$current_user = wp_get_current_user();
if ( $current_user->exists() ) {
// Do something with the current user
echo 'Hello, ' . $current_user->display_name . '!';
} else {
echo 'Not logged in.';
}
}
这段代码看似没问题,但实际上在plugins_loaded 触发时,用户认证可能还没有完成,wp_get_current_user() 可能返回一个空对象。 正确的做法是将这段代码放在init action中:
<?php
add_action( 'init', 'my_plugin_get_current_user' );
function my_plugin_get_current_user() {
$current_user = wp_get_current_user();
if ( $current_user->exists() ) {
// Do something with the current user
echo 'Hello, ' . $current_user->display_name . '!';
} else {
echo 'Not logged in.';
}
}
坑2:在init 中定义常量
虽然在init 中可以安全地依赖WordPress的核心功能,但不建议在这个action中定义常量。 因为常量通常需要在代码的最开始就定义,以便在后续的代码中使用。 如果将常量定义放在init 中,可能会导致一些代码无法访问到这些常量。 正确的做法是将常量定义放在plugins_loaded action中。
总结:各司其职,高效协作
plugins_loaded 和 init 这两个action,就像一个团队里的两个重要成员,各司其职,高效协作,共同完成了WordPress的启动过程。 plugins_loaded 负责打基础,加载核心代码,init 负责执行业务逻辑,处理用户请求。 只有充分理解了它们的作用和特点,才能在WordPress开发中游刃有余,写出高质量的插件和主题。
课后作业:
- 编写一个插件,分别在
plugins_loaded和initaction中输出一段文字,观察它们的执行顺序。 - 尝试在
plugins_loaded中注册一个自定义文章类型,看看会发生什么。
好啦,今天的课程就到这里。希望大家能够学有所获,在WordPress开发的道路上越走越远! 咱们下次再见!