深入理解 WordPress `wpmu_delete_blog()` 函数的源码:删除子站点时如何清理相关数据。

各位观众老爷们,晚上好!我是老码农,今天咱们来聊聊 WordPress 多站点(WPMU)中一个“辣手摧花”的函数:wpmu_delete_blog()。 别看它名字平平无奇,干的可是“杀人放火”的勾当——删除整个子站点!

但是,它可不是简单粗暴地删个数据库就完事儿了,它得负责把所有与这个站点相关的数据都清理干净,保证“人走茶凉,不留痕迹”。 否则,留下来的“冤魂”(残留数据)会给系统带来各种问题。

所以,今天我们就来扒一扒 wpmu_delete_blog() 的源码,看看它是如何做到“斩草除根”的。 准备好了吗? 系好安全带,发车啦!

1. 函数入口,接收“死亡通知”

首先,让我们找到 wpmu_delete_blog() 的真身。 它位于 wp-includes/ms-functions.php 文件中。 打开文件,你会看到类似这样的代码:

/**
 * Deletes a blog from the network.
 *
 * @since 2.0.0
 *
 * @param int  $blog_id ID of the blog to delete.
 * @param bool $drop    Optional. Whether to completely drop the blog from the database.
 *                      Default is true.
 *
 * @return bool True on success, false on failure.
 */
function wpmu_delete_blog( $blog_id, $drop = true ) {
    global $wpdb, $current_site;

    $blog_id = (int) $blog_id;

    if ( ! is_numeric( $blog_id ) || $blog_id <= 1 ) {
        return false;
    }

    /**
     * Fires before a blog is deleted from the network.
     *
     * @since 2.0.0
     *
     * @param int  $blog_id ID of the blog to delete.
     * @param bool $drop    Whether to completely drop the blog from the database.
     */
    do_action( 'wpmu_delete_blog', $blog_id, $drop );

    if ( is_super_admin() ) {
        //... 后面还有很多代码
    } else {
        return false; // 只有超级管理员才能删除站点
    }

    //... 更多代码
}

这段代码做了什么呢?

  • 接收参数: 接受两个参数:$blog_id (要删除的站点ID) 和 $drop (是否彻底删除数据库)。
  • 参数校验: 检查 $blog_id 是否为有效的站点ID(必须是大于1的整数)。
  • 权限验证: 只有超级管理员才能执行删除操作。
  • 触发钩子: do_action( 'wpmu_delete_blog', $blog_id, $drop ); 这个非常重要,它允许其他插件或主题在站点删除之前执行一些自定义操作。 比如,清理与该站点相关的缓存、发送通知等等。 这就像给站点发了一张“死亡通知单”,告诉大家“伙计们,这家伙要凉了,赶紧收拾东西!”

2. 准备“行刑”:切换数据库

在真正删除数据之前,wpmu_delete_blog() 需要先切换到要删除的站点的数据库。 毕竟,数据都在那个数据库里呢!

    $old_blog = $wpdb->blogid;
    switch_to_blog( $blog_id ); //切换到要删除的站点数据库

这里,switch_to_blog() 函数就像一个“传送门”,把我们带到要删除的站点所在的数据库。

3. “大清洗”:开始删除数据

终于到了最关键的环节:删除数据! wpmu_delete_blog() 使用了一系列的函数和SQL语句来完成这个任务。 我们来逐一分析:

  • 删除站点选项 (Options):

        delete_option( 'cron' );
        delete_option( 'recently_edited' );
        delete_transient( 'random_seed' );
        delete_transient( 'doing_cron' );

    这些代码删除了站点的一些常用选项和瞬态数据(transients)。 cron 是计划任务相关的信息,recently_edited 是最近编辑的文章列表,random_seeddoing_cron 是一些临时性的数据。 删除这些数据可以避免一些潜在的问题。

  • 删除站点上的所有用户:

        $users = get_users(
            array(
                'blog_id' => $blog_id,
                'fields'  => 'ids',
                'number'  => 99999,
            )
        );
    
        if ( $users ) {
            foreach ( $users as $user_id ) {
                wp_delete_user( $user_id, $reassign = null );
            }
        }

    这段代码获取站点上的所有用户,然后逐个删除。 wp_delete_user() 函数负责删除用户数据。 $reassign = null 表示删除用户时,不将用户发表的文章分配给其他用户。 这意味着这些文章也会被删除(因为它们属于被删除的用户)。

  • 删除站点的上传文件:

        $upload_dir = wp_upload_dir();
        $dir        = $upload_dir['basedir'];
        delete_files( $dir, true );

    这段代码获取站点的上传目录,然后使用 delete_files() 函数删除该目录下的所有文件和子目录。 delete_files() 函数是一个递归函数,它可以删除目录及其所有内容。 true 参数表示强制删除。

  • 删除站点上的所有文章、页面、自定义文章类型等内容:

        $tables = array( 'posts', 'comments', 'term_relationships', 'termmeta' );
    
        foreach ( $tables as $table ) {
            $wpdb->query( "DELETE FROM {$wpdb->$table} WHERE {$wpdb->$table}.{$wpdb->blogid_column} = '$blog_id'" );
        }
    
        $wpdb->query( "DELETE FROM {$wpdb->terms} WHERE term_id NOT IN ( SELECT term_id FROM {$wpdb->term_taxonomy} )" );
        $wpdb->query( "DELETE FROM {$wpdb->term_taxonomy} WHERE term_taxonomy_id NOT IN ( SELECT term_taxonomy_id FROM {$wpdb->term_relationships} )" );

    这段代码删除了站点上的所有文章、评论、分类、标签等内容。 它遍历几个关键的数据表(posts, comments, term_relationships, termmeta),然后使用 DELETE 语句删除与该站点相关的数据。 {$wpdb->blogid_column} 表示包含站点ID的列名,通常是 blog_id。 最后两行SQL语句用于清理 termsterm_taxonomy 表中孤立的数据。

  • 删除站点相关的缓存:

        wp_cache_flush();

    wp_cache_flush() 函数用于清空站点缓存。 这可以确保删除操作的更改立即生效,避免出现缓存导致的数据不一致问题。

