* Gets the sites a user belongs to.
* @since 4.7.0 Converted to use `get_sites()`.
* @global wpdb $wpdb WordPress database abstraction object.
* @param int $user_id User ID
* @param bool $all Whether to retrieve all sites, or only sites that are not
* marked as deleted, archived, or spam.
* @return object[] A list of the user's sites. An empty array if the user doesn't exist
* or belongs to no sites.
function get_blogs_of_user( $user_id, $all = false ) {
$user_id = (int) $user_id;
// Logged out users can't have sites.
if ( empty( $user_id ) ) {
* Filters the list of a user's sites before it is populated.
* Returning a non-null value from the filter will effectively short circuit
* get_blogs_of_user(), returning that value instead.
* @param null|object[] $sites An array of site objects of which the user is a member.
* @param int $user_id User ID.
* @param bool $all Whether the returned array should contain all sites, including
* those marked 'deleted', 'archived', or 'spam'. Default false.
$sites = apply_filters( 'pre_get_blogs_of_user', null, $user_id, $all );
$keys = get_user_meta( $user_id );
if ( ! is_multisite() ) {
$site_id = get_current_blog_id();
$sites = array( $site_id => new stdClass() );
$sites[ $site_id ]->userblog_id = $site_id;
$sites[ $site_id ]->blogname = get_option( 'blogname' );
$sites[ $site_id ]->domain = '';
$sites[ $site_id ]->path = '';
$sites[ $site_id ]->site_id = 1;
$sites[ $site_id ]->siteurl = get_option( 'siteurl' );
$sites[ $site_id ]->archived = 0;
$sites[ $site_id ]->spam = 0;
$sites[ $site_id ]->deleted = 0;
if ( isset( $keys[ $wpdb->base_prefix . 'capabilities' ] ) && defined( 'MULTISITE' ) ) {
unset( $keys[ $wpdb->base_prefix . 'capabilities' ] );
$keys = array_keys( $keys );
foreach ( $keys as $key ) {
if ( ! str_ends_with( $key, 'capabilities' ) ) {
if ( $wpdb->base_prefix && ! str_starts_with( $key, $wpdb->base_prefix ) ) {
$site_id = str_replace( array( $wpdb->base_prefix, '_capabilities' ), '', $key );
if ( ! is_numeric( $site_id ) ) {
$site_ids[] = (int) $site_id;
if ( ! empty( $site_ids ) ) {
$_sites = get_sites( $args );
foreach ( $_sites as $site ) {
$sites[ $site->id ] = (object) array(
'userblog_id' => $site->id,
'blogname' => $site->blogname,
'domain' => $site->domain,
'site_id' => $site->network_id,
'siteurl' => $site->siteurl,
'archived' => $site->archived,
'mature' => $site->mature,
'deleted' => $site->deleted,
* Filters the list of sites a user belongs to.
* @param object[] $sites An array of site objects belonging to the user.
* @param int $user_id User ID.
* @param bool $all Whether the returned sites array should contain all sites, including
* those marked 'deleted', 'archived', or 'spam'. Default false.
return apply_filters( 'get_blogs_of_user', $sites, $user_id, $all );
* Finds out whether a user is a member of a given blog.
* @global wpdb $wpdb WordPress database abstraction object.
* @param int $user_id Optional. The unique ID of the user. Defaults to the current user.
* @param int $blog_id Optional. ID of the blog to check. Defaults to the current site.
function is_user_member_of_blog( $user_id = 0, $blog_id = 0 ) {
$user_id = (int) $user_id;
$blog_id = (int) $blog_id;
if ( empty( $user_id ) ) {
$user_id = get_current_user_id();
* Technically not needed, but does save calls to get_site() and get_user_meta()
* in the event that the function is called when a user isn't logged in.
if ( empty( $user_id ) ) {
$user = get_userdata( $user_id );
if ( ! $user instanceof WP_User ) {
if ( ! is_multisite() ) {
if ( empty( $blog_id ) ) {
$blog_id = get_current_blog_id();
$blog = get_site( $blog_id );
if ( ! $blog || ! isset( $blog->domain ) || $blog->archived || $blog->spam || $blog->deleted ) {
$keys = get_user_meta( $user_id );
// No underscore before capabilities in $base_capabilities_key.
$base_capabilities_key = $wpdb->base_prefix . 'capabilities';
$site_capabilities_key = $wpdb->base_prefix . $blog_id . '_capabilities';
if ( isset( $keys[ $base_capabilities_key ] ) && 1 === $blog_id ) {
if ( isset( $keys[ $site_capabilities_key ] ) ) {
* Adds meta data to a user.
* @param int $user_id User ID.
* @param string $meta_key Metadata name.
* @param mixed $meta_value Metadata value. Arrays and objects are stored as serialized data and
* will be returned as the same type when retrieved. Other data types will
* be stored as strings in the database:
* - false is stored and retrieved as an empty string ('')
* - true is stored and retrieved as '1'
* - numbers (both integer and float) are stored and retrieved as strings
* Must be serializable if non-scalar.
* @param bool $unique Optional. Whether the same key should not be added.
* @return int|false Meta ID on success, false on failure.
function add_user_meta( $user_id, $meta_key, $meta_value, $unique = false ) {
return add_metadata( 'user', $user_id, $meta_key, $meta_value, $unique );
* Removes metadata matching criteria from a user.
* You can match based on the key, or key and value. Removing based on key and
* value, will keep from removing duplicate metadata with the same key. It also
* allows removing all metadata matching key, if needed.
* @link https://developer.wordpress.org/reference/functions/delete_user_meta/
* @param int $user_id User ID
* @param string $meta_key Metadata name.
* @param mixed $meta_value Optional. Metadata value. If provided,
* rows will only be removed that match the value.
* Must be serializable if non-scalar. Default empty.
* @return bool True on success, false on failure.
function delete_user_meta( $user_id, $meta_key, $meta_value = '' ) {
return delete_metadata( 'user', $user_id, $meta_key, $meta_value );
* Retrieves user meta field for a user.
* @link https://developer.wordpress.org/reference/functions/get_user_meta/
* @param int $user_id User ID.
* @param string $key Optional. The meta key to retrieve. By default,
* returns data for all keys.
* @param bool $single Optional. Whether to return a single value.
* This parameter has no effect if `$key` is not specified.
* @return mixed An array of values if `$single` is false.
* The value of meta data field if `$single` is true.
* False for an invalid `$user_id` (non-numeric, zero, or negative value).
* An empty array if a valid but non-existing user ID is passed and `$single` is false.
* An empty string if a valid but non-existing user ID is passed and `$single` is true.
* Note: Non-serialized values are returned as strings:
* - false values are returned as empty strings ('')
* - true values are returned as '1'
* - numbers (both integer and float) are returned as strings
* Arrays and objects retain their original type.
function get_user_meta( $user_id, $key = '', $single = false ) {
return get_metadata( 'user', $user_id, $key, $single );
* Updates user meta field based on user ID.
* Use the $prev_value parameter to differentiate between meta fields with the
* If the meta field for the user does not exist, it will be added.
* @link https://developer.wordpress.org/reference/functions/update_user_meta/
* @param int $user_id User ID.
* @param string $meta_key Metadata key.
* @param mixed $meta_value Metadata value. Must be serializable if non-scalar.
* @param mixed $prev_value Optional. Previous value to check before updating.
* If specified, only update existing metadata entries with
* this value. Otherwise, update all entries. Default empty.
* @return int|bool Meta ID if the key didn't exist, true on successful update,
* false on failure or if the value passed to the function
* is the same as the one that is already in the database.
function update_user_meta( $user_id, $meta_key, $meta_value, $prev_value = '' ) {
return update_metadata( 'user', $user_id, $meta_key, $meta_value, $prev_value );
* Counts number of users who have each of the user roles.
* Assumes there are neither duplicated nor orphaned capabilities meta_values.
* Assumes role names are unique phrases. Same assumption made by WP_User_Query::prepare_query()
* Using $strategy = 'time' this is CPU-intensive and should handle around 10^7 users.
* Using $strategy = 'memory' this is memory-intensive and should handle around 10^5 users, but see WP Bug #12257.
* @since 4.4.0 The number of users with no role is now included in the `none` element.
* @since 4.9.0 The `$site_id` parameter was added to support multisite.
* @global wpdb $wpdb WordPress database abstraction object.
* @param string $strategy Optional. The computational strategy to use when counting the users.
* Accepts either 'time' or 'memory'. Default 'time'.
* @param int|null $site_id Optional. The site ID to count users for. Defaults to the current site.
* @type int $total_users Total number of users on the site.
* @type int[] $avail_roles Array of user counts keyed by user role.
function count_users( $strategy = 'time', $site_id = null ) {
$site_id = get_current_blog_id();
* Filters the user count before queries are run.
* Return a non-null value to cause count_users() to return early.
* @param null|array $result The value to return instead. Default null to continue with the query.
* @param string $strategy Optional. The computational strategy to use when counting the users.
* Accepts either 'time' or 'memory'. Default 'time'.
* @param int $site_id The site ID to count users for.
$pre = apply_filters( 'pre_count_users', null, $strategy, $site_id );
$blog_prefix = $wpdb->get_blog_prefix( $site_id );
if ( 'time' === $strategy ) {
if ( is_multisite() && get_current_blog_id() !== $site_id ) {
switch_to_blog( $site_id );
$avail_roles = wp_roles()->get_names();
$avail_roles = wp_roles()->get_names();
// Build a CPU-intensive query that will return concise information.
foreach ( $avail_roles as $this_role => $name ) {
$select_count[] = $wpdb->prepare( 'COUNT(NULLIF(`meta_value` LIKE %s, false))', '%' . $wpdb->esc_like( '"' . $this_role . '"' ) . '%' );
$select_count[] = "COUNT(NULLIF(`meta_value` = 'a:0:{}', false))";
$select_count = implode( ', ', $select_count );
// Add the meta_value index to the selection list, then run the query.
SELECT {$select_count}, COUNT(*)
INNER JOIN {$wpdb->users} ON user_id = ID
WHERE meta_key = '{$blog_prefix}capabilities'
// Run the previous loop again to associate results with role names.
foreach ( $avail_roles as $this_role => $name ) {
$count = (int) $row[ $col++ ];
$role_counts[ $this_role ] = $count;
$role_counts['none'] = (int) $row[ $col++ ];
// Get the meta_value index from the end of the result set.
$total_users = (int) $row[ $col ];
$result['total_users'] = $total_users;
$result['avail_roles'] =& $role_counts;
$users_of_blog = $wpdb->get_col(
INNER JOIN {$wpdb->users} ON user_id = ID
WHERE meta_key = '{$blog_prefix}capabilities'
foreach ( $users_of_blog as $caps_meta ) {
$b_roles = maybe_unserialize( $caps_meta );
if ( ! is_array( $b_roles ) ) {
if ( empty( $b_roles ) ) {
foreach ( $b_roles as $b_role => $val ) {
if ( isset( $avail_roles[ $b_role ] ) ) {
++$avail_roles[ $b_role ];
$avail_roles[ $b_role ] = 1;
$result['total_users'] = count( $users_of_blog );
$result['avail_roles'] =& $avail_roles;
* Returns the number of active users in your installation.
* Note that on a large site the count may be cached and only updated twice daily.
* @since 4.8.0 The `$network_id` parameter has been added.
* @since 6.0.0 Moved to wp-includes/user.php.
* @param int|null $network_id ID of the network. Defaults to the current network.
* @return int Number of active users on the network.
function get_user_count( $network_id = null ) {
if ( ! is_multisite() && null !== $network_id ) {
/* translators: %s: $network_id */
__( 'Unable to pass %s if not using multisite.' ),
'<code>$network_id</code>'
return (int) get_network_option( $network_id, 'user_count', -1 );
* Updates the total count of users on the site if live user counting is enabled.
* @param int|null $network_id ID of the network. Defaults to the current network.
* @return bool Whether the update was successful.
function wp_maybe_update_user_counts( $network_id = null ) {
if ( ! is_multisite() && null !== $network_id ) {
/* translators: %s: $network_id */
__( 'Unable to pass %s if not using multisite.' ),
'<code>$network_id</code>'
$is_small_network = ! wp_is_large_user_count( $network_id );
/** This filter is documented in wp-includes/ms-functions.php */
if ( ! apply_filters( 'enable_live_network_counts', $is_small_network, 'users' ) ) {
return wp_update_user_counts( $network_id );
* Updates the total count of users on the site.
* @global wpdb $wpdb WordPress database abstraction object.
* @param int|null $network_id ID of the network. Defaults to the current network.