深入理解 `wp_delete_user()` 函数的源码,它如何处理用户删除,并清理其文章、评论和元数据?

各位好! 今天咱们就来扒一扒 WordPress 里的“断舍离大师”—— wp_delete_user() 函数。这家伙可不简单,表面上只是删个用户,背地里却要处理一大堆烂摊子,什么文章、评论、元数据,全都得收拾得干干净净。 咱们今天就来深入了解下这位“大师”是如何操作的,看看它到底用了什么魔法,能把一个用户及其所有痕迹从数据库里抹去。

一、初识 wp_delete_user():别看它名字简单,干的活可不少

wp_delete_user(),顾名思义,就是删除用户用的。但它可不是简单地在 wp_users 表里删掉一条记录就完事了。它要做的事情可多了,包括:

  1. 删除用户记录: 这是最基本的,从 wp_users 表里移除用户。
  2. 重新分配文章: 用户的文章可以转移给其他用户,或者直接删除。
  3. 删除评论: 删除用户发表的评论,或者将其归属给其他用户。
  4. 清理元数据: 删除与用户相关的各种元数据,比如用户资料、设置等等。

所以说,wp_delete_user() 是一个相当复杂的操作,需要谨慎使用。

二、源码剖析:一步一步揭开 wp_delete_user() 的面纱

现在,咱们就打开 WordPress 的源码,找到 wp-includes/user.php 文件,一起来看看 wp_delete_user() 函数的庐山真面目。

function wp_delete_user( $id, $reassign = null ) {
    global $wpdb;

    $id = (int) $id;

    if ( ! get_user( $id ) ) {
        return false;
    }

    if ( ! current_user_can( 'delete_user', $id ) ) {
        return false;
    }

    if ( is_multisite() && is_super_admin( $id ) ) {
        return false;
    }

    /**
     * Fires before a user is deleted from the database.
     *
     * @since 2.0.0
     *
     * @param int $id The user ID.
     */
    do_action( 'delete_user', $id );

    /**
     * Fires immediately before a user is permanently deleted from the database.
     *
     * @since 4.2.0
     *
     * @param int $id The user ID.
     */
    do_action( 'pre_user_deletion', $id );

    if ( null === $reassign ) {
        $reassign = get_current_user_id();
    }

    $reassign = (int) $reassign;

    if ( $reassign === $id ) {
        return false;
    }

    if ( ! get_user( $reassign ) ) {
        $reassign = 0;
    }

    /**
     * Filters the user ID to reassign posts and links to.
     *
     * @since 3.0.0
     *
     * @param int $reassign The user ID to reassign posts and links to.
     * @param int $id       The user ID being deleted.
     */
    $reassign = apply_filters( 'wp_delete_user_reassign', $reassign, $id );

    // Don't allow the user to reassign to themselves.
    if ( $reassign === $id ) {
        return false;
    }

    // Get the user's ID.
    $user = get_userdata( $id );

    if ( ! $user ) {
        return false;
    }

    // Disable events firing during user deletion.
    wp_suspend_cache_invalidation( true );

    // Move posts and links to new user.
    if ( $reassign ) {
        // Reassign posts.
        $wpdb->update( $wpdb->posts, array( 'post_author' => $reassign ), array( 'post_author' => $id ) );

        // Reassign links.
        $wpdb->update( $wpdb->links, array( 'link_owner' => $reassign ), array( 'link_owner' => $id ) );
    } else {
        // Alternatively, delete posts and links.
        $posts = get_posts( array( 'author' => $id, 'post_type' => 'any', 'post_status' => 'any', 'numberposts' => -1, 'fields' => 'ids' ) );
        if ( $posts ) {
            foreach ( $posts as $post ) {
                wp_delete_post( $post, true );
            }
        }

        $links = $wpdb->get_results( $wpdb->prepare( "SELECT link_id FROM $wpdb->links WHERE link_owner = %d", $id ) );
        if ( $links ) {
            foreach ( $links as $link ) {
                wp_delete_link( $link->link_id );
            }
        }
    }

    // Delete user's comments.
    $comments = $wpdb->get_results( $wpdb->prepare( "SELECT comment_ID FROM $wpdb->comments WHERE user_id = %d", $id ) );
    if ( $comments ) {
        foreach ( $comments as $comment ) {
            wp_delete_comment( $comment->comment_ID, true );
        }
    }

    // Remove all of the user's meta data.
    delete_user_meta( $id );

    /**
     * Fires immediately before the user is deleted permanently from the database.
     *
     * @since 2.2.0
     *
     * @param int $id User ID.
     */
    do_action( 'delete_user', $id );

    // Finally, delete user.
    $result = $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->users WHERE ID = %d", $id ) );

    wp_suspend_cache_invalidation( false );

    if ( ! $result ) {
        return false;
    }

    /**
     * Fires after a user is deleted from the database.
     *
     * @since 2.0.0
     *
     * @param int $id       The user ID.
     * @param int $reassign The user ID to reassign posts and links to.
     */
    do_action( 'deleted_user', $id, $reassign );

    return true;
}

