分析 WordPress `get_site_url()` 函数在多站点模式下的源码:如何生成站点的 URL。

各位同学,晚上好!我是老码,今天给大家带来一场关于 WordPress 多站点模式下 get_site_url() 函数源码的深度剖析。这玩意儿看似简单,实则暗藏玄机,尤其是多站点环境下,URL 的生成可不是随便拼接一下就能搞定的。咱们今天就一层一层扒开它的代码,看看它到底是怎么玩的。

开场白:URL,网站的身份证

想象一下,URL 就像是网站的身份证,告诉浏览器:“嘿,你要找的东西在这里!” 在单站点模式下,生成 URL 还算简单,但在多站点模式下,事情就变得复杂起来了。你需要区分不同的站点,确保每个站点都有自己独特的“身份证”。 get_site_url() 函数就是负责生成这个“身份证”的关键人物。

第一幕:单站点模式下的 get_site_url()

咱们先从最简单的场景开始,看看单站点模式下 get_site_url() 是怎么工作的。

function get_site_url( $blog_id = null, $path = '', $scheme = null ) {
    global $current_site;

    if ( empty( $blog_id ) ) {
        $blog_id = get_current_blog_id();
    }

    $s = is_ssl() ? 'https' : 'http';

    if ( isset( $scheme ) && in_array( $scheme, array( 'http', 'https', 'relative' ), true ) ) {
        $s = $scheme;
    }

    if ( is_multisite() && ( ! isset( $current_site ) || ! is_object( $current_site ) ) ) {
        ms_load_current_site_and_network();
    }

    if ( is_multisite() && ms_is_switched() ) {
        restore_current_blog();
    }

    if ( is_multisite() && ( $blog_id != get_current_blog_id() ) ) {
        switch_to_blog( $blog_id );
        $url = get_option( 'siteurl' );
        restore_current_blog();
    } else {
        $url = get_option( 'siteurl' );
    }

    if ( 'relative' === $s ) {
        $url = wp_make_link_relative( $url );
    } else {
        $url = set_url_scheme( $url, $s );
    }

    $url = rtrim( $url, '/' );

    if ( ! empty( $path ) && is_string( $path ) ) {
        $url .= '/' . ltrim( $path, '/' );
    }

    return apply_filters( 'site_url', $url, $path, $scheme, $blog_id );
}

代码解读:

  1. 参数:

    • $blog_id: 站点 ID,默认为当前站点 ID。
    • $path: 要附加到 URL 的路径。
    • $scheme: URL 协议 (http, https, relative)。
  2. 获取当前站点 ID: 如果没有提供 $blog_id,就使用 get_current_blog_id() 获取当前站点的 ID。

  3. 确定协议: 使用 is_ssl() 判断是否是 HTTPS,或者使用提供的 $scheme 参数。

  4. 获取 siteurl 选项: 通过 get_option( 'siteurl' ) 获取数据库中存储的站点 URL。这是关键,单站点模式下,站点 URL 就存在这个选项里。

  5. 处理相对 URL: 如果 $scheme 是 ‘relative’,则使用 wp_make_link_relative() 将 URL 转换为相对 URL。

  6. 设置 URL 协议: 使用 set_url_scheme() 设置 URL 的协议。

  7. 添加路径: 如果提供了 $path,则将其添加到 URL 的末尾。

  8. 过滤: 使用 apply_filters( 'site_url', $url, $path, $scheme, $blog_id ) 提供一个钩子,允许开发者修改最终的 URL。

重点: 在单站点模式下,get_site_url() 主要依赖于 get_option( 'siteurl' ) 来获取站点 URL。

第二幕:多站点模式下的 get_site_url() 的挑战

多站点模式下,一个 WordPress 安装可以运行多个站点,每个站点都有自己的数据库表和设置。这意味着 get_option( 'siteurl' ) 不再能直接满足需求,因为它只能存储一个站点的 URL。

那么,多站点模式下的 get_site_url() 是如何解决这个问题的呢?

代码剖析:多站点模式的玄机

让我们再次回到 get_site_url() 的源码,这次重点关注多站点相关的逻辑。

