解析 WordPress `get_site_option()` 函数的源码:在多站点模式下,如何获取网络级选项。

各位同学,晚上好! 今天咱们来聊聊 WordPress 多站点模式下的一个关键函数:get_site_option()。 相信不少同学都用过 get_option(),它用来获取某个站点的选项。 那么 get_site_option() 呢? 顾名思义,它用来获取 整个网络 的选项。 听起来是不是很厉害的样子? 别怕,咱们一点点解剖它,让它在你面前变得像个透明人。

一、 啥是网络选项? 为什么要用它?

首先,我们要搞清楚啥是“网络选项”。 在 WordPress 多站点模式下,你可以把它想象成一个“总开关”。 某些设置,你希望整个网络的所有站点都遵循同一个规则, 比如:

  • 网络管理员邮箱: 发送系统通知的邮箱。
  • 注册设置: 是否允许新用户/站点注册。
  • 上传文件类型限制: 允许上传的文件类型。

这些选项,如果每个站点都单独设置,管理起来会非常麻烦。 所以,WordPress 提供了“网络选项”,让你可以集中管理这些全局设置。

二、get_site_option() 函数:源码剖析

好,现在咱们进入正题,看看 get_site_option() 的源码(基于 WordPress 最新版本,可能会有细微差异):

function get_site_option( $option, $default = false ) {
    global $wpdb, $wp_site_cache;

    if ( ! is_multisite() ) {
        return get_option( $option, $default );
    }

    $switched = false;

    if ( ! isset( $wp_site_cache ) ) {
        $wp_site_cache = new WP_Object_Cache();
    }

    $cache_key = $option;

    $cache = $wp_site_cache->get( $cache_key, 'site-options' );

    if ( false !== $cache ) {
        /**
         * Filters the value of a specific site option.
         *
         * The dynamic portion of the hook name, `$option`, refers to the site option name.
         *
         * @since 2.9.0
         *
         * @param mixed $value Value of the site option.
         */
        return apply_filters( "site_option_{$option}", $cache );
    }

    // Load all options if not cached.
    if ( ! isset( $wp_site_cache->cache['site-options']['alloptions'] ) ) {
        $suppress = $wpdb->suppress_errors();
        $alloptions = $wpdb->get_results( "SELECT option_name, option_value FROM {$wpdb->sitemeta} WHERE site_id = '{$wpdb->siteid}'" , OBJECT_K );
        $wpdb->suppress_errors( $suppress );

        $options = array();
        foreach ( (array) $alloptions as $o ) {
            $options[ $o->option_name ] = maybe_unserialize( $o->option_value );
        }
        $wp_site_cache->add( 'alloptions', $options, 'site-options' );
    } else {
        $options = $wp_site_cache->cache['site-options']['alloptions'];
    }

    if ( isset( $options[ $option ] ) ) {
        /** This filter is documented in wp-includes/option.php */
        $value = apply_filters( "site_option_{$option}", $options[ $option ] );
        $wp_site_cache->add( $cache_key, $value, 'site-options' );

        return $value;
    }

    /**
     * Filters the default value of a specific site option.
     *
     * The dynamic portion of the hook name, `$option`, refers to the site option name.
     *
     * @since 2.9.0
     *
     * @param mixed $default Default value of the site option.
     */
    return apply_filters( "default_site_option_{$option}", $default );
}

看起来有点长,别慌,咱们一段一段地分析:

2.1 前置条件:多站点检查

if ( ! is_multisite() ) {
    return get_option( $option, $default );
}

这段代码很关键。 它首先检查当前 WordPress 是否运行在多站点模式下。 如果不是(! is_multisite()),那么直接调用 get_option() 函数,也就是获取 当前站点 的选项值。 这意味着,在单站点模式下,get_site_option()get_option() 的行为是完全一样的。

2.2 缓存机制:WP_Object_Cache

global $wpdb, $wp_site_cache;

if ( ! isset( $wp_site_cache ) ) {
    $wp_site_cache = new WP_Object_Cache();
}

$cache_key = $option;

$cache = $wp_site_cache->get( $cache_key, 'site-options' );