哇,代码有点长,不过别怕,咱们慢慢来分析。

2.1 前置检查:确保删除操作是合法的

在真正开始删除用户之前,wp_delete_user() 会进行一系列的检查,确保删除操作是合法的。

  • 用户是否存在? get_user( $id ) 用来检查用户是否存在。如果用户不存在,直接返回 false
  • 当前用户是否有权限删除? current_user_can( 'delete_user', $id ) 检查当前用户是否有权限删除指定用户。只有具有 delete_user 权限的用户才能删除。
  • 是否是超级管理员? is_multisite() && is_super_admin( $id ) 检查是否是多站点环境下的超级管理员。超级管理员不能直接被删除。
  • 不能将文章重新分配给自己: 检查重新分配文章的用户 ID 是否等于要删除的用户 ID。如果是,返回 false

这些检查就像是安全门,确保只有符合条件的用户才能执行删除操作。

2.2 钩子 (Hooks):给开发者们留的后门

在删除用户前后,wp_delete_user() 使用了多个钩子,允许开发者在删除过程中插入自定义代码。

  • do_action( 'delete_user', $id ): 在用户删除前触发,允许开发者执行一些清理工作,比如删除与用户相关的自定义数据。
  • do_action( 'pre_user_deletion', $id ): 在用户被永久删除前触发, 同样允许开发者执行清理工作。
  • apply_filters( 'wp_delete_user_reassign', $reassign, $id ): 允许开发者修改文章重新分配的用户 ID。
  • do_action( 'delete_user', $id ): 在实际删除用户之前再次触发,允许开发者执行一些最后的清理工作。
  • do_action( 'deleted_user', $id, $reassign ): 在用户删除后触发,允许开发者执行一些后续操作,比如记录删除日志。

这些钩子就像是给开发者们留的后门,可以根据自己的需求定制删除流程。

2.3 文章重新分配或删除:是去是留,命运掌握在管理员手中

接下来,wp_delete_user() 会处理用户发布的文章。有两种选择:

  • 重新分配给其他用户: 如果指定了 $reassign 参数,wp_delete_user() 会将用户的所有文章和链接重新分配给指定的 $reassign 用户。

    $wpdb->update( $wpdb->posts, array( 'post_author' => $reassign ), array( 'post_author' => $id ) );
    $wpdb->update( $wpdb->links, array( 'link_owner' => $reassign ), array( 'link_owner' => $id ) );

    这两行代码使用了 wpdb->update() 函数,分别更新了 wp_posts 表和 wp_links 表,将 post_authorlink_owner 字段的值从被删除用户的 ID 修改为 $reassign 用户的 ID。

  • 直接删除: 如果没有指定 $reassign 参数,wp_delete_user() 会直接删除用户的所有文章和链接。

    $posts = get_posts( array( 'author' => $id, 'post_type' => 'any', 'post_status' => 'any', 'numberposts' => -1, 'fields' => 'ids' ) );
    if ( $posts ) {
        foreach ( $posts as $post ) {
            wp_delete_post( $post, true );
        }
    }
    
    $links = $wpdb->get_results( $wpdb->prepare( "SELECT link_id FROM $wpdb->links WHERE link_owner = %d", $id ) );
    if ( $links ) {
        foreach ( $links as $link ) {
            wp_delete_link( $link->link_id );
        }
    }

    这段代码首先使用 get_posts() 函数获取用户的所有文章 ID,然后循环调用 wp_delete_post() 函数删除每篇文章。wp_delete_post() 函数的第二个参数 $force_delete 设置为 true,表示强制删除,不放入回收站。 然后获取链接,使用 wp_delete_link()删除链接。

2.4 评论处理:不放过任何一条评论

处理完文章后,wp_delete_user() 会处理用户发表的评论。它会删除用户的所有评论。

$comments = $wpdb->get_results( $wpdb->prepare( "SELECT comment_ID FROM $wpdb->comments WHERE user_id = %d", $id ) );
if ( $comments ) {
    foreach ( $comments as $comment ) {
        wp_delete_comment( $comment->comment_ID, true );
    }
}