function get_site_url( $blog_id = null, $path = '', $scheme = null ) {
    global $current_site;

    if ( empty( $blog_id ) ) {
        $blog_id = get_current_blog_id();
    }

    $s = is_ssl() ? 'https' : 'http';

    if ( isset( $scheme ) && in_array( $scheme, array( 'http', 'https', 'relative' ), true ) ) {
        $s = $scheme;
    }

    if ( is_multisite() && ( ! isset( $current_site ) || ! is_object( $current_site ) ) ) {
        ms_load_current_site_and_network();
    }

    if ( is_multisite() && ms_is_switched() ) {
        restore_current_blog();
    }

    if ( is_multisite() && ( $blog_id != get_current_blog_id() ) ) {
        switch_to_blog( $blog_id );
        $url = get_option( 'siteurl' );
        restore_current_blog();
    } else {
        $url = get_option( 'siteurl' );
    }

    if ( 'relative' === $s ) {
        $url = wp_make_link_relative( $url );
    } else {
        $url = set_url_scheme( $url, $s );
    }

    $url = rtrim( $url, '/' );

    if ( ! empty( $path ) && is_string( $path ) ) {
        $url .= '/' . ltrim( $path, '/' );
    }

    return apply_filters( 'site_url', $url, $path, $scheme, $blog_id );
}

关键代码段:

if ( is_multisite() && ( $blog_id != get_current_blog_id() ) ) {
    switch_to_blog( $blog_id );
    $url = get_option( 'siteurl' );
    restore_current_blog();
} else {
    $url = get_option( 'siteurl' );
}

这段代码做了什么?

  • is_multisite() 首先判断是否是多站点模式。
  • $blog_id != get_current_blog_id() 判断要获取 URL 的站点 ID 是否与当前站点 ID 不同。
  • switch_to_blog( $blog_id ) 如果站点 ID 不同,则切换到目标站点。这个函数会临时修改全局状态,以便后续的 get_option() 函数可以从目标站点的数据库表中获取选项。
  • $url = get_option( 'siteurl' ) 在切换到目标站点后,获取目标站点的 siteurl 选项。
  • restore_current_blog() 切换回原来的站点,恢复全局状态。

核心思想:

多站点模式下的 get_site_url() 通过临时切换站点的方式,从每个站点的数据库表中获取 siteurl 选项,从而生成对应站点的 URL。

案例分析:两种多站点模式

WordPress 多站点模式有两种主要类型:

  • 子域名模式: 每个站点都有一个独立的子域名,例如 site1.example.comsite2.example.com
  • 子目录模式: 每个站点都在主域名下的一个子目录中,例如 example.com/site1example.com/site2

在子域名模式下,每个站点的 siteurl 选项通常包含完整的子域名。而在子目录模式下,siteurl 选项通常只包含主域名,然后 get_site_url() 函数会根据站点 ID 和配置,添加相应的子目录。

第三幕:ms_load_current_site_and_network() 的作用

get_site_url() 函数中,我们还看到这样一段代码:

if ( is_multisite() && ( ! isset( $current_site ) || ! is_object( $current_site ) ) ) {
    ms_load_current_site_and_network();
}

这个 ms_load_current_site_and_network() 函数有什么作用呢?

它负责加载当前站点和网络的信息,并将它们存储在全局变量 $current_site$current_network 中。这些信息包括站点 ID、域名、路径等。

为什么需要加载这些信息?

因为在多站点模式下,很多函数都需要访问这些信息才能正常工作。例如,get_current_blog_id() 函数就需要访问 $current_site 才能获取当前站点的 ID。

深入挖掘:switch_to_blog()restore_current_blog()

switch_to_blog()restore_current_blog() 这两个函数在多站点模式下扮演着至关重要的角色。它们负责临时切换站点,以便我们可以访问目标站点的数据库表和设置。

function switch_to_blog( $new_blog, $deprecated = '' ) {
    global $wpdb, $current_blog, $blog_id, $switched, $table_prefix;

    if ( $deprecated ) {
        _deprecated_argument( __FUNCTION__, '3.5.0' );
    }

    $new_blog = (int) $new_blog;

    if ( isset( $switched ) && $switched ) {
        restore_current_blog();
    }

    $current_blog = $blog_id;
    $blog_id      = $new_blog;
    $switched     = true;

    $details = get_blog_details( $new_blog );

    if ( ! $details ) {
        return false;
    }

    $wpdb->set_blog_id( $new_blog );

    $table_prefix = $wpdb->get_blog_prefix( $new_blog );

    do_action( 'switch_to_blog', $new_blog, $current_blog );

    return true;
}