4. “终极审判”:彻底删除数据库

如果 $drop 参数为 true (默认值),wpmu_delete_blog() 还会彻底删除该站点的数据库表。 这相当于把整个站点从数据库中抹去。

    if ( $drop ) {
        $prefix = $wpdb->get_blog_prefix( $blog_id );

        $query = $wpdb->prepare( "SHOW TABLES LIKE %s", $wpdb->esc_like( $prefix ) . '%' );
        $tables = $wpdb->get_col( $query );

        foreach ( $tables as $table ) {
            $wpdb->query( "DROP TABLE IF EXISTS `$table`" );
        }
    }

这段代码首先获取该站点的表前缀(例如 wp_2_),然后使用 SHOW TABLES LIKE 语句查找所有以该前缀开头的表。 最后,使用 DROP TABLE 语句逐个删除这些表。 注意,这里使用了反引号 (`) 来包裹表名,以防止表名中包含特殊字符导致SQL错误。

5. “善后处理”:恢复数据库并清理站点记录

删除完数据后,wpmu_delete_blog() 还需要做一些“善后处理”:

    restore_current_blog();  // 恢复到之前的站点数据库

    // Remove the blog from the blogs table
    $wpdb->delete( $wpdb->blogs, array( 'blog_id' => $blog_id ) );

    // Remove the blog from the sitemeta table
    $wpdb->delete( $wpdb->sitemeta, array( 'site_id' => $current_site->id, 'meta_key' => 'blog_id', 'meta_value' => $blog_id ) );

    clean_site_cache();

    /**
     * Fires after a blog is deleted from the network.
     *
     * @since 2.0.0
     *
     * @param int  $blog_id ID of the deleted blog.
     * @param bool $drop    Whether the blog was completely dropped from the database.
     */
    do_action( 'wpmu_delete_blog', $blog_id, $drop );

    return true;

这段代码做了什么呢?

  • 恢复数据库: restore_current_blog() 函数恢复到之前的站点数据库。 这就像从“传送门”回来,回到主站点。
  • 删除站点记录:wp_blogs 表中删除该站点的记录,从 wp_sitemeta 表中删除与该站点相关的元数据。
  • 清理站点缓存: clean_site_cache() 函数清理站点缓存。
  • 触发钩子: do_action( 'wpmu_delete_blog', $blog_id, $drop ); 再次触发钩子,允许其他插件或主题在站点删除之后执行一些自定义操作。 这就像给站点办了一场“追悼会”,告诉大家“这家伙真的凉透了,可以盖棺定论了!”

6. 流程总结:

为了更好地理解 wpmu_delete_blog() 的工作流程,我们用一个表格来总结一下:

步骤 描述 相关代码
1. 入口 接收站点ID和是否彻底删除的参数,进行参数校验和权限验证,触发删除前的钩子。 wpmu_delete_blog( $blog_id, $drop = true ), do_action( 'wpmu_delete_blog', $blog_id, $drop );
2. 切换数据库 切换到要删除的站点的数据库。 switch_to_blog( $blog_id );
3. 删除数据 删除站点选项、用户、上传文件、文章、评论、分类、标签等内容,清理缓存。 delete_option(), delete_transient(), wp_delete_user(), delete_files(), $wpdb->query( "DELETE FROM ..."), wp_cache_flush()
4. 删除数据库 如果 $drop 参数为 true,则彻底删除该站点的数据库表。 $wpdb->query( "DROP TABLE IF EXISTS$table" )
5. 善后处理 恢复到之前的站点数据库,删除 wp_blogs 表和 wp_sitemeta 表中的站点记录,清理站点缓存,触发删除后的钩子。 restore_current_blog(), $wpdb->delete(), clean_site_cache(), do_action( 'wpmu_delete_blog', $blog_id, $drop );
6. 返回结果 返回 true 表示删除成功,false 表示删除失败。 return true;return false;

7. 注意事项:

  • 谨慎操作: wpmu_delete_blog() 是一个高危函数,使用前务必备份数据! 一旦删除,数据就无法恢复了。
  • 理解 $drop 参数: $drop 参数决定是否彻底删除数据库。 如果只想删除站点数据,但保留数据库表,可以将 $drop 设置为 false
  • 利用钩子: wpmu_delete_blog 钩子允许你在站点删除前后执行自定义操作。 这对于清理插件或主题的残留数据非常有用。
  • 性能考虑: 删除站点可能需要很长时间,特别是对于数据量大的站点。 建议在服务器空闲时执行删除操作。

8. 总结:

wpmu_delete_blog() 函数是 WordPress 多站点中一个非常重要的函数,它负责删除子站点及其相关的所有数据。 通过分析源码,我们可以深入了解它的工作原理,以及如何安全有效地使用它。 希望今天的讲座对你有所帮助!

下次再见!

发表回复

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