探讨 wp_update_user 如何与 usermeta 同步写入逻辑

好的,我们开始今天的讲座,主题是:wp_update_user 如何与 usermeta 同步写入逻辑

wp_update_user 是 WordPress 提供的一个核心函数,用于更新用户信息。 用户信息不仅仅包含基本的 wp_users 表中的字段,还包括大量的用户元数据(usermeta),存储在 wp_usermeta 表中。 理解 wp_update_user 如何与 usermeta 协同工作对于开发涉及用户管理的 WordPress 插件或主题至关重要。

1. wp_update_user 函数概述

wp_update_user 函数位于 wp-includes/user.php 文件中。 它的基本语法如下:

/**
 * Updates user data.
 *
 * @since 2.0.0
 *
 * @param array|WP_User|object $userdata User ID or array of user data.
 * @return int|WP_Error WP_Error on failure, User ID on success.
 */
function wp_update_user( $userdata ) {
    global $wpdb;

    $user = null;

    if ( is_object( $userdata ) ) {
        $userdata = (array) $userdata;
    }

    if ( is_numeric( $userdata ) ) {
        $user_id  = $userdata;
        $userdata = array( 'ID' => $user_id );
    } else {
        $user_id = isset( $userdata['ID'] ) ? absint( $userdata['ID'] ) : 0;
    }

    if ( ! $user_id ) {
        return new WP_Error( 'invalid_user_id', __( 'Invalid user ID.' ) );
    }

    $user = get_user( $user_id );

    if ( ! $user ) {
        return new WP_Error( 'invalid_user', __( 'Invalid user.' ) );
    }

    $data = wp_unslash( $userdata ); // Unslash incoming data.

    // Core fields.
    $update = array();

    if ( isset( $data['user_pass'] ) ) {
        $update['user_pass'] = wp_hash_password( $data['user_pass'] );
    }

    if ( isset( $data['user_login'] ) ) {
        $update['user_login'] = $data['user_login'];
    }

    if ( isset( $data['user_nicename'] ) ) {
        $update['user_nicename'] = sanitize_title_with_dashes( $data['user_nicename'], '', 'save' );
    }

    if ( isset( $data['user_url'] ) ) {
        $update['user_url'] = esc_url_raw( $data['user_url'] );
    }

    if ( isset( $data['user_email'] ) ) {
        $update['user_email'] = sanitize_email( $data['user_email'] );
    }

    if ( isset( $data['display_name'] ) ) {
        $update['display_name'] = trim( $data['display_name'] );
    }

    if ( isset( $data['nickname'] ) ) {
        $update['nickname'] = trim( $data['nickname'] );
    }

    if ( isset( $data['first_name'] ) ) {
        $update['first_name'] = trim( $data['first_name'] );
    }

    if ( isset( $data['last_name'] ) ) {
        $update['last_name'] = trim( $data['last_name'] );
    }

    if ( isset( $data['description'] ) ) {
        $update['description'] = trim( $data['description'] );
    }

    if ( isset( $data['locale'] ) ) {
        $update['locale'] = sanitize_text_field( $data['locale'] );
    }

    // Fires before the user profile is updated.
    do_action( 'profile_update', $user_id, $data );

    if ( ! empty( $update ) ) {
        $where = array( 'ID' => $user_id );

        $updated = $wpdb->update( $wpdb->users, $update, $where );

        if ( false === $updated ) {
            return new WP_Error( 'db_update_error', __( 'Could not update user in database.' ), $wpdb->last_error );
        }
    }

    // Update user meta.
    if ( isset( $data['meta'] ) && is_array( $data['meta'] ) ) {
        foreach ( $data['meta'] as $key => $value ) {
            update_user_meta( $user_id, $key, $value );
        }
    } else {
        // Handle individual usermeta updates.
        $allowed_meta_keys = apply_filters( 'update_user_meta_keys', array() );
        foreach ( $data as $key => $value ) {
            if ( in_array( $key, $allowed_meta_keys, true ) ) {
                update_user_meta( $user_id, $key, $value );
            }
        }
    }

    clean_user_cache( $user_id );

    // Fires after the user profile is updated.
    do_action( 'updated_user', $user_id, $data );

    return $user_id;
}