function restore_current_blog() {
    global $wpdb, $current_blog, $blog_id, $switched, $table_prefix;

    if ( ! isset( $switched ) || ! $switched ) {
        return false;
    }

    $switched = false;

    $wpdb->set_blog_id( $current_blog );

    $table_prefix = $wpdb->get_blog_prefix( $current_blog );

    $blog_id = $current_blog;

    unset( $current_blog );

    do_action( 'restore_current_blog', $blog_id );

    return true;
}

switch_to_blog() 的主要作用:

  1. 保存当前站点 ID: 将当前的 $blog_id 保存到 $current_blog 中。
  2. 切换站点 ID:$blog_id 设置为新的站点 ID。
  3. 更新数据库连接: 使用 $wpdb->set_blog_id() 更新数据库连接,以便后续的数据库查询可以访问目标站点的数据库表。
  4. 更新表前缀: 更新 $table_prefix 变量,以便后续的数据库查询可以使用目标站点的表前缀。
  5. 触发 switch_to_blog 钩子: 允许开发者在切换站点后执行自定义操作。

restore_current_blog() 的主要作用:

  1. 恢复站点 ID:$blog_id 恢复为之前保存的 $current_blog
  2. 更新数据库连接: 使用 $wpdb->set_blog_id() 恢复数据库连接到原始站点。
  3. 更新表前缀: 恢复 $table_prefix 变量到原始站点的表前缀。
  4. 触发 restore_current_blog 钩子: 允许开发者在恢复站点后执行自定义操作。

表格总结:get_site_url() 在不同模式下的行为

模式 主要依赖 如何获取站点 URL
单站点模式 siteurl 选项 直接从数据库的 wp_options 表中读取 siteurl 选项。
多站点模式 siteurl 选项 + 站点 ID 1. 如果 $blog_id 与当前站点 ID 相同,则直接从当前站点的 wp_options 表中读取 siteurl 选项。 2. 如果 $blog_id 与当前站点 ID 不同,则先使用 switch_to_blog() 切换到目标站点,然后从目标站点的 wp_options 表中读取 siteurl 选项,最后使用 restore_current_blog() 切换回原始站点。

第四幕:安全与性能的考量

虽然 get_site_url() 函数看起来很简单,但在多站点环境下,我们需要注意一些安全和性能问题。

  • 安全: 确保用户没有权限访问其他站点的 siteurl 选项。 WordPress 核心代码已经做了很多安全措施,但开发者也需要注意避免在自定义代码中出现安全漏洞。
  • 性能: 频繁地切换站点可能会影响性能。 尽量避免在循环中调用 get_site_url() 函数,可以考虑缓存结果或者使用其他更高效的方法。

第五幕:实战演练

假设我们有一个多站点 WordPress 安装,包含两个站点:

  • 站点 ID 1:主站点,URL 为 example.com
  • 站点 ID 2:子站点,URL 为 example.com/site2

现在,我们在主站点中需要获取子站点的 URL。

$site2_url = get_site_url( 2 ); // 获取站点 ID 为 2 的站点的 URL

echo $site2_url; // 输出:example.com/site2

在这个例子中,get_site_url( 2 ) 函数会先切换到站点 ID 为 2 的站点,然后从该站点的 wp_options 表中读取 siteurl 选项,最后切换回主站点。

总结:get_site_url() 的精髓

get_site_url() 函数是 WordPress 中一个非常重要的函数,它负责生成站点的 URL。在多站点模式下,它通过临时切换站点的方式,从每个站点的数据库表中获取 siteurl 选项,从而生成对应站点的 URL。理解 get_site_url() 的工作原理,对于开发多站点 WordPress 插件和主题至关重要。

彩蛋:一些小技巧

  • 使用 home_url() 函数可以获取站点的首页 URL,它与 get_site_url() 的区别在于,home_url() 返回的是首页 URL,而 get_site_url() 返回的是站点 URL。
  • 可以使用 network_site_url() 函数获取主站点的 URL,即使当前站点不是主站点。

好了,今天的讲座就到这里。希望大家通过今天的学习,能够对 WordPress 多站点模式下的 get_site_url() 函数有更深入的理解。下次再见!

发表回复

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