Make WordPress Core

Changeset 59817


Ignore:
Timestamp:
02/12/2025 09:48:04 PM (4 weeks ago)
Author:
spacedmonkey
Message:

Users: Add caching to count_user_posts function

Introduced caching for the count_user_posts function to reduce redundant database queries. This ensures better performance by storing and reusing query results when possible. Additionally, sanitized and sorted the $post_type array to avoid invalid queries.

Props spacedmonkey, peterwilsoncc, mamaduka, flixos90, johnjamesjacoby, swissspidy, dilip2615, johnregan3, wpgurudev, desrosj, milindmore22, Krstarica, dilipom13.
Fixes #39242.

Location:
trunk
Files:
2 edited

Legend:

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

    r59754 r59817  
    605605    global $wpdb;
    606606
     607    $post_type = array_unique( (array) $post_type );
     608    sort( $post_type );
     609
    607610    $where = get_posts_by_author_sql( $post_type, true, $userid, $public_only );
    608 
    609     $count = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->posts $where" );
     611    $query = "SELECT COUNT(*) FROM $wpdb->posts $where";
     612
     613    $last_changed = wp_cache_get_last_changed( 'posts' );
     614    $cache_key    = 'count_user_posts:' . md5( $query ) . ':' . $last_changed;
     615    $count        = wp_cache_get( $cache_key, 'post-queries' );
     616    if ( false === $count ) {
     617        $count = $wpdb->get_var( $query );
     618        wp_cache_set( $cache_key, $count, 'post-queries' );
     619    }
    610620
    611621    /**
  • trunk/tests/phpunit/tests/user/countUserPosts.php

    r54402 r59817  
    9090        $this->assertSame( '4', count_user_posts( self::$user_id, array( 'foo', 'post' ) ) );
    9191    }
     92
     93    /**
     94     * Post count be correct after reassigning posts to another user.
     95     *
     96     * @ticket 39242
     97     */
     98    public function test_reassigning_users_posts_modifies_count() {
     99        // Create new user.
     100        $new_user_id = self::factory()->user->create(
     101            array(
     102                'role' => 'author',
     103            )
     104        );
     105
     106        // Prior to reassigning posts.
     107        $this->assertSame( '4', count_user_posts( self::$user_id ), 'Original user is expected to have a count of four posts prior to reassignment.' );
     108        $this->assertSame( '0', count_user_posts( $new_user_id ), 'New user is expected to have a count of zero posts prior to reassignment.' );
     109
     110        // Delete the original user, reassigning their posts to the new user.
     111        wp_delete_user( self::$user_id, $new_user_id );
     112
     113        // After reassigning posts.
     114        $this->assertSame( '0', count_user_posts( self::$user_id ), 'Original user is expected to have a count of zero posts following reassignment.' );
     115        $this->assertSame( '4', count_user_posts( $new_user_id ), 'New user is expected to have a count of four posts following reassignment.' );
     116    }
     117
     118    /**
     119     * Post count be correct after deleting user without reassigning posts.
     120     *
     121     * @ticket 39242
     122     */
     123    public function test_post_count_retained_after_deleting_user_without_reassigning_posts() {
     124        $this->assertSame( '4', count_user_posts( self::$user_id ), 'User is expected to have a count of four posts prior to deletion.' );
     125
     126        // Delete the original user without reassigning their posts.
     127        wp_delete_user( self::$user_id );
     128
     129        $this->assertSame( '0', count_user_posts( self::$user_id ), 'User is expected to have a count of zero posts following deletion.' );
     130    }
     131
     132    /**
     133     * Post count should work for users that don't exist but have posts assigned.
     134     *
     135     * @ticket 39242
     136     */
     137    public function test_count_user_posts_for_non_existent_user() {
     138        $next_user_id = self::$user_id + 1;
     139
     140        // Assign post to next user.
     141        self::factory()->post->create(
     142            array(
     143                'post_author' => $next_user_id,
     144                'post_type'   => 'post',
     145            )
     146        );
     147
     148        $next_user_post_count = count_user_posts( $next_user_id );
     149        $this->assertSame( '1', $next_user_post_count, 'Non-existent user is expected to have count of one post.' );
     150    }
     151
     152    /**
     153     * Cached user count value should be accurate after user is created.
     154     *
     155     * @ticket 39242
     156     */
     157    public function test_count_user_posts_for_user_created_after_being_assigned_posts() {
     158        global $wpdb;
     159        $next_user_id = (int) $wpdb->get_var( "SELECT `auto_increment` FROM INFORMATION_SCHEMA.TABLES WHERE table_name = '$wpdb->users'" );
     160
     161        // Assign post to next user.
     162        self::factory()->post->create(
     163            array(
     164                'post_author' => $next_user_id,
     165                'post_type'   => 'post',
     166            )
     167        );
     168
     169        // Cache the user count.
     170        count_user_posts( $next_user_id );
     171
     172        // Create user.
     173        $real_next_user_id = self::factory()->user->create(
     174            array(
     175                'role' => 'author',
     176            )
     177        );
     178
     179        $this->assertSame( $next_user_id, $real_next_user_id, 'User ID should match calculated value' );
     180        $this->assertSame( '1', count_user_posts( $next_user_id ), 'User is expected to have count of one post.' );
     181    }
     182
     183    /**
     184     * User count cache should be hit regardless of post type order.
     185     *
     186     * @ticket 39242
     187     */
     188    public function test_cache_should_be_hit_regardless_of_post_type_order() {
     189        // Prime Cache.
     190        count_user_posts( self::$user_id, array( 'wptests_pt', 'post' ) );
     191
     192        $query_num_start = get_num_queries();
     193        count_user_posts( self::$user_id, array( 'post', 'wptests_pt' ) );
     194        $total_queries = get_num_queries() - $query_num_start;
     195
     196        $this->assertSame( 0, $total_queries, 'Cache should be hit regardless of post type order.' );
     197    }
     198
     199    /**
     200     * User count cache should be hit for string and array of post types.
     201     *
     202     * @ticket 39242
     203     */
     204    public function test_cache_should_be_hit_for_string_and_array_equivalent_queries() {
     205        // Prime Cache.
     206        count_user_posts( self::$user_id, 'post' );
     207
     208        $query_num_start = get_num_queries();
     209        count_user_posts( self::$user_id, array( 'post' ) );
     210        $total_queries = get_num_queries() - $query_num_start;
     211
     212        $this->assertSame( 0, $total_queries, 'Cache should be hit for string and array equivalent post types.' );
     213    }
     214
     215    /**
     216     * User count cache should be hit for array duplicates and equivalent queries.
     217     *
     218     * @ticket 39242
     219    */
     220    public function test_cache_should_be_hit_for_and_array_duplicates_equivalent_queries() {
     221        // Prime Cache
     222        count_user_posts( self::$user_id, array( 'post', 'post', 'post' ) );
     223
     224        $query_num_start = get_num_queries();
     225        count_user_posts( self::$user_id, array( 'post' ) );
     226        $total_queries = get_num_queries() - $query_num_start;
     227
     228        $this->assertSame( 0, $total_queries, 'Cache is expected to be hit for equivalent queries with duplicate post types' );
     229    }
    92230}
Note: See TracChangeset for help on using the changeset viewer.