探究 WordPress `get_blog_details()` 函数的源码:如何获取指定子站点的详细信息。

咳咳,麦克风试音… 1, 2, 3… 好嘞!各位好,我是你们今天的WordPress探秘向导,江湖人称代码小能手,今天咱们要一起扒一扒WordPress多站点里一个非常实用,但又容易被忽略的函数:get_blog_details()

大家有没有遇到过这样的情况:在一个WordPress多站点网络里,你需要在某个子站点里获取另一个子站点的各种信息,比如站点名称、域名、路径、管理邮箱等等。如果手动去数据库里捞,那可就太low了,效率低不说,还容易出错。这时候,get_blog_details() 函数就派上大用场了。

一、get_blog_details() 函数的前世今生

get_blog_details() 函数,顾名思义,就是“获取博客详情”的意思。它主要用于在WordPress多站点环境中获取指定站点的详细信息。它的定义位于 wp-includes/ms-functions.php 文件中(没错,多站点相关的核心函数都在这里)。

二、get_blog_details() 函数的语法和参数

先来看看 get_blog_details() 函数的基本语法:

get_blog_details( int $blog_id = 0, string $field = '' ) : WP_Site|stdClass|string|null

是不是有点眼花缭乱?别怕,我来给你分解一下:

  • $blog_id (int, optional): 这是要获取信息的站点的ID。如果省略或者设置为 0,则默认获取当前站点的信息。记住,这里指的是 WordPress 多站点网络中的站点 ID,不是文章 ID 或者其他 ID。

  • $field (string, optional): 这是一个可选参数,用于指定要获取的字段。如果不指定,则函数会返回一个包含所有站点信息的对象。如果指定了,则只会返回该字段的值。 常见的字段包括:

    • 'domain':站点域名。
    • 'path':站点路径。
    • 'blogname':站点名称。
    • 'siteurl':站点 URL。
    • 'postcount':站点文章数量。
    • 'admin_email':站点管理员邮箱。
  • 返回值 (WP_Site|stdClass|string|null): 返回值类型取决于你传入的 $field 参数:

    • 如果 $field 为空,则返回一个 WP_Site 对象 (WordPress 4.6 及以上版本) 或者一个 stdClass 对象 (WordPress 4.6 之前版本),包含站点的所有信息。
    • 如果 $field 被指定,则返回该字段的字符串值。
    • 如果找不到指定的站点或者字段,则返回 null

三、get_blog_details() 函数的源码剖析

源码是最好的老师,让我们一起深入 wp-includes/ms-functions.php 文件,看看 get_blog_details() 函数的真面目:

function get_blog_details( $blog_id = 0, $field = '' ) {
    global $wpdb, $current_site;

    if ( ! is_numeric( $blog_id ) ) {
        _doing_it_wrong( __FUNCTION__, sprintf( __( 'Blog ID should be a number. Value: %s' ), esc_html( var_export( $blog_id, true ) ) ), '4.6.0' );
        return null;
    }

    $blog_id = (int) $blog_id;

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

    if ( ! is_object( $current_site ) ) {
        $current_site = new stdClass;
    }

    $details = wp_cache_get( $blog_id, 'blog-details' );

    if ( ! is_object( $details ) ) {
        switch_to_blog( $blog_id );

        $details = new stdClass;

        $details->blog_id = $blog_id;
        $details->domain  = $wpdb->get_var( $wpdb->prepare( "SELECT domain FROM {$wpdb->blogs} WHERE blog_id = %d", $blog_id ) );
        $details->path    = $wpdb->get_var( $wpdb->prepare( "SELECT path FROM {$wpdb->blogs} WHERE blog_id = %d", $blog_id ) );

        $scheme = is_ssl() ? 'https://' : 'http://';
        $details->siteurl = $scheme . $details->domain . $details->path;

        $name = get_option( 'blogname' );
        if ( empty( $name ) ) {
            $name = get_network()->site_name;
        }
        $details->blogname = $name;

        $admin_email = get_option( 'admin_email' );
        if ( empty( $admin_email ) ) {
            $admin_email = get_site_option( 'admin_email' );
        }
        $details->admin_email = $admin_email;

        $details->postcount = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(ID) FROM {$wpdb->posts} WHERE post_status = 'publish' AND post_type = 'post'", '' ) );

        restore_current_blog();

        wp_cache_set( $blog_id, $details, 'blog-details' );
    }

    if ( version_compare( $GLOBALS['wp_version'], '4.6', '>=' ) ) {
        $site = WP_Site::get_instance( $blog_id );

        if ( $site ) {
            $details->domain = $site->domain;
            $details->path   = $site->path;
        }
    }

    if ( ! empty( $field ) ) {
        if ( isset( $details->$field ) ) {
            return $details->$field;
        } else {
            return null;
        }
    } else {
        return $details;
    }
}