if ( false !== $cache ) {
    /**
     * Filters the value of a specific site option.
     *
     * The dynamic portion of the hook name, `$option`, refers to the site option name.
     *
     * @since 2.9.0
     *
     * @param mixed $value Value of the site option.
     */
    return apply_filters( "site_option_{$option}", $cache );
}

这部分代码负责缓存。 WordPress 使用 WP_Object_Cache 类来缓存网络选项,以提高性能。

  • global $wpdb, $wp_site_cache;: 引入全局变量,$wpdb 用于数据库操作, $wp_site_cache 是缓存对象。
  • if ( ! isset( $wp_site_cache ) ) { $wp_site_cache = new WP_Object_Cache(); }:如果 $wp_site_cache 未设置,则创建一个新的 WP_Object_Cache 实例。
  • $cache_key = $option;:使用选项名作为缓存键。
  • $cache = $wp_site_cache->get( $cache_key, 'site-options' );:尝试从缓存中获取选项值,缓存组为 ‘site-options’。
  • if ( false !== $cache ) { ... }:如果缓存命中(即 $cache 不是 false),则应用 site_option_{$option} 过滤器并返回缓存值。 这个过滤器允许你修改缓存中的选项值。

2.3 从数据库加载选项:wp_sitemeta

// Load all options if not cached.
if ( ! isset( $wp_site_cache->cache['site-options']['alloptions'] ) ) {
    $suppress = $wpdb->suppress_errors();
    $alloptions = $wpdb->get_results( "SELECT option_name, option_value FROM {$wpdb->sitemeta} WHERE site_id = '{$wpdb->siteid}'" , OBJECT_K );
    $wpdb->suppress_errors( $suppress );

    $options = array();
    foreach ( (array) $alloptions as $o ) {
        $options[ $o->option_name ] = maybe_unserialize( $o->option_value );
    }
    $wp_site_cache->add( 'alloptions', $options, 'site-options' );
} else {
    $options = $wp_site_cache->cache['site-options']['alloptions'];
}

如果缓存没有命中,或者缓存中没有加载所有选项,那么就需要从数据库中加载了。

  • if ( ! isset( $wp_site_cache->cache['site-options']['alloptions'] ) ) { ... }:检查是否已经缓存了所有网络选项。
  • $suppress = $wpdb->suppress_errors(); ... $wpdb->suppress_errors( $suppress );: 临时禁止数据库错误报告,提高性能。
  • $alloptions = $wpdb->get_results( "SELECT option_name, option_value FROM {$wpdb->sitemeta} WHERE site_id = '{$wpdb->siteid}'" , OBJECT_K );:从 wp_sitemeta 表中查询 site_id 等于当前站点 ID 的所有选项。 注意:这里查询的是 wp_sitemeta 表,而不是 wp_options 表。 wp_sitemeta 表存储网络选项,而 wp_options 表存储单个站点的选项。 $wpdb->siteid 包含当前站点的 ID。 OBJECT_K 参数使结果集以 option_name 作为键。
  • foreach ( (array) $alloptions as $o ) { $options[ $o->option_name ] = maybe_unserialize( $o->option_value ); }:将数据库查询结果转换为 PHP 数组,并使用 maybe_unserialize() 函数反序列化选项值。
  • $wp_site_cache->add( 'alloptions', $options, 'site-options' );: 将所有选项缓存到 WP_Object_Cache 中,键为 ‘alloptions’,缓存组为 ‘site-options’。
  • else { $options = $wp_site_cache->cache['site-options']['alloptions']; }:如果已经缓存了所有选项,则直接从缓存中获取。

2.4 返回选项值

if ( isset( $options[ $option ] ) ) {
    /** This filter is documented in wp-includes/option.php */
    $value = apply_filters( "site_option_{$option}", $options[ $option ] );
    $wp_site_cache->add( $cache_key, $value, 'site-options' );

    return $value;
}
  • if ( isset( $options[ $option ] ) ) { ... }:检查选项是否存在于已加载的选项数组中。
  • $value = apply_filters( "site_option_{$option}", $options[ $option ] );:如果选项存在,则应用 site_option_{$option} 过滤器,允许修改选项值。
  • $wp_site_cache->add( $cache_key, $value, 'site-options' );:将单个选项缓存到 WP_Object_Cache 中,键为选项名,缓存组为 ‘site-options’。
  • return $value;:返回选项值。

