WordPress.org

Make WordPress Core


Ignore:
Timestamp:
03/03/2010 07:08:30 PM (12 years ago)
Author:
ryan
Message:

Improve user listing performance. Props miqrogroove. see #11914

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/wp-includes/user.php

    r13553 r13576  
    149149 * @return int Amount of posts user has written.
    150150 */
    151 function get_usernumposts($userid) {
     151function count_user_posts($userid) {
    152152    global $wpdb;
    153     $userid = (int) $userid;
    154     $count = $wpdb->get_var( $wpdb->prepare("SELECT COUNT(*) FROM $wpdb->posts WHERE post_author = %d AND post_type = 'post' AND ", $userid) . get_private_posts_cap_sql('post'));
     153
     154    $where = get_posts_by_author_sql('post', TRUE, $userid);
     155
     156    $count = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->posts $where" );
     157
    155158    return apply_filters('get_usernumposts', $count, $userid);
     159}
     160
     161/**
     162 * Number of posts written by a list of users.
     163 *
     164 * @since 3.0.0
     165 * @param array $userid User ID number list.
     166 * @return array Amount of posts each user has written.
     167 */
     168function count_many_users_posts($users) {
     169    global $wpdb;
     170   
     171    if (0 == count($users))
     172        return array();
     173       
     174    $userlist = implode(',', $users);
     175    $where = get_posts_by_author_sql('post');
     176
     177    $result = $wpdb->get_results( "SELECT post_author, COUNT(*) FROM $wpdb->posts $where AND post_author IN ($userlist) GROUP BY post_author", ARRAY_N );
     178
     179    $count = array();
     180    foreach($result as $row) {
     181        $count[$row[0]] = $row[1];
     182    }
     183
     184    foreach($users as $id) {
     185        $id = (string) $id;
     186        if (!isset($count[$id]))
     187            $count[$id] = 0;
     188    }
     189
     190    return $count;
    156191}
    157192
     
    341376function update_user_meta($user_id, $meta_key, $meta_value, $prev_value = '') {
    342377    return update_metadata('user', $user_id, $meta_key, $meta_value, $prev_value);
     378}
     379
     380/**
     381 * Count number of users who have each of the user roles.
     382 *
     383 * Assumes there are neither duplicated nor orphaned capabilities meta_values.
     384 * Assumes role names are unique phrases.  Same assumption made by WP_User_Search::prepare_query()
     385 * Using $strategy = 'time' this is CPU-intensive and should handle around 10^7 users.
     386 * Using $strategy = 'memory' this is memory-intensive and should handle around 10^5 users, but see WP Bug #12257.
     387 *
     388 * @since 3.0.0
     389 * @param string $strategy 'time' or 'memory'
     390 * @return array Includes a grand total and an array of counts indexed by role strings.
     391 */
     392function count_users($strategy = 'time') {
     393    global $wpdb, $blog_id, $wp_roles;
     394
     395    // Initialize
     396    $id = (int) $blog_id;
     397    $blog_prefix = $wpdb->get_blog_prefix($id);
     398    $result = array();
     399
     400    if ('time' == $strategy) {
     401        $avail_roles = $wp_roles->get_names();
     402
     403        // Build a CPU-intensive query that will return concise information.
     404        $select_count = array();
     405        foreach ( $avail_roles as $this_role => $name ) {
     406            $select_count[] = "COUNT(NULLIF(`meta_value` LIKE '%" . like_escape($this_role) . "%', FALSE))";
     407        }
     408        $select_count = implode(', ', $select_count);
     409
     410        // Add the meta_value index to the selection list, then run the query.
     411        $row = $wpdb->get_row( "SELECT $select_count, COUNT(*) FROM $wpdb->usermeta WHERE meta_key = '{$blog_prefix}capabilities'", ARRAY_N );
     412
     413        // Run the previous loop again to associate results with role names.
     414        $col = 0;
     415        $role_counts = array();
     416        foreach ( $avail_roles as $this_role => $name ) {
     417            $count = (int) $row[$col++];
     418            if ($count > 0) {
     419                $role_counts[$this_role] = $count;
     420            }
     421        }
     422
     423        // Get the meta_value index from the end of the result set.
     424        $total_users = (int) $row[$col];
     425
     426        $result['total_users'] = $total_users;
     427        $result['avail_roles'] =& $role_counts;
     428    } else {
     429        $avail_roles = array();
     430
     431        $users_of_blog = $wpdb->get_col( "SELECT meta_value FROM $wpdb->usermeta WHERE meta_key = '{$blog_prefix}capabilities'" );
     432
     433        foreach ( $users_of_blog as $caps_meta ) {
     434            $b_roles = unserialize($caps_meta);
     435            if ( is_array($b_roles) ) {
     436                foreach ( $b_roles as $b_role => $val ) {
     437                    if ( isset($avail_roles[$b_role]) ) {
     438                        $avail_roles[$b_role]++;
     439                    } else {
     440                        $avail_roles[$b_role] = 1;
     441                    }
     442                }
     443            }
     444        }
     445
     446        $result['total_users'] = count( $users_of_blog );
     447        $result['avail_roles'] =& $avail_roles;
     448    }
     449
     450    return $result;
    343451}
    344452
     
    499607 * The finished user data is cached, but the cache is not used to fill in the
    500608 * user data for the given object. Once the function has been used, the cache
    501  * should be used to retrieve user data. The purpose seems then to be to ensure
    502  * that the data in the object is always fresh.
     609 * should be used to retrieve user data. The intention is if the current data
     610 * had been cached already, there would be no need to call this function.
    503611 *
    504612 * @access private
     
    509617 */
    510618function _fill_user( &$user ) {
     619    $metavalues = get_user_metavalues(array($user->ID));
     620    _fill_single_user($user, $metavalues[$user->ID]);
     621}
     622
     623/**
     624 * Perform the query to get the $metavalues array(s) needed by _fill_user and _fill_many_users
     625 *
     626 * @since 3.0.0
     627 * @param array $ids User ID numbers list.
     628 * @return array of arrays. The array is indexed by user_id, containing $metavalues object arrays.
     629 */
     630function get_user_metavalues($ids) {
    511631    global $wpdb;
    512632
     633    $clean = array_map('intval', $ids);
     634    if ( 0 == count($clean) )
     635        return $objects;
     636
     637    $list = implode(',', $clean);
     638
    513639    $show = $wpdb->hide_errors();
    514     $metavalues = $wpdb->get_results($wpdb->prepare("SELECT meta_key, meta_value FROM $wpdb->usermeta WHERE user_id = %d", $user->ID));
     640    $metavalues = $wpdb->get_results("SELECT user_id, meta_key, meta_value FROM $wpdb->usermeta WHERE user_id IN ($list)");
    515641    $wpdb->show_errors($show);
    516642
    517     if ( $metavalues ) {
    518         foreach ( (array) $metavalues as $meta ) {
    519             $value = maybe_unserialize($meta->meta_value);
    520             $user->{$meta->meta_key} = $value;
    521         }
     643    $objects = array();
     644    foreach($clean as $id) {
     645        $objects[$id] = array();
     646    }
     647    foreach($metavalues as $meta_object) {
     648        $objects[$meta_object->user_id][] = $meta_object;
     649    }
     650
     651    return $objects;
     652}
     653
     654/**
     655 * Unserialize user metadata, fill $user object, then cache everything.
     656 *
     657 * @since 3.0.0
     658 * @param object $user The User object.
     659 * @param array $metavalues An array of objects provided by get_user_metavalues()
     660 */
     661function _fill_single_user( &$user, &$metavalues ) {
     662    global $wpdb;
     663
     664    foreach ( $metavalues as $meta ) {
     665        $value = maybe_unserialize($meta->meta_value);
     666        $user->{$meta->meta_key} = $value;
    522667    }
    523668
     
    534679        $user->user_description = $user->description;
    535680
    536     wp_cache_add($user->ID, $user, 'users');
    537     wp_cache_add($user->user_login, $user->ID, 'userlogins');
    538     wp_cache_add($user->user_email, $user->ID, 'useremail');
    539     wp_cache_add($user->user_nicename, $user->ID, 'userslugs');
     681    update_user_caches($user);
     682}
     683
     684/**
     685 * Take an array of user objects, fill them with metas, and cache them.
     686 *
     687 * @since 3.0.0
     688 * @param array $users User objects
     689 * @param array $metas User metavalues objects
     690 */
     691function _fill_many_users( &$users ) {
     692    $ids = array();
     693    foreach($users as $user_object) {
     694        $ids[] = $user_object->ID;
     695    }
     696
     697    $metas = get_user_metavalues($ids);
     698
     699    foreach($users as $user_object) {
     700        if (isset($metas[$user_object->ID])) {
     701            _fill_single_user($user_object, $metas[$user_object->ID]);
     702        }
     703    }
    540704}
    541705
     
    657821
    658822/**
     823 * Update all user caches
     824 *
     825 * @since 3.0.0
     826 *
     827 * @param object $user User object to be cached
     828 */
     829function update_user_caches(&$user) {
     830    wp_cache_add($user->ID, $user, 'users');
     831    wp_cache_add($user->user_login, $user->ID, 'userlogins');
     832    wp_cache_add($user->user_email, $user->ID, 'useremail');
     833    wp_cache_add($user->user_nicename, $user->ID, 'userslugs');
     834}
     835
     836/**
    659837 * Clean all user caches
    660838 *
    661  * @since 3.0
     839 * @since 3.0.0
    662840 *
    663841 * @param int $id User ID
    664  * @return void
    665842 */
    666843function clean_user_cache($id) {
Note: See TracChangeset for help on using the changeset viewer.