WordPress函数wp_delete_user在多站点与meta数据清理中的执行逻辑

WordPress 函数 wp_delete_user 在多站点与 Meta 数据清理中的执行逻辑

大家好,今天我们来深入探讨 WordPress 中的一个核心函数:wp_delete_user。 这个函数的功能很简单,就是删除一个用户。然而,在多站点环境下,以及涉及到用户 Meta 数据清理时,它的执行逻辑就变得复杂起来。 本次讲座将详细分析 wp_delete_user 函数的实现,特别是它在多站点场景下的行为,以及如何有效地清理与被删除用户相关的 Meta 数据。

wp_delete_user 函数的基本结构

首先,让我们来了解 wp_delete_user 函数的基本结构。 在 wp-includes/user.php 文件中,你可以找到这个函数的定义。

/**
 * Deletes a user.
 *
 * @since 2.0.0
 *
 * @param int  $id        The user ID.
 * @param int  $reassign  Optional. ID of the user to reassign posts and links to.
 *                        Default null.
 * @return bool True on success, false on failure.
 */
function wp_delete_user( $id, $reassign = null ) {
    global $wpdb;

    $id = (int) $id;

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

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

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

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

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

    $reassign = false === $reassign ? 'false' : $reassign;
    $reassign = (int) $reassign;

    if ( ! empty( $reassign ) ) {
        if ( ! get_user( $reassign ) ) {
            $reassign = null;
        }
    }

    /**
     * Fires immediately before a user is deleted from the database.
     *
     * @since 2.0.0
     *
     * @param int      $id       ID of the user to delete.
     * @param int|null $reassign ID of the user to reassign posts and links to.
     */
    do_action( 'pre_user_deletion', $id, $reassign );

    if ( $reassign ) {
        reassign_posts( $id, $reassign );
    } else {
        wp_delete_user_posts( $id );
    }

    // Remove all of the user's comments.
    $comments = get_comments(
        array(
            'user_id' => $id,
            'number'  => 0, // All comments.
        )
    );
    if ( $comments ) {
        foreach ( $comments as $comment ) {
            wp_delete_comment( $comment->comment_ID, true );
        }
    }

    $meta = get_user_meta( $id );

    foreach ( $meta as $key => $value ) {
        delete_user_meta( $id, $key );
    }

    $wpdb->delete( $wpdb->users, array( 'ID' => $id ) );
    $wpdb->delete( $wpdb->usermeta, array( 'user_id' => $id ) );

    wp_cache_delete_multiple( array(
        "user_{$id}",
        "user_email_{$id}",
        "user_login_{$id}",
        "user_nicename_{$id}",
    ), 'users' );

    wp_cache_delete( 'users_recently_activated', 'users' );

    /**
     * Fires after a user is deleted.
     *
     * @since 2.0.0
     *
     * @param int $id ID of the deleted user.
     */
    do_action( 'deleted_user', $id );

    return true;
}

这个函数的主要步骤包括:

  1. 参数验证: 验证用户 ID 是否有效,用户是否存在,当前用户是否有权限删除该用户,以及是否尝试删除自己。
  2. delete_user 动作钩子: 允许在删除用户之前执行自定义操作。
  3. pre_user_deletion 动作钩子: 允许在删除用户之前执行自定义操作,并提供重新分配用户的选项。
  4. 重新分配文章或删除文章: 如果提供了 $reassign 参数,则将用户的所有文章重新分配给指定的用户。否则,删除用户的所有文章。 这是通过 reassign_postswp_delete_user_posts 函数完成的。
  5. 删除评论: 删除用户的所有评论。
  6. 删除用户 Meta 数据: 删除与用户关联的所有 Meta 数据。
  7. 从数据库中删除用户:wp_userswp_usermeta 表中删除用户及其 Meta 数据。
  8. 清除缓存: 清除与用户相关的缓存。
  9. deleted_user 动作钩子: 允许在删除用户之后执行自定义操作。

多站点环境下的特殊性

在多站点环境下,wp_delete_user 的行为会受到一些额外的因素影响。 主要涉及到用户在不同站点上的角色和权限,以及 Meta 数据的存储方式。

  1. 全局用户: 在多站点网络中,用户可以在多个站点上拥有不同的角色和权限。 wp_users 表存储了所有用户的基本信息,而 wp_usermeta 表存储了用户的 Meta 数据。 这意味着用户数据是全局的。

  2. 站点特定的 Meta 数据: 虽然用户是全局的,但某些 Meta 数据可能是站点特定的。 WordPress 使用不同的前缀来区分不同站点的 Meta 数据。 例如,wp_7_capabilities 存储了站点 ID 为 7 的用户的角色。

  3. 删除用户的影响范围: 删除用户会影响到用户在网络中的所有站点。 这意味着用户将无法再登录到任何站点。

