阐述 WordPress `_wp_filter_active_plugins()` 函数的源码:如何过滤和激活插件列表。

咳咳,各位观众老爷,晚上好!我是你们今晚的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_dependenciestrue,防止重复执行依赖检查。

  • $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.phpplugin-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.phpplugin-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() 函数有了更深入的了解。记住,源码的世界充满了乐趣,只要你敢于探索,就能发现其中的奥秘。咱们下次再见!

发表回复

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