这段代码使用 wpdb->get_results() 函数查询 wp_comments 表,获取用户的所有评论 ID,然后循环调用 wp_delete_comment() 函数删除每条评论。wp_delete_comment() 函数的第二个参数 $force_delete 设置为 true,表示强制删除,不放入回收站。

2.5 元数据清理:不留任何痕迹

接下来,wp_delete_user() 会清理与用户相关的各种元数据。

delete_user_meta( $id );

这行代码调用了 delete_user_meta() 函数,删除了 wp_usermeta 表中所有与用户 ID 相关的记录。这些元数据可能包括用户的个人资料、设置、偏好等等。

2.6 真正的删除:挥一挥衣袖,不带走一片云彩

最后,wp_delete_user() 会真正地从 wp_users 表中删除用户记录。

$result = $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->users WHERE ID = %d", $id ) );

这行代码使用 wpdb->query() 函数执行 SQL DELETE 语句,从 wp_users 表中删除用户 ID 为 $id 的记录。

2.7 缓存失效暂停:保证数据一致性

在删除用户前后,wp_delete_user() 使用了 wp_suspend_cache_invalidation() 函数,暂停了缓存失效操作。

wp_suspend_cache_invalidation( true );
// ... 删除用户的操作 ...
wp_suspend_cache_invalidation( false );

这样做是为了避免在删除用户过程中,由于缓存失效导致数据不一致。

三、代码总结:wp_delete_user() 的核心逻辑

咱们用一个表格来总结一下 wp_delete_user() 的核心逻辑:

步骤 描述 相关代码
前置检查 检查用户是否存在、当前用户是否有权限删除、是否是超级管理员。 get_user( $id ), current_user_can( 'delete_user', $id ), is_multisite() && is_super_admin( $id )
钩子 触发 delete_userpre_user_deletion 动作,允许开发者在删除前后执行自定义代码。使用 apply_filters 过滤重新分配文章的用户ID. do_action( 'delete_user', $id ), do_action( 'pre_user_deletion', $id ), apply_filters( 'wp_delete_user_reassign', $reassign, $id )
文章处理 如果指定了 $reassign 参数,将用户的所有文章和链接重新分配给指定的 $reassign 用户。否则,直接删除用户的所有文章和链接。 $wpdb->update( $wpdb->posts, array( 'post_author' => $reassign ), array( 'post_author' => $id ) ), $wpdb->update( $wpdb->links, array( 'link_owner' => $reassign ), array( 'link_owner' => $id ) ), get_posts( array( 'author' => $id, 'post_type' => 'any', 'post_status' => 'any', 'numberposts' => -1, 'fields' => 'ids' ) ), wp_delete_post( $post, true ), $wpdb->get_results( $wpdb->prepare( "SELECT link_id FROM $wpdb->links WHERE link_owner = %d", $id ) ), wp_delete_link( $link->link_id )
评论处理 删除用户的所有评论。 $wpdb->get_results( $wpdb->prepare( "SELECT comment_ID FROM $wpdb->comments WHERE user_id = %d", $id ) ), wp_delete_comment( $comment->comment_ID, true )
元数据清理 删除与用户相关的各种元数据。 delete_user_meta( $id )
用户删除 wp_users 表中删除用户记录。 $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->users WHERE ID = %d", $id ) )
缓存失效暂停 在删除用户前后,暂停缓存失效操作,保证数据一致性。 wp_suspend_cache_invalidation( true ), wp_suspend_cache_invalidation( false )
钩子 触发 deleted_user 动作,允许开发者在删除后执行自定义代码。 do_action( 'deleted_user', $id, $reassign )

四、使用注意事项:删除需谨慎,操作要小心

wp_delete_user() 函数功能强大,但使用时需要注意以下几点:

  1. 备份数据: 在删除用户之前,务必备份数据库,以防万一。
  2. 权限控制: 只有具有 delete_user 权限的用户才能删除用户。
  3. 谨慎选择 reassign 如果不确定,最好不要指定 $reassign 参数,直接删除用户的所有文章和评论。
  4. 自定义处理: 可以通过钩子自定义删除流程,比如删除与用户相关的自定义数据。

五、总结:wp_delete_user(),WordPress 的“断舍离大师”

总而言之,wp_delete_user() 函数是 WordPress 中一个非常重要的函数,它负责删除用户及其所有相关数据,保证了数据的完整性和一致性。虽然代码有点复杂,但只要理解了其核心逻辑,就能灵活运用,实现各种自定义的删除需求。

希望今天的讲解对大家有所帮助! 下次有机会,咱们再一起深入探讨 WordPress 的其他奥秘。

发表回复

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