WordPress Version: 6.4
/**
* 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 3.0.0
* @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.
* @return array {
* User counts.
*
* @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)
{
global $wpdb;
// Initialize.
if (!$site_id) {
$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.
*
* @since 5.1.0
*
* @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);
if (null !== $pre) {
return $pre;
}
$blog_prefix = $wpdb->get_blog_prefix($site_id);
$result = array();
if ('time' === $strategy) {
if (is_multisite() && get_current_blog_id() != $site_id) {
switch_to_blog($site_id);
$avail_roles = wp_roles()->get_names();
restore_current_blog();
} else {
$avail_roles = wp_roles()->get_names();
}
// Build a CPU-intensive query that will return concise information.
$select_count = array();
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.
$row = $wpdb->get_row("\n\t\t\tSELECT {$select_count}, COUNT(*)\n\t\t\tFROM {$wpdb->usermeta}\n\t\t\tINNER JOIN {$wpdb->users} ON user_id = ID\n\t\t\tWHERE meta_key = '{$blog_prefix}capabilities'\n\t\t", ARRAY_N);
// Run the previous loop again to associate results with role names.
$col = 0;
$role_counts = array();
foreach ($avail_roles as $this_role => $name) {
$count = (int) $row[$col++];
if ($count > 0) {
$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;
} else {
$avail_roles = array('none' => 0);
$users_of_blog = $wpdb->get_col("\n\t\t\tSELECT meta_value\n\t\t\tFROM {$wpdb->usermeta}\n\t\t\tINNER JOIN {$wpdb->users} ON user_id = ID\n\t\t\tWHERE meta_key = '{$blog_prefix}capabilities'\n\t\t");
foreach ($users_of_blog as $caps_meta) {
$b_roles = maybe_unserialize($caps_meta);
if (!is_array($b_roles)) {
continue;
}
if (empty($b_roles)) {
++$avail_roles['none'];
}
foreach ($b_roles as $b_role => $val) {
if (isset($avail_roles[$b_role])) {
++$avail_roles[$b_role];
} else {
$avail_roles[$b_role] = 1;
}
}
}
$result['total_users'] = count($users_of_blog);
$result['avail_roles'] =& $avail_roles;
}
return $result;
}