该函数接收一个数组或对象作为参数,其中包含要更新的用户数据。 它可以更新 wp_users 表中的核心字段(例如 user_pass, user_email, display_name), 并能通过 update_user_meta 函数更新 wp_usermeta 表中的用户元数据。

2. wp_usermeta 表结构

wp_usermeta 表用于存储用户的元数据。 它的基本结构如下:

列名 数据类型 说明
umeta_id bigint(20) UNSIGNED 主键,自增长
user_id bigint(20) UNSIGNED 关联的用户的 ID,外键,指向 wp_users.ID
meta_key varchar(255) 元数据的键名
meta_value longtext 元数据的值

3. update_user_meta 函数

update_user_meta 函数负责更新或添加用户的元数据。 它的基本语法如下:

/**
 * Updates user meta field based on user ID.
 *
 * Use the is_protected_meta() function to check whether the key provided
 * is protected and to prevent updating protected meta.
 *
 * @since 2.9.0
 *
 * @param int    $user_id   User ID.
 * @param string $meta_key  Meta key.
 * @param mixed  $meta_value Meta value. May be an array or object.
 * @param mixed  $prev_value Optional. Previous value to check before removing.
 *                            If specified, only delete rows matching when updating.
 *
 * @return int|bool False on failure, true if value did not change, otherwise the number of rows affected.
 */
function update_user_meta( $user_id, $meta_key, $meta_value, $prev_value = '' ) {
    return update_metadata( 'user', $user_id, $meta_key, $meta_value, $prev_value );
}

update_user_meta 函数实际上是调用了更通用的 update_metadata 函数,该函数可以用于更新各种类型的元数据(包括 post meta, term meta 等)。

4. wp_update_userupdate_user_meta 的同步逻辑

wp_update_user 函数内部直接调用了 update_user_meta 函数来处理用户元数据的更新。 具体逻辑如下:

  1. 数据准备: 首先,wp_update_user 函数接收传入的用户数据,并从中提取出核心字段和元数据。

  2. 核心字段更新: 函数更新 wp_users 表中的核心字段,例如 user_emaildisplay_name

  3. 元数据更新: 函数检查传入的数据中是否包含 meta 键。 如果包含,则遍历 meta 数组,并为每个键值对调用 update_user_meta 函数。

    • 如果没有 meta 键,则检查传入的数据中是否存在允许更新的元数据键。 这通过 apply_filters( 'update_user_meta_keys', array() ) 实现,允许插件或主题定义哪些键可以直接通过 wp_update_user 更新。
  4. update_user_meta 执行: update_user_meta 函数接收用户 ID、元数据键名和元数据值作为参数。 它内部的 update_metadata 函数执行以下操作:

    • 检查是否存在: 首先,查询 wp_usermeta 表中是否存在 user_idmeta_key 匹配的记录。
    • 更新或添加:
      • 如果存在匹配的记录,则更新 meta_value 字段。
      • 如果不存在匹配的记录,则插入一条新的记录,包含 user_id, meta_keymeta_value
    • 可选的 prev_value 参数: update_user_meta 允许传入一个可选的 $prev_value 参数。 如果指定了该参数,则只有在数据库中已存在的 meta_value$prev_value 相匹配时,才会执行更新操作。 这可以用于防止并发更新导致的数据丢失。
    • 数据序列化: update_metadata 函数会自动序列化(serialize)数组和对象类型的 meta_value,以便将其存储在 wp_usermeta 表的 longtext 字段中。 读取时,会自动反序列化(unserialize)。
  5. 清理缓存: wp_update_user 函数在更新完成后,会调用 clean_user_cache 函数清理用户缓存,以确保后续访问的用户信息是最新的。

  6. 触发 Action Hooks: wp_update_user 在更新之前和之后分别触发 profile_updateupdated_user action hooks,允许插件或主题在用户更新过程中执行自定义逻辑。