这段代码看起来有点长,但其实逻辑很简单,我来给大家一行一行地解读:

  1. global $wpdb, $current_site;: 引入全局变量 $wpdb (数据库对象) 和 $current_site (当前站点对象)。
  2. if ( ! is_numeric( $blog_id ) ) { ... }: 检查 $blog_id 是否为数字,如果不是,则报错并返回 null
  3. $blog_id = (int) $blog_id;: 将 $blog_id 强制转换为整数类型。
  4. if ( empty( $blog_id ) ) { ... }: 如果 $blog_id 为空,则获取当前站点的 ID。
  5. if ( ! is_object( $current_site ) ) { ... }: 如果 $current_site 不是对象,则创建一个新的 stdClass 对象。
  6. $details = wp_cache_get( $blog_id, 'blog-details' );: 尝试从缓存中获取站点信息。WordPress 使用缓存来提高性能,避免重复查询数据库。
  7. if ( ! is_object( $details ) ) { ... }: 如果缓存中没有站点信息,则从数据库中查询:
    • switch_to_blog( $blog_id );: 切换到指定的站点。这个函数非常重要,它会改变全局状态,让后续的数据库查询操作针对指定的站点。
    • $details = new stdClass;: 创建一个新的 stdClass 对象来存储站点信息。
    • $details->blog_id = $blog_id;: 设置站点 ID。
    • $details->domain = $wpdb->get_var( $wpdb->prepare( "SELECT domain FROM {$wpdb->blogs} WHERE blog_id = %d", $blog_id ) );: 从 wp_blogs 表中查询站点域名。 $wpdb->prepare() 函数用于防止 SQL 注入。
    • $details->path = $wpdb->get_var( $wpdb->prepare( "SELECT path FROM {$wpdb->blogs} WHERE blog_id = %d", $blog_id ) );: 从 wp_blogs 表中查询站点路径。
    • $scheme = is_ssl() ? 'https://' : 'http://';: 判断当前是否使用 SSL,并设置协议。
    • $details->siteurl = $scheme . $details->domain . $details->path;: 拼接站点 URL。
    • $name = get_option( 'blogname' ); ... $details->blogname = $name;: 从 wp_options 表中查询站点名称。如果站点名称为空,则使用网络名称。
    • $admin_email = get_option( 'admin_email' ); ... $details->admin_email = $admin_email;: 从 wp_options 表中查询管理员邮箱。如果管理员邮箱为空,则使用站点管理员邮箱。
    • $details->postcount = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(ID) FROM {$wpdb->posts} WHERE post_status = 'publish' AND post_type = 'post'", '' ) );: 查询站点文章数量。
    • restore_current_blog();: 恢复到之前的站点。 这个函数和 switch_to_blog() 配套使用,非常重要,否则可能会影响其他站点的操作。
    • wp_cache_set( $blog_id, $details, 'blog-details' );: 将站点信息保存到缓存中。
  8. if ( version_compare( $GLOBALS['wp_version'], '4.6', '>=' ) ) { ... }: 如果是 WordPress 4.6 及以上版本,则使用 WP_Site 对象来获取站点域名和路径。
  9. if ( ! empty( $field ) ) { ... }: 如果指定了 $field 参数,则返回该字段的值。
  10. else { ... }: 否则,返回包含所有站点信息的对象。

四、get_blog_details() 函数的使用示例

光说不练假把式,让我们来看一些实际的使用示例:

示例 1:获取指定站点的域名

$blog_id = 2; // 假设我们要获取 ID 为 2 的站点的域名
$domain = get_blog_details( $blog_id, 'domain' );

if ( $domain ) {
    echo '站点 ID ' . $blog_id . ' 的域名是:' . $domain;
} else {
    echo '找不到站点 ID ' . $blog_id . ' 的信息';
}

示例 2:获取当前站点的所有信息

$details = get_blog_details(); // 不传递任何参数,默认获取当前站点的信息