Meta 数据清理的策略

在删除用户时,彻底清理 Meta 数据至关重要,以避免数据残留和潜在的安全问题。 以下是一些清理 Meta 数据的策略:

  1. 默认的 wp_delete_user 行为: wp_delete_user 函数会删除 wp_usermeta 表中所有 user_id 等于被删除用户 ID 的记录。 这包括全局 Meta 数据和站点特定的 Meta 数据。

  2. 自定义清理操作: 有时,我们需要删除与用户相关的其他数据,例如自定义的 Meta 数据表或其他数据库表中的记录。 我们可以使用 pre_user_deletiondeleted_user 动作钩子来执行这些自定义清理操作。

  3. 跨站点 Meta 数据清理: 在多站点环境中,需要特别注意清理跨站点的 Meta 数据。 例如,如果某个插件在每个站点上都为用户存储了特定的 Meta 数据,则需要在删除用户时遍历所有站点并删除这些数据。

代码示例:使用 pre_user_deletion 钩子清理自定义 Meta 数据

以下代码示例展示了如何使用 pre_user_deletion 钩子来清理自定义 Meta 数据。假设我们有一个名为 wp_user_custom_data 的自定义数据库表,其中存储了与用户相关的额外信息。

/**
 * 清理自定义用户数据表.
 *
 * @param int $user_id 要删除的用户的ID.
 * @param int|null $reassign  要重新分配文章的用户的ID,如果为null则删除文章.
 */
function my_custom_delete_user_data( $user_id, $reassign ) {
    global $wpdb;

    $table_name = $wpdb->prefix . 'user_custom_data';

    $wpdb->delete(
        $table_name,
        array(
            'user_id' => $user_id,
        ),
        array( '%d' )
    );
}
add_action( 'pre_user_deletion', 'my_custom_delete_user_data', 10, 2 );

这段代码首先定义了一个名为 my_custom_delete_user_data 的函数,该函数接受两个参数:被删除用户的 ID 和重新分配用户的 ID(如果存在)。 然后,它使用 $wpdb 对象来删除 wp_user_custom_data 表中所有 user_id 等于被删除用户 ID 的记录。 最后,它使用 add_action 函数将 my_custom_delete_user_data 函数挂载到 pre_user_deletion 动作钩子上。 这样,在删除用户之前,就会自动执行 my_custom_delete_user_data 函数,从而清理自定义 Meta 数据。

代码示例:跨站点 Meta 数据清理

以下代码示例展示了如何在多站点环境中清理跨站点的 Meta 数据。假设我们有一个名为 my_plugin_user_option 的用户 Meta 键,需要在所有站点上删除它。

/**
 * 清理所有站点上的用户 Meta 数据.
 *
 * @param int $user_id 要删除的用户的ID.
 */
function my_custom_delete_user_site_meta( $user_id ) {
    if ( ! is_multisite() ) {
        return;
    }

    $sites = get_sites();

    foreach ( $sites as $site ) {
        switch_to_blog( $site->blog_id );
        delete_user_meta( $user_id, 'my_plugin_user_option' );
        restore_current_blog();
    }
}
add_action( 'deleted_user', 'my_custom_delete_user_site_meta' );

这段代码首先检查是否是多站点环境。 如果不是,则直接返回。 然后,它使用 get_sites 函数获取所有站点的列表。 接下来,它遍历所有站点,并使用 switch_to_blog 函数切换到每个站点。 在每个站点上,它使用 delete_user_meta 函数删除 my_plugin_user_option Meta 数据。 最后,它使用 restore_current_blog 函数恢复到当前站点。 我们将这个函数挂载到 deleted_user 动作钩子上,确保在用户删除后执行清理操作。

深入分析 reassign_postswp_delete_user_posts

wp_delete_user 函数中,文章的处理方式取决于 $reassign 参数。 如果提供了 $reassign 参数,则调用 reassign_posts 函数将文章重新分配给指定的用户。 否则,调用 wp_delete_user_posts 函数删除文章。

reassign_posts 函数

/**
 * Reassigns posts and links from one user to another.
 *
 * @since 2.9.0
 *
 * @global wpdb $wpdb WordPress database abstraction object.
 *
 * @param int $old_user_id ID of the user whose posts/links will be reassigned.
 * @param int $new_user_id ID of the user who will receive the posts/links.
 * @return void
 */