5. 代码示例

以下是一个示例,演示如何使用 wp_update_user 函数更新用户的核心字段和元数据:

$user_id = 1; // 要更新的用户的 ID

$userdata = array(
    'ID'           => $user_id,
    'user_email'   => '[email protected]',
    'display_name' => 'New Display Name',
    'meta'         => array(
        'phone'   => '123-456-7890',
        'address' => '123 Main Street'
    )
);

$result = wp_update_user( $userdata );

if ( is_wp_error( $result ) ) {
    echo 'Error updating user: ' . $result->get_error_message();
} else {
    echo 'User updated successfully. User ID: ' . $result;
}

在这个示例中,我们更新了用户的 user_emaildisplay_name 字段,并添加了 phoneaddress 两个元数据。

6. 使用 update_user_meta_keys filter

可以通过 update_user_meta_keys filter 允许直接通过 wp_update_user 更新指定的元数据键,而无需将它们放入 meta 数组。

add_filter( 'update_user_meta_keys', 'my_allowed_meta_keys' );

function my_allowed_meta_keys( $allowed_meta_keys ) {
    $allowed_meta_keys[] = 'custom_field_1';
    $allowed_meta_keys[] = 'custom_field_2';
    return $allowed_meta_keys;
}

// 现在可以直接这样更新:
$userdata = array(
    'ID'             => 1,
    'custom_field_1' => 'value 1',
    'custom_field_2' => 'value 2'
);

wp_update_user( $userdata );

7. 使用 profile_updateupdated_user action hooks

profile_updateupdated_user action hooks 允许在用户更新过程中执行自定义逻辑。

add_action( 'profile_update', 'my_profile_update_action', 10, 2 );

function my_profile_update_action( $user_id, $old_user_data ) {
    // 在用户更新之前执行的逻辑
    // 例如,记录日志或发送通知
    error_log( 'User ' . $user_id . ' is being updated.' );
    error_log( print_r( $old_user_data, true ) ); // 打印旧的用户数据
}

add_action( 'updated_user', 'my_updated_user_action', 10, 2 );

function my_updated_user_action( $user_id, $user_data ) {
    // 在用户更新之后执行的逻辑
    // 例如,更新其他相关数据或发送确认邮件
    error_log( 'User ' . $user_id . ' updated successfully.' );
    error_log( print_r( $user_data, true ) ); // 打印新的用户数据
}

8. 安全注意事项

  • 数据验证和清理: 在更新用户信息之前,务必对所有传入的数据进行验证和清理,以防止安全漏洞,例如 SQL 注入和跨站脚本攻击 (XSS)。 使用 sanitize_email, esc_url_raw, sanitize_text_field 等函数来清理数据。
  • 权限控制: 确保只有授权的用户才能更新用户信息。 使用 current_user_can 函数检查当前用户是否具有相应的权限。
  • 防止恶意元数据: 限制可以更新的元数据键,并对元数据值进行验证,以防止恶意用户存储恶意数据。 考虑使用 update_user_meta_keys filter 来限制可更新的键。

9. 常见的 usermeta

以下是一些常见的 usermeta 键:

键名 说明
nickname 昵称
first_name
last_name
description 个人简介
rich_editing 是否使用可视化编辑器
comment_shortcuts 是否启用评论快捷键
admin_color 管理后台配色方案
show_admin_bar_front 是否在前台显示管理工具栏
wp_capabilities 用户角色和权限
wp_user_level 用户级别

这些只是示例, 插件和主题可以根据需要添加自定义的元数据键。

10. 总结

wp_update_user 函数通过直接调用 update_user_meta 函数来同步更新核心用户信息以及用户元数据。 理解 update_user_meta 的工作原理以及如何使用 update_user_meta_keys filter 和 action hooks 对于有效地管理 WordPress 用户信息至关重要。 务必注意数据验证和安全,以确保用户数据的完整性和安全性。

发表回复

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