if ( is_object( $details ) ) {
    echo '站点名称:' . $details->blogname . '<br>';
    echo '站点域名:' . $details->domain . '<br>';
    echo '站点路径:' . $details->path . '<br>';
    echo '站点 URL:' . $details->siteurl . '<br>';
    echo '站点管理员邮箱:' . $details->admin_email . '<br>';
    echo '站点文章数量:' . $details->postcount . '<br>';
} else {
    echo '获取站点信息失败';
}

示例 3:在循环中获取多个站点的名称

$blog_ids = [1, 2, 3]; // 假设我们要获取 ID 为 1, 2, 3 的站点的名称

foreach ( $blog_ids as $blog_id ) {
    $blogname = get_blog_details( $blog_id, 'blogname' );

    if ( $blogname ) {
        echo '站点 ID ' . $blog_id . ' 的名称是:' . $blogname . '<br>';
    } else {
        echo '找不到站点 ID ' . $blog_id . ' 的信息<br>';
    }
}

示例 4:配合 switch_to_blog()restore_current_blog() 使用

这个示例展示了如何在不同的站点之间切换,并获取每个站点的文章数量:

$original_blog_id = get_current_blog_id(); // 保存当前站点的 ID

$blog_ids = [1, 2, 3]; // 假设我们要获取 ID 为 1, 2, 3 的站点的文章数量

foreach ( $blog_ids as $blog_id ) {
    switch_to_blog( $blog_id ); // 切换到指定站点

    $post_count = get_blog_details( null, 'postcount' ); // 获取当前站点的文章数量 (注意这里 $blog_id 传 null)

    if ( $post_count !== null ) {
        echo '站点 ID ' . $blog_id . ' 的文章数量是:' . $post_count . '<br>';
    } else {
        echo '找不到站点 ID ' . $blog_id . ' 的信息<br>';
    }

    restore_current_blog(); // 恢复到之前的站点
}

// 确保最后恢复到最初的站点
if ($original_blog_id != get_current_blog_id()) {
    switch_to_blog($original_blog_id);
}

五、注意事项和最佳实践

  • 缓存: get_blog_details() 函数会使用 WordPress 的缓存机制,所以不必担心频繁调用会影响性能。但是,如果站点信息发生变化,可能需要清除缓存才能看到最新的信息。
  • switch_to_blog()restore_current_blog() 在使用 switch_to_blog() 函数切换站点后,一定要记得使用 restore_current_blog() 函数恢复到之前的站点,否则可能会导致不可预知的错误。
  • 安全性: get_blog_details() 函数本身没有安全问题,但是在使用它获取站点信息时,要注意对输出进行转义,防止 XSS 攻击。
  • 性能: 虽然 get_blog_details() 函数使用了缓存,但是频繁调用仍然会影响性能。如果需要在循环中获取多个站点的信息,建议一次性获取所有站点的信息,然后进行遍历。
  • 错误处理: 在使用 get_blog_details() 函数时,要进行错误处理,判断是否成功获取了站点信息。
  • WordPress 版本: 注意不同 WordPress 版本对 get_blog_details() 函数的返回值类型可能略有不同,特别是 WP_Site 对象的引入。

六、get_blog_details() 函数的替代方案

虽然 get_blog_details() 函数非常方便,但在某些情况下,你可能需要考虑其他方案:

  • 直接查询数据库: 如果你需要获取的站点信息不在 get_blog_details() 函数提供的字段范围内,或者你需要进行更复杂的查询,可以直接使用 $wpdb 对象查询数据库。
  • 使用 WP_Site 对象: 在 WordPress 4.6 及以上版本中,你可以使用 WP_Site 对象来获取站点信息。WP_Site 对象提供了更多的方法来操作站点信息,例如获取站点的所有选项。
  • 自定义函数: 如果你需要频繁获取特定的站点信息,可以编写自定义函数来封装 get_blog_details() 函数或者直接查询数据库。

七、总结

get_blog_details() 函数是WordPress多站点开发中一个非常实用的工具,它可以帮助你轻松获取指定站点的详细信息。掌握它的用法,可以大大提高你的开发效率。希望今天的讲解能帮助大家更好地理解和使用 get_blog_details() 函数。

最后,记住,代码的世界充满了乐趣,不断学习和探索才能成为真正的代码高手! 下次再见!

发表回复

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