function reassign_posts( $old_user_id, $new_user_id ) {
    global $wpdb;

    $old_user_id = (int) $old_user_id;
    $new_user_id = (int) $new_user_id;

    /**
     * Fires before posts are reassigned.
     *
     * @since 2.9.0
     *
     * @param int $old_user_id ID of the user whose posts/links will be reassigned.
     * @param int $new_user_id ID of the user who will receive the posts/links.
     */
    do_action( 'reassign_posts', $old_user_id, $new_user_id );

    $wpdb->update( $wpdb->posts, array( 'post_author' => $new_user_id ), array( 'post_author' => $old_user_id ) );

    /**
     * Fires after posts are reassigned.
     *
     * @since 2.9.0
     *
     * @param int $old_user_id ID of the user whose posts/links were reassigned.
     * @param int $new_user_id ID of the user who received the posts/links.
     */
    do_action( 'reassigned_posts', $old_user_id, $new_user_id );
}

reassign_posts 函数只是简单地更新 wp_posts 表中的 post_author 字段,将所有属于 $old_user_id 的文章的作者更改为 $new_user_id

wp_delete_user_posts 函数

/**
 * Deletes all posts from a user.
 *
 * @since 2.0.0
 *
 * @global wpdb $wpdb WordPress database abstraction object.
 *
 * @param int $id User ID.
 * @return void
 */
function wp_delete_user_posts( $id ) {
    global $wpdb;

    $id = (int) $id;

    /**
     * Fires before posts are deleted.
     *
     * @since 2.0.0
     *
     * @param int $id ID of user whose posts are to be deleted.
     */
    do_action( 'delete_user_posts', $id );

    $post_ids = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_author = %d", $id ) );

    if ( $post_ids ) {
        foreach ( $post_ids as $post_id ) {
            wp_delete_post( $post_id, true );
        }
    }

    /**
     * Fires after posts are deleted.
     *
     * @since 2.0.0
     *
     * @param int $id ID of user whose posts were deleted.
     */
    do_action( 'deleted_user_posts', $id );
}

wp_delete_user_posts 函数首先获取用户的所有文章 ID,然后遍历这些 ID,并使用 wp_delete_post 函数删除每篇文章。 wp_delete_post 函数会触发 wp_trash_post 钩子(如果文章可以被放入回收站),并且还会删除与文章相关的 Meta 数据、评论和附件。 wp_delete_post($post_id, true) 中的 true 参数表示强制删除,不放入回收站。

特殊场景考虑

  1. 插件数据依赖: 有些插件可能会在自己的数据库表中存储与用户 ID 关联的数据。 在删除用户时,这些数据不会被自动删除。 因此,你需要编写自定义代码来清理这些数据。 使用 pre_user_deletiondeleted_user 钩子来执行清理操作。

  2. 用户角色和权限: 在多站点环境中,用户可能在不同的站点上拥有不同的角色和权限。 在删除用户时,需要确保彻底删除所有站点上的角色和权限信息。

  3. 缓存问题: 删除用户后,可能会出现缓存问题。 确保清除与用户相关的缓存,以避免显示过时的数据。 WordPress 提供了 wp_cache_deletewp_cache_flush 等函数来清除缓存。 wp_delete_user 函数内部已经清除了部分缓存,但你可能需要根据实际情况进行额外的缓存清理。

表格:wp_delete_user 函数相关钩子

钩子名称 触发时机 参数 说明
delete_user 在删除用户之前 $id (int): 要删除的用户的 ID 允许在删除用户之前执行自定义操作。
pre_user_deletion 在从数据库中删除用户之前 $id (int): 要删除的用户的 ID, $reassign (int null): 要重新分配文章的用户的 ID 允许在删除用户之前执行自定义操作,并提供重新分配用户的选项。
deleted_user 在删除用户之后 $id (int): 已删除的用户的 ID 允许在删除用户之后执行自定义操作。
reassign_posts 在重新分配文章之前 $old_user_id (int): 要删除的用户的 ID, $new_user_id (int): 要重新分配文章的用户的 ID 允许在重新分配文章之前执行自定义操作。
reassigned_posts 在重新分配文章之后 $old_user_id (int): 要删除的用户的 ID, $new_user_id (int): 要重新分配文章的用户的 ID 允许在重新分配文章之后执行自定义操作。
delete_user_posts 在删除用户的所有文章之前 $id (int): 要删除的用户的 ID 允许在删除用户的文章之前执行自定义操作。
deleted_user_posts 在删除用户的所有文章之后 $id (int): 要删除的用户的 ID 允许在删除用户的文章之后执行自定义操作。

总结 wp_delete_user 函数的重点

wp_delete_user 是 WordPress 中删除用户的核心函数,在多站点环境下需要特别注意站点特定数据的清理。 通过使用 pre_user_deletiondeleted_user 钩子,可以自定义清理操作,确保数据完整性。 理解 reassign_postswp_delete_user_posts 函数的行为对于正确处理用户文章至关重要。

发表回复

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