Make WordPress Core

Changeset 53011


Ignore:
Timestamp:
03/29/2022 12:41:00 PM (2 years ago)
Author:
spacedmonkey
Message:

Users: Introduce the concept of a large site to single site installations.

Currently in WordPress multisite there is a concept of large networks. The function wp_is_large_network is used to determine if a network has a large number of sites or users. If a network is marked as large, then
expensive queries to calculate user counts are not run on page load but deferred to scheduled events. However there are a number of places in a single site installation where this functionality would also be useful, as
expensive calls to count users and roles can make screens in the admin extremely slow.

In this change, the get_user_count function and related functionality around it is ported to be available in a single site context. This means that expensive calls to the count_users function are replaced with
calls to get_user_count. This change also includes a new function called wp_is_large_user_count and a filter of the same name, to mark if a site is large.

Props johnbillion, Spacedmonkey, Mista-Flo, lumpysimon, tharsheblows, obenland, miss_jwo, jrchamp, flixos90, macbookandrew, pento, desrosj, johnjamesjacoby, jb510, davidbaumwald, costdev.
Fixes #38741.

Location:
trunk
Files:
1 added
11 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-admin/includes/class-wp-debug-data.php

    r53003 r53011  
    389389            }
    390390
    391             $info['wp-core']['fields']['user_count'] = array(
    392                 'label' => __( 'User count' ),
    393                 'value' => get_user_count(),
    394             );
    395 
    396391            $info['wp-core']['fields']['site_count'] = array(
    397392                'label' => __( 'Site count' ),
     
    403398                'value' => $network_query->found_networks,
    404399            );
    405         } else {
    406             $user_count = count_users();
    407 
    408             $info['wp-core']['fields']['user_count'] = array(
    409                 'label' => __( 'User count' ),
    410                 'value' => $user_count['total_users'],
    411             );
    412         }
     400        }
     401
     402        $info['wp-core']['fields']['user_count'] = array(
     403            'label' => __( 'User count' ),
     404            'value' => get_user_count(),
     405        );
    413406
    414407        // WordPress features requiring processing.
  • trunk/src/wp-admin/includes/class-wp-posts-list-table.php

    r52929 r53011  
    16541654
    16551655                <?php
    1656                 if ( post_type_supports( $screen->post_type, 'author' ) ) {
     1656                if ( post_type_supports( $screen->post_type, 'author' ) && ! wp_is_large_user_count() ) {
    16571657                    $authors_dropdown = '';
    16581658
  • trunk/src/wp-admin/includes/class-wp-users-list-table.php

    r52581 r53011  
    178178        $wp_roles = wp_roles();
    179179
     180        $count_users = ! wp_is_large_user_count();
     181
    180182        if ( $this->is_site_users ) {
    181183            $url = 'site-users.php?id=' . $this->site_id;
    182             switch_to_blog( $this->site_id );
    183             $users_of_blog = count_users( 'time', $this->site_id );
    184             restore_current_blog();
    185184        } else {
    186             $url           = 'users.php';
    187             $users_of_blog = count_users();
    188         }
    189 
    190         $total_users = $users_of_blog['total_users'];
    191         $avail_roles =& $users_of_blog['avail_roles'];
    192         unset( $users_of_blog );
    193 
     185            $url = 'users.php';
     186        }
     187
     188        $role_links              = array();
     189        $avail_roles             = array();
     190        $all_text                = __( 'All' );
    194191        $current_link_attributes = empty( $role ) ? ' class="current" aria-current="page"' : '';
    195192
    196         $role_links        = array();
    197         $role_links['all'] = sprintf(
    198             '<a href="%s"%s>%s</a>',
    199             $url,
    200             $current_link_attributes,
    201             sprintf(
     193        if ( $count_users ) {
     194            if ( $this->is_site_users ) {
     195                switch_to_blog( $this->site_id );
     196                $users_of_blog = count_users( 'time', $this->site_id );
     197                restore_current_blog();
     198            } else {
     199                $users_of_blog = count_users();
     200            }
     201
     202            $total_users = $users_of_blog['total_users'];
     203            $avail_roles =& $users_of_blog['avail_roles'];
     204            unset( $users_of_blog );
     205
     206            $all_text = sprintf(
    202207                /* translators: %s: Number of users. */
    203208                _nx(
     
    208213                ),
    209214                number_format_i18n( $total_users )
    210             )
    211         );
     215            );
     216        }
     217
     218        $role_links['all'] = sprintf( '<a href="%s"%s>%s</a>', $url, $current_link_attributes, $all_text );
    212219
    213220        foreach ( $wp_roles->get_names() as $this_role => $name ) {
    214             if ( ! isset( $avail_roles[ $this_role ] ) ) {
     221            if ( $count_users && ! isset( $avail_roles[ $this_role ] ) ) {
    215222                continue;
    216223            }
     
    223230
    224231            $name = translate_user_role( $name );
    225             $name = sprintf(
    226                 /* translators: 1: User role name, 2: Number of users. */
    227                 __( '%1$s <span class="count">(%2$s)</span>' ),
    228                 $name,
    229                 number_format_i18n( $avail_roles[ $this_role ] )
    230             );
     232            if ( $count_users ) {
     233                $name = sprintf(
     234                    /* translators: 1: User role name, 2: Number of users. */
     235                    __( '%1$s <span class="count">(%2$s)</span>' ),
     236                    $name,
     237                    number_format_i18n( $avail_roles[ $this_role ] )
     238                );
     239            }
    231240
    232241            $role_links[ $this_role ] = "<a href='" . esc_url( add_query_arg( 'role', $this_role, $url ) ) . "'$current_link_attributes>$name</a>";
  • trunk/src/wp-admin/includes/schema.php

    r52215 r53011  
    12681268        'global_terms_enabled'        => global_terms_enabled() ? '1' : '0',
    12691269        'ms_files_rewriting'          => is_multisite() ? get_site_option( 'ms_files_rewriting' ) : '0',
     1270        'user_count'                  => get_site_option( 'user_count' ),
    12701271        'initial_db_version'          => get_option( 'initial_db_version' ),
    12711272        'active_sitewide_plugins'     => array(),
  • trunk/src/wp-admin/includes/upgrade.php

    r52662 r53011  
    846846    }
    847847
     848    if ( $wp_current_db_version < 53011 ) {
     849        upgrade_600();
     850    }
     851
    848852    maybe_disable_link_manager();
    849853
     
    22842288
    22852289/**
     2290 * Executes changes made in WordPress 6.0.0.
     2291 *
     2292 * @ignore
     2293 * @since 6.0.0
     2294 *
     2295 * @global int $wp_current_db_version The old (current) database version.
     2296 */
     2297function upgrade_600() {
     2298    global $wp_current_db_version;
     2299
     2300    if ( $wp_current_db_version < 53011 ) {
     2301        wp_update_user_counts();
     2302    }
     2303}
     2304
     2305/**
    22862306 * Executes network-level upgrade routines.
    22872307 *
  • trunk/src/wp-includes/default-filters.php

    r52768 r53011  
    9898// Meta.
    9999add_filter( 'register_meta_args', '_wp_register_meta_args_allowed_list', 10, 2 );
     100
     101// Counts.
     102add_action( 'admin_init', 'wp_schedule_update_user_counts' );
     103add_action( 'wp_update_user_counts', 'wp_schedule_update_user_counts', 10, 0 );
     104foreach ( array( 'user_register', 'deleted_user' ) as $action ) {
     105    add_action( $action, 'wp_maybe_update_user_counts', 10, 0 );
     106}
    100107
    101108// Post meta.
  • trunk/src/wp-includes/functions.php

    r52955 r53011  
    84198419    return abs( (float) $expected - (float) $actual ) <= $precision;
    84208420}
     8421
     8422/**
     8423 * Returns the number of active users in your installation.
     8424 *
     8425 * Note that on a large site the count may be cached and only updated twice daily.
     8426 *
     8427 * @since MU (3.0.0)
     8428 * @since 4.8.0 The `$network_id` parameter has been added.
     8429 * @since 6.0.0 Move to wp-includes/functions.php.
     8430 *
     8431 * @param int|null $network_id ID of the network. Default is the current network.
     8432 * @return int Number of active users on the network.
     8433 */
     8434function get_user_count( $network_id = null ) {
     8435    if ( ! is_multisite() && null !== $network_id ) {
     8436        _doing_it_wrong( __FUNCTION__, __( 'Unable to pass $network_id if not using multisite.' ), '6.0.0' );
     8437    }
     8438    return (int) get_network_option( $network_id, 'user_count', -1 );
     8439}
     8440
     8441/**
     8442 * Updates the total count of users on the site if live user counting is enabled.
     8443 *
     8444 * @since 6.0.0
     8445 *
     8446 * @param int|null $network_id ID of the network. Default is the current network.
     8447 * @return bool Whether the update was successful.
     8448 */
     8449function wp_maybe_update_user_counts( $network_id = null ) {
     8450    if ( ! is_multisite() && null !== $network_id ) {
     8451        _doing_it_wrong( __FUNCTION__, __( 'Unable to pass $network_id if not using multisite.' ), '6.0.0' );
     8452    }
     8453
     8454    $is_small_network = ! wp_is_large_user_count( $network_id );
     8455    /** This filter is documented in wp-includes/ms-functions.php */
     8456    if ( ! apply_filters( 'enable_live_network_counts', $is_small_network, 'users' ) ) {
     8457        return false;
     8458    }
     8459
     8460    return wp_update_user_counts( $network_id );
     8461}
     8462
     8463/**
     8464 * Updates the total count of users on the site.
     8465 *
     8466 * @global wpdb $wpdb WordPress database abstraction object.
     8467 * @since 6.0.0
     8468 *
     8469 * @param int|null $network_id ID of the network. Default is the current network.
     8470 * @return bool Whether the update was successful.
     8471 */
     8472function wp_update_user_counts( $network_id = null ) {
     8473    global $wpdb;
     8474
     8475    if ( ! is_multisite() && null !== $network_id ) {
     8476        _doing_it_wrong( __FUNCTION__, __( 'Unable to pass $network_id if not using multisite.' ), '6.0.0' );
     8477    }
     8478
     8479    $query = "SELECT COUNT(ID) as c FROM $wpdb->users";
     8480    if ( is_multisite() ) {
     8481        $query .= " WHERE spam = '0' AND deleted = '0'";
     8482    }
     8483
     8484    $count = $wpdb->get_var( $query );
     8485
     8486    return update_network_option( $network_id, 'user_count', $count );
     8487}
     8488
     8489/**
     8490 * Schedules a recurring recalculation of the total count of users.
     8491 *
     8492 * @since 6.0.0
     8493 */
     8494function wp_schedule_update_user_counts() {
     8495    if ( ! is_main_site() ) {
     8496        return;
     8497    }
     8498
     8499    if ( ! wp_next_scheduled( 'wp_update_user_counts' ) && ! wp_installing() ) {
     8500        wp_schedule_event( time(), 'twicedaily', 'wp_update_user_counts' );
     8501    }
     8502}
     8503
     8504/**
     8505 * Determines whether the site has a large number of users.
     8506 *
     8507 * The default criteria for a large site is more than 10,000 users.
     8508 *
     8509 * @since 6.0.0
     8510 *
     8511 * @param int|null $network_id ID of the network. Default is the current network.
     8512 * @return bool Whether the site has a large number of users.
     8513 */
     8514function wp_is_large_user_count( $network_id = null ) {
     8515    if ( ! is_multisite() && null !== $network_id ) {
     8516        _doing_it_wrong( __FUNCTION__, __( 'Unable to pass $network_id if not using multisite.' ), '6.0.0' );
     8517    }
     8518    $count = get_user_count( $network_id );
     8519
     8520    /**
     8521     * Filters whether the site is considered large, based on its number of users.
     8522     *
     8523     * @since 6.0.0
     8524     *
     8525     * @param bool     $is_large_user_count Whether the site has a large number of users.
     8526     * @param int      $count               The total number of users.
     8527     * @param int|null $network_id          ID of the network. `null` represents the current network.
     8528     */
     8529    return apply_filters( 'wp_is_large_user_count', $count > 10000, $count, $network_id );
     8530}
  • trunk/src/wp-includes/ms-default-filters.php

    r52207 r53011  
    8585add_action( 'admin_init', 'wp_schedule_update_network_counts' );
    8686add_action( 'update_network_counts', 'wp_update_network_counts', 10, 0 );
    87 foreach ( array( 'user_register', 'deleted_user', 'wpmu_new_user', 'make_spam_user', 'make_ham_user' ) as $action ) {
     87foreach ( array( 'wpmu_new_user', 'make_spam_user', 'make_ham_user' ) as $action ) {
    8888    add_action( $action, 'wp_maybe_update_network_user_counts', 10, 0 );
    8989}
     90
     91// These counts are handled by wp_update_network_counts() on Multisite:
     92remove_action( 'admin_init', 'wp_schedule_update_user_counts' );
     93remove_action( 'wp_update_user_counts', 'wp_schedule_update_user_counts' );
     94
    9095foreach ( array( 'make_spam_blog', 'make_ham_blog', 'archive_blog', 'unarchive_blog', 'make_delete_blog', 'make_undelete_blog' ) as $action ) {
    9196    add_action( $action, 'wp_maybe_update_network_site_counts', 10, 0 );
  • trunk/src/wp-includes/ms-functions.php

    r52737 r53011  
    9999        return $primary;
    100100    }
    101 }
    102 
    103 /**
    104  * The number of active users in your installation.
    105  *
    106  * The count is cached and updated twice daily. This is not a live count.
    107  *
    108  * @since MU (3.0.0)
    109  * @since 4.8.0 The `$network_id` parameter has been added.
    110  *
    111  * @param int|null $network_id ID of the network. Default is the current network.
    112  * @return int Number of active users on the network.
    113  */
    114 function get_user_count( $network_id = null ) {
    115     return get_network_option( $network_id, 'user_count' );
    116101}
    117102
     
    26122597 * @since 3.7.0
    26132598 * @since 4.8.0 The `$network_id` parameter has been added.
    2614  *
    2615  * @global wpdb $wpdb WordPress database abstraction object.
     2599 * @since 6.0.0 This function is now a wrapper for wp_update_user_counts().
    26162600 *
    26172601 * @param int|null $network_id ID of the network. Default is the current network.
    26182602 */
    26192603function wp_update_network_user_counts( $network_id = null ) {
    2620     global $wpdb;
    2621 
    2622     $count = $wpdb->get_var( "SELECT COUNT(ID) as c FROM $wpdb->users WHERE spam = '0' AND deleted = '0'" );
    2623     update_network_option( $network_id, 'user_count', $count );
     2604    wp_update_user_counts( $network_id );
    26242605}
    26252606
     
    27552736    if ( 'users' === $using ) {
    27562737        $count = get_user_count( $network_id );
     2738
     2739        $is_large_network = wp_is_large_user_count( $network_id );
     2740
    27572741        /**
    27582742         * Filters whether the network is considered large.
     
    27662750         * @param int    $network_id       The ID of the network being checked.
    27672751         */
    2768         return apply_filters( 'wp_is_large_network', $count > 10000, 'users', $count, $network_id );
     2752        return apply_filters( 'wp_is_large_network', $is_large_network, 'users', $count, $network_id );
    27692753    }
    27702754
  • trunk/src/wp-includes/update.php

    r52610 r53011  
    8181
    8282    if ( is_multisite() ) {
    83         $user_count        = get_user_count();
    8483        $num_blogs         = get_blog_count();
    8584        $wp_install        = network_site_url();
    8685        $multisite_enabled = 1;
    8786    } else {
    88         $user_count        = count_users();
    89         $user_count        = $user_count['total_users'];
    9087        $multisite_enabled = 0;
    9188        $num_blogs         = 1;
     
    10097        'local_package'      => isset( $wp_local_package ) ? $wp_local_package : '',
    10198        'blogs'              => $num_blogs,
    102         'users'              => $user_count,
     99        'users'              => get_user_count(),
    103100        'multisite_enabled'  => $multisite_enabled,
    104101        'initial_db_version' => get_site_option( 'initial_db_version' ),
  • trunk/tests/phpunit/tests/multisite/network.php

    r52010 r53011  
    206206        }
    207207
    208         /**
    209          * @ticket 37866
    210          */
    211         public function test_get_user_count_on_different_network() {
    212             wp_update_network_user_counts();
    213             $current_network_user_count = get_user_count();
    214 
    215             // Add another user to fake the network user count to be different.
    216             wpmu_create_user( 'user', 'pass', 'email' );
    217 
    218             wp_update_network_user_counts( self::$different_network_id );
    219 
    220             $user_count = get_user_count( self::$different_network_id );
    221 
    222             $this->assertEquals( $current_network_user_count + 1, $user_count );
    223         }
    224 
    225         /**
    226          * @ticket 22917
    227          */
    228         public function test_enable_live_network_user_counts_filter() {
    229             // False for large networks by default.
    230             add_filter( 'enable_live_network_counts', '__return_false' );
    231 
    232             // Refresh the cache.
    233             wp_update_network_counts();
    234             $start_count = get_user_count();
    235 
    236             wpmu_create_user( 'user', 'pass', 'email' );
    237 
    238             // No change, cache not refreshed.
    239             $count = get_user_count();
    240 
    241             $this->assertSame( $start_count, $count );
    242 
    243             wp_update_network_counts();
    244             $start_count = get_user_count();
    245 
    246             add_filter( 'enable_live_network_counts', '__return_true' );
    247 
    248             wpmu_create_user( 'user2', 'pass2', 'email2' );
    249 
    250             $count = get_user_count();
    251             $this->assertEquals( $start_count + 1, $count );
    252 
    253             remove_filter( 'enable_live_network_counts', '__return_false' );
    254             remove_filter( 'enable_live_network_counts', '__return_true' );
    255         }
     208
    256209
    257210        public function test_active_network_plugins() {
     
    318271        public function helper_deactivate_hook() {
    319272            $this->plugin_hook_count++;
    320         }
    321 
    322         public function test_get_user_count() {
    323             // Refresh the cache.
    324             wp_update_network_counts();
    325             $start_count = get_user_count();
    326 
    327             // Only false for large networks as of 3.7.
    328             add_filter( 'enable_live_network_counts', '__return_false' );
    329             self::factory()->user->create( array( 'role' => 'administrator' ) );
    330 
    331             $count = get_user_count(); // No change, cache not refreshed.
    332             $this->assertSame( $start_count, $count );
    333 
    334             wp_update_network_counts(); // Magic happens here.
    335 
    336             $count = get_user_count();
    337             $this->assertEquals( $start_count + 1, $count );
    338             remove_filter( 'enable_live_network_counts', '__return_false' );
    339273        }
    340274
     
    408342            update_network_option( null, 'user_count', 40 );
    409343
    410             $expected = $wpdb->get_var( "SELECT COUNT(ID) as c FROM $wpdb->users WHERE spam = '0' AND deleted = '0'" );
     344            $expected = (int) $wpdb->get_var( "SELECT COUNT(ID) as c FROM $wpdb->users WHERE spam = '0' AND deleted = '0'" );
    411345
    412346            wp_update_network_user_counts();
     
    424358            update_network_option( self::$different_network_id, 'user_count', 40 );
    425359
    426             $expected = $wpdb->get_var( "SELECT COUNT(ID) as c FROM $wpdb->users WHERE spam = '0' AND deleted = '0'" );
     360            $expected = (int) $wpdb->get_var( "SELECT COUNT(ID) as c FROM $wpdb->users WHERE spam = '0' AND deleted = '0'" );
    427361
    428362            wp_update_network_user_counts( self::$different_network_id );
Note: See TracChangeset for help on using the changeset viewer.