2.5 返回默认值

/**
 * Filters the default value of a specific site option.
 *
 * The dynamic portion of the hook name, `$option`, refers to the site option name.
 *
 * @since 2.9.0
 *
 * @param mixed $default Default value of the site option.
 */
return apply_filters( "default_site_option_{$option}", $default );

如果选项不存在,则应用 default_site_option_{$option} 过滤器,允许修改默认值,并返回默认值。

三、 重点总结:get_site_option() 的工作流程

为了方便大家理解,我们用一张表格来总结 get_site_option() 的工作流程:

步骤 描述
1 检查是否是多站点模式。 如果不是,调用 get_option() 并返回结果。
2 尝试从缓存中获取选项值。 如果缓存命中,应用 site_option_{$option} 过滤器并返回缓存值。
3 如果缓存未命中,从 wp_sitemeta 表中加载所有网络选项,并缓存到 WP_Object_Cache 中。
4 检查选项是否存在于已加载的选项数组中。 如果存在,应用 site_option_{$option} 过滤器,缓存选项值并返回。
5 如果选项不存在,应用 default_site_option_{$option} 过滤器,并返回默认值。

四、 实际应用:代码示例

说了这么多理论,不如来点实际的。 咱们看几个使用 get_site_option() 的例子:

4.1 获取网络管理员邮箱:

$admin_email = get_site_option( 'admin_email' );
echo '网络管理员邮箱:' . esc_html( $admin_email );

4.2 检查是否允许新用户注册:

$registration = get_site_option( 'registration' );

if ( $registration == 'all' ) {
    echo '允许新用户和站点注册';
} elseif ( $registration == 'user' ) {
    echo '只允许新用户注册';
} else {
    echo '不允许注册';
}

4.3 使用过滤器修改网络选项值:

function my_custom_admin_email( $email ) {
    return '[email protected]';
}
add_filter( 'site_option_admin_email', 'my_custom_admin_email' );

$admin_email = get_site_option( 'admin_email' );
echo '网络管理员邮箱:' . esc_html( $admin_email ); // 输出:[email protected]

这个例子中,我们使用 site_option_admin_email 过滤器,将网络管理员邮箱修改为 [email protected]

4.4 使用默认值过滤器

function my_default_new_setting( $default ) {
    return 'default_value';
}
add_filter( 'default_site_option_new_setting', 'my_default_new_setting' );

$new_setting = get_site_option( 'new_setting', 'fallback_value' );
echo 'New Setting: ' . esc_html( $new_setting ); // 输出:default_value

如果 new_setting 网络选项不存在,将返回 default_value,而不是 fallback_value

五、 注意事项

  • 性能: 尽量避免频繁调用 get_site_option(),特别是获取同一个选项时。 可以将选项值缓存到你自己的变量中。
  • 安全性: 不要直接输出 get_site_option() 获取的值,特别是用户输入相关的值。 使用 esc_html() 等函数进行转义,防止 XSS 攻击。
  • 上下文: 确保在多站点模式下调用 get_site_option()。 在单站点模式下,它和 get_option() 的行为是一样的。
  • 数据表: 网络选项存储在 wp_sitemeta 表中,而不是 wp_options 表中。

六、update_site_option()delete_site_option()

get_site_option() 对应的,还有 update_site_option()delete_site_option() 函数, 分别用于更新和删除网络选项。 它们的用法和 update_option()delete_option() 类似,这里就不再赘述了。 记住,操作网络选项要谨慎,因为它们会影响整个网络的所有站点。

七、 总结

今天咱们深入剖析了 WordPress 多站点模式下的 get_site_option() 函数。 从源码分析到实际应用,希望你能对它有一个更清晰的认识。 记住,理解源码是成为 WordPress 高手的必经之路。 多阅读源码,多实践,你也能成为 WordPress 大神!

好了,今天的讲座就到这里。 感谢大家的聆听! 下次有机会,咱们再聊聊 WordPress 的其他有趣话题。 祝大家学习愉快!

发表回复

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