咳咳,各位观众老爷,晚上好!我是你们今晚的WordPress源码解读员,咱们今儿个聊聊WordPress里一个挺重要,但又容易被忽略的小家伙——_wp_filter_active_plugins()
。这家伙专门负责过滤和激活插件列表,就像个精明的门卫,决定哪些插件能进门干活,哪些得在外面凉快会儿。
准备好了吗?咱们这就开始扒它的底裤,看看它到底是怎么干活的。
1. 亮相:_wp_filter_active_plugins()
是个啥?
首先,我们要明确,_wp_filter_active_plugins()
是一个内部函数,意味着它主要供WordPress核心代码使用,插件开发者通常不会直接调用它。它的职责就是根据当前站点的配置,筛选出真正应该激活的插件列表。
2. 源码剖析:一行一行地看它怎么表演
咱们直接上代码,然后一句一句地分析:
/**
* Filters the active plugins based on site and network activation states.
*
* @since 4.6.0
* @access private
*
* @global WP_Plugin_Dependencies $wp_plugin_dependencies
*
* @param array $active_plugins An array of active plugins.
* @return array Filtered array of active plugins.
*/
function _wp_filter_active_plugins( $active_plugins ) {
global $wp_plugin_dependencies;
$network_plugins = (array) get_site_option( 'active_sitewide_plugins', array() );
if ( is_multisite() ) {
$current_site_id = get_current_blog_id();
$site_plugins = get_option( 'active_plugins' );
if ( ! is_array( $site_plugins ) ) {
$site_plugins = array();
}
$network_plugins = array_keys( $network_plugins );
$active_plugins = array_intersect( $active_plugins, $site_plugins );
foreach ( $network_plugins as $plugin ) {
if ( ! in_array( $plugin, $active_plugins, true ) ) {
$active_plugins[] = $plugin;
}
}
if ( is_main_site() ) {
// On the main site, network activated plugins are always active.
} else {
// On sub-sites, network activated plugins can be deactivated individually.
$deactivated_network_plugins = get_option( 'deactivated_sitewide_plugins', array() );
$deactivated_network_plugins = (array) $deactivated_network_plugins;
foreach ( $deactivated_network_plugins as $plugin ) {
$key = array_search( $plugin, $active_plugins, true );
if ( false !== $key ) {
unset( $active_plugins[ $key ] );
}
}
}
}
/*
* Check that any dependencies are loaded first.
*
* Only run this check once to avoid any infinite loops.
*/
static $checked_dependencies = false;
if ( ! $checked_dependencies && class_exists( 'WP_Plugin_Dependencies' ) ) {
$checked_dependencies = true;
$active_plugins = $wp_plugin_dependencies->check_dependencies( $active_plugins );
}
return $active_plugins;
}
好,咱们开始逐行解读:
-
function _wp_filter_active_plugins( $active_plugins ) {
:函数定义,接收一个$active_plugins
数组作为参数,这个数组包含了所有理论上应该激活的插件。 -
global $wp_plugin_dependencies;
:声明全局变量$wp_plugin_dependencies
,这个变量是WP_Plugin_Dependencies
类的实例,用于处理插件依赖关系。 -
$network_plugins = (array) get_site_option( 'active_sitewide_plugins', array() );
:从站点选项中获取网络激活的插件列表(仅在多站点环境下有效)。get_site_option()
函数用于获取整个网络共享的选项。如果找不到 ‘active_sitewide_plugins’ 选项,就返回一个空数组。 -
if ( is_multisite() ) { ... }
:判断是否是多站点环境。如果不是多站点,就跳过这部分逻辑。 -
$current_site_id = get_current_blog_id();
:获取当前站点的 ID。 -
$site_plugins = get_option( 'active_plugins' );
:获取当前站点激活的插件列表。get_option()
函数用于获取当前站点的选项。 -
if ( ! is_array( $site_plugins ) ) { $site_plugins = array(); }
:确保$site_plugins
是一个数组。 -
$network_plugins = array_keys( $network_plugins );
:将$network_plugins
数组的键名提取出来,因为get_site_option('active_sitewide_plugins')
返回的是一个键值对数组,键是插件文件名,值通常是时间戳(表示激活时间)。这里只需要插件文件名。 -
$active_plugins = array_intersect( $active_plugins, $site_plugins );
:取$active_plugins
和$site_plugins
的交集。这意味着只有同时在两个数组中出现的插件才会被保留。 这个操作确保了站点级别的激活设置优先于原始的激活列表。如果一个插件在站点级别被禁用,即使它最初在$active_plugins
中,也会被移除。 -
foreach ( $network_plugins as $plugin ) { ... }
:遍历网络激活的插件列表。 -
if ( ! in_array( $plugin, $active_plugins, true ) ) { $active_plugins[] = $plugin; }
:如果某个网络激活的插件不在$active_plugins
中,就把它添加到$active_plugins
中。 这确保了网络激活的插件总是会被激活,除非它们在子站点上被显式地禁用。true
参数用于in_array()
函数,表示进行严格类型比较。 -
if ( is_main_site() ) { ... } else { ... }
:根据当前站点是否是主站点,执行不同的逻辑。// On the main site, network activated plugins are always active.
:在主站点上,网络激活的插件总是激活的。// On sub-sites, network activated plugins can be deactivated individually.
:在子站点上,网络激活的插件可以被单独禁用。
-
$deactivated_network_plugins = get_option( 'deactivated_sitewide_plugins', array() );
:获取当前站点禁用的网络激活插件列表。 -
$deactivated_network_plugins = (array) $deactivated_network_plugins;
:确保$deactivated_network_plugins
是一个数组。 -
foreach ( $deactivated_network_plugins as $plugin ) { ... }
:遍历禁用的网络激活插件列表。 -
$key = array_search( $plugin, $active_plugins, true );
:在$active_plugins
中查找禁用插件的键名。 -
if ( false !== $key ) { unset( $active_plugins[ $key ] ); }
:如果找到了禁用插件,就从$active_plugins
中移除它。 -
static $checked_dependencies = false;
:声明一个静态变量$checked_dependencies
,用于控制依赖检查只运行一次。 -
if ( ! $checked_dependencies && class_exists( 'WP_Plugin_Dependencies' ) ) { ... }
:如果依赖检查还没有运行,并且WP_Plugin_Dependencies
类存在,就执行依赖检查。 -
$checked_dependencies = true;
:设置$checked_dependencies
为true
,防止重复执行依赖检查。 -
$active_plugins = $wp_plugin_dependencies->check_dependencies( $active_plugins );
:调用$wp_plugin_dependencies
对象的check_dependencies()
方法,检查插件依赖关系。这个方法会根据插件的Plugin URI
头部信息中指定的依赖关系,从$active_plugins
列表中移除任何未满足依赖的插件。 -
return $active_plugins;
:返回经过过滤后的$active_plugins
数组。
3. 多站点环境下的玄机
多站点是 WordPress 的一个高级特性,允许你用一个 WordPress 安装来管理多个网站。 在多站点环境下,插件的激活状态会更加复杂,因为插件可以在以下几个层面激活:
- 网络激活 (Network Activated): 插件对整个网络的所有站点都生效。
- 站点激活 (Site Activated): 插件只对特定的站点生效。
_wp_filter_active_plugins()
函数在多站点环境下,主要做了以下几件事:
- 优先考虑站点激活状态: 只有在站点选项
active_plugins
中存在的插件,才会被认为是激活的。 - 合并网络激活插件: 将网络激活的插件添加到激活列表中,除非它们在子站点上被显式地禁用。
- 允许子站点禁用网络插件: 子站点可以通过
deactivated_sitewide_plugins
选项来禁用网络激活的插件。
可以用一个表格来总结:
插件状态 | 主站点行为 | 子站点行为 |
---|---|---|
网络激活 (Network Activated) | 始终激活 | 默认激活,但可以通过 deactivated_sitewide_plugins 选项禁用 |
站点激活 (Site Activated) | 仅对当前站点激活 | 仅对当前站点激活 |
未激活 | 不激活 | 不激活 |
4. 插件依赖:不是你想激活就能激活
WordPress允许插件声明依赖关系。这意味着某个插件可能需要其他插件先激活,才能正常工作。_wp_filter_active_plugins()
函数会调用 WP_Plugin_Dependencies
类的 check_dependencies()
方法来检查这些依赖关系。
check_dependencies()
方法会解析插件的头部信息,查找 Requires Plugins
字段。这个字段包含了插件所依赖的其他插件的列表。如果某个插件的依赖没有被满足,那么它就会被从激活列表中移除。
例如,一个插件的头部信息可能包含以下内容:
/**
* Plugin Name: My Awesome Plugin
* Description: This plugin does amazing things.
* Version: 1.0.0
* Author: Me
* Requires Plugins: another-plugin/another-plugin.php, yet-another-plugin/yet-another-plugin.php
*/
这意味着 "My Awesome Plugin" 依赖于 "another-plugin" 和 "yet-another-plugin"。如果这两个插件没有激活,那么 "My Awesome Plugin" 就不会被激活。
5. 举个栗子:实战演练
假设我们有一个多站点环境,包含一个主站点和两个子站点。我们有三个插件:
plugin-a/plugin-a.php
: 一个普通的插件。plugin-b/plugin-b.php
: 一个网络激活的插件。plugin-c/plugin-c.php
: 一个需要plugin-a/plugin-a.php
才能运行的插件。
现在,我们来模拟一下 _wp_filter_active_plugins()
函数的执行过程:
-
主站点:
active_plugins
最初包含plugin-a/plugin-a.php
和plugin-c/plugin-c.php
。active_sitewide_plugins
包含plugin-b/plugin-b.php
。_wp_filter_active_plugins()
会将plugin-b/plugin-b.php
添加到active_plugins
中。check_dependencies()
会检查plugin-c/plugin-c.php
的依赖,因为plugin-a/plugin-a.php
已经激活,所以plugin-c/plugin-c.php
会被保留。- 最终,主站点的
active_plugins
包含plugin-a/plugin-a.php
,plugin-b/plugin-b.php
,plugin-c/plugin-c.php
。
-
子站点 1:
active_plugins
最初包含plugin-a/plugin-a.php
和plugin-c/plugin-c.php
。active_sitewide_plugins
包含plugin-b/plugin-b.php
。deactivated_sitewide_plugins
包含plugin-b/plugin-b.php
(假设我们在子站点 1 上禁用了网络激活的插件 B)。_wp_filter_active_plugins()
会将plugin-b/plugin-b.php
添加到active_plugins
中,但随后又因为在deactivated_sitewide_plugins
中找到而被移除。check_dependencies()
会检查plugin-c/plugin-c.php
的依赖,因为plugin-a/plugin-a.php
已经激活,所以plugin-c/plugin-c.php
会被保留。- 最终,子站点 1 的
active_plugins
包含plugin-a/plugin-a.php
,plugin-c/plugin-c.php
。
-
子站点 2:
active_plugins
最初包含plugin-c/plugin-c.php
。 (假设这个站点没有激活plugin-a
)active_sitewide_plugins
包含plugin-b/plugin-b.php
。_wp_filter_active_plugins()
会将plugin-b/plugin-b.php
添加到active_plugins
中。check_dependencies()
会检查plugin-c/plugin-c.php
的依赖,因为plugin-a/plugin-a.php
没有激活,所以plugin-c/plugin-c.php
会被移除。- 最终,子站点 2 的
active_plugins
包含plugin-b/plugin-b.php
。
6. 总结:门卫的智慧
_wp_filter_active_plugins()
函数虽然看起来简单,但它在 WordPress 的插件激活过程中扮演着至关重要的角色。它负责:
- 处理多站点环境下的激活状态: 确保网络激活的插件在主站点上总是激活的,并允许子站点禁用它们。
- 检查插件依赖关系: 确保插件的依赖项已经满足,避免插件因缺少依赖而无法正常工作。
- 确保站点级别的激活设置优先: 尊重站点管理员对插件的激活和禁用设置。
总而言之,_wp_filter_active_plugins()
就像一个精明的门卫,它知道哪些插件应该激活,哪些插件应该禁用,从而保证了 WordPress 站点的稳定性和可靠性。
好了,今天的讲座就到这里。希望通过今天的讲解,大家对 _wp_filter_active_plugins()
函数有了更深入的了解。记住,源码的世界充满了乐趣,只要你敢于探索,就能发现其中的奥秘。咱们下次再见!