Make WordPress Core


Ignore:
Timestamp:
10/15/2025 11:56:57 PM (6 months ago)
Author:
peterwilsoncc
Message:

Users: Add caching to count_many_users_posts().

Introduces object caching to the count_many_users_posts() function.

Argument equivalency is checked prior to generating the cache key to ensure that the same cache is hit regardless of array order for users and post types. For example count_many_users_posts( [ 1, 2 ] ) will hit the same cache as count_many_users_posts( [ 2, 1 ] ).

Props adamsilverstein, flixos90, kalpeshh, rollybueno, sachinrajcp123, shailu25, sirlouen, spacedmonkey, westonruter, wildworks.
Fixes #63045.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/tests/phpunit/tests/user.php

    r60634 r60941  
    574574    /**
    575575     * @ticket 21431
     576     *
     577     * @covers ::count_many_users_posts
    576578     */
    577579    public function test_count_many_users_posts() {
     
    603605        $this->assertSame( '1', $counts[ self::$author_id ] );
    604606        $this->assertSame( '1', $counts[ $user_id_b ] );
     607    }
     608
     609    /**
     610     * Ensure the second and subsequent calls to count_many_users_posts() are cached.
     611     *
     612     * @ticket 63045
     613     *
     614     * @covers ::count_many_users_posts
     615     */
     616    public function test_count_many_users_posts_is_cached() {
     617        $user_1 = self::$user_ids[0];
     618        $user_2 = self::$user_ids[1];
     619
     620        // Create posts for both users.
     621        self::factory()->post->create( array( 'post_author' => $user_1 ) );
     622        self::factory()->post->create( array( 'post_author' => $user_2 ) );
     623
     624        // Warm the cache.
     625        $count1 = count_many_users_posts( array( $user_1, $user_2 ), 'post', false );
     626
     627        // Ensure cache is hit for second call.
     628        $start_queries = get_num_queries();
     629        $count2        = count_many_users_posts( array( $user_1, $user_2 ), 'post', false );
     630        $end_queries   = get_num_queries();
     631        $this->assertSame( 0, $end_queries - $start_queries, 'No database queries expected for second call to count_many_users_posts()' );
     632        $this->assertSameSetsWithIndex( $count1, $count2, 'Expected same results from both calls to count_many_users_posts()' );
     633    }
     634
     635    /**
     636     * Ensure equivalent arguments hit the same cache in count_many_users_posts().
     637     *
     638     * @ticket 63045
     639     *
     640     * @covers ::count_many_users_posts
     641     *
     642     * @dataProvider data_count_many_users_posts_cached_for_equivalent_arguments
     643     *
     644     * @param array $first_args  First set of arguments to pass to count_many_users_posts().
     645     * @param array $second_args Second set of arguments to pass to count_many_users_posts().
     646     */
     647    public function test_count_many_users_posts_cached_for_equivalent_arguments( $first_args, $second_args ) {
     648        // Replace placeholder user IDs with real ones.
     649        $first_args[0]  = array_map(
     650            static function ( $user ) {
     651                return self::$user_ids[ $user ];
     652            },
     653            $first_args[0]
     654        );
     655        $second_args[0] = array_map(
     656            static function ( $user ) {
     657                return self::$user_ids[ $user ];
     658            },
     659            $second_args[0]
     660        );
     661
     662        // Warm the cache with the first set of arguments.
     663        $count1 = count_many_users_posts( ...$first_args );
     664
     665        // Ensure the cache is hit for the second set of equivalent arguments.
     666        $start_queries = get_num_queries();
     667        $count2        = count_many_users_posts( ...$second_args );
     668        $end_queries   = get_num_queries();
     669        $this->assertSame( 0, $end_queries - $start_queries, 'No database queries expected for second call to count_many_users_posts() with equivalent arguments' );
     670        $this->assertSameSetsWithIndex( $count1, $count2, 'Expected same results from both calls to count_many_users_posts()' );
     671    }
     672
     673    /**
     674     * Data provider for test_count_many_users_posts_cached_for_equivalent_arguments().
     675     *
     676     * @return array[] Data provider.
     677     */
     678    public function data_count_many_users_posts_cached_for_equivalent_arguments(): array {
     679        return array(
     680            'single post string vs array'  => array(
     681                array( array( 0 ), 'post' ),
     682                array( array( 0 ), array( 'post' ) ),
     683            ),
     684            'duplicate post type in array' => array(
     685                array( array( 0 ), array( 'post', 'post' ) ),
     686                array( array( 0 ), array( 'post' ) ),
     687            ),
     688            'different post type order'    => array(
     689                array( array( 0 ), array( 'post', 'page' ) ),
     690                array( array( 0 ), array( 'page', 'post' ) ),
     691            ),
     692            'duplicate user IDs in array'  => array(
     693                array( array( 0, 1, 1 ), 'post' ),
     694                array( array( 0, 1 ), 'post' ),
     695            ),
     696            'different user order'         => array(
     697                array( array( 0, 1 ), 'post' ),
     698                array( array( 1, 0 ), 'post' ),
     699            ),
     700            'integer vs string user IDs'   => array(
     701                array( array( 0, 1 ), 'post' ),
     702                array( array( '0', '1' ), 'post' ),
     703            ),
     704        );
     705    }
     706
     707    /**
     708     * Test cache invalidation for count_many_users_posts().
     709     *
     710     * @ticket 63045
     711     *
     712     * @covers ::count_many_users_posts
     713     */
     714    public function test_count_many_users_posts_cache_invalidation() {
     715        $user_1 = self::$user_ids[0];
     716        $user_2 = self::$user_ids[1];
     717
     718        // Create posts for both users.
     719        self::factory()->post->create( array( 'post_author' => $user_1 ) );
     720        self::factory()->post->create( array( 'post_author' => $user_2 ) );
     721
     722        $counts1 = count_many_users_posts( array( $user_1, $user_2 ), 'post', false );
     723        $this->assertSame(
     724            array(
     725                $user_1 => '1',
     726                $user_2 => '1',
     727            ),
     728            $counts1,
     729            'Initial call is expected to have one post for each user.'
     730        );
     731
     732        // Create another post for user 1.
     733        self::factory()->post->create( array( 'post_author' => $user_1 ) );
     734
     735        $counts2 = count_many_users_posts( array( $user_1, $user_2 ), 'post', false );
     736        $this->assertSame(
     737            array(
     738                $user_1 => '2',
     739                $user_2 => '1',
     740            ),
     741            $counts2,
     742            'Second call is expected to have two posts for user 1 and one post for user 2.'
     743        );
     744    }
     745
     746    /**
     747     * Ensure different post types use different caches in count_many_users_posts().
     748     *
     749     * @ticket 63045
     750     *
     751     * @covers ::count_many_users_posts
     752     */
     753    public function test_different_post_types_use_different_caches() {
     754        $user_id = self::$user_ids[0];
     755
     756        // Create one post and two pages for the user.
     757        self::factory()->post->create(
     758            array(
     759                'post_author' => $user_id,
     760                'post_type'   => 'post',
     761            )
     762        );
     763        self::factory()->post->create(
     764            array(
     765                'post_author' => $user_id,
     766                'post_type'   => 'page',
     767            )
     768        );
     769        self::factory()->post->create(
     770            array(
     771                'post_author' => $user_id,
     772                'post_type'   => 'page',
     773            )
     774        );
     775
     776        $start_queries = get_num_queries();
     777        $count1        = count_many_users_posts( array( $user_id ), 'post', false );
     778        $end_queries   = get_num_queries();
     779        $this->assertSame( 1, $end_queries - $start_queries, 'Expected to hit database for first call to count_many_users_posts() with post type "post".' );
     780        $this->assertSame( '1', $count1[ $user_id ], 'Expected to have one post for user with post type "post".' );
     781
     782        $start_queries = get_num_queries();
     783        $count2        = count_many_users_posts( array( $user_id ), 'page', false );
     784        $end_queries   = get_num_queries();
     785        $this->assertSame( 1, $end_queries - $start_queries, 'Expected to hit database for first call to count_many_users_posts() with post type "page".' );
     786        $this->assertSame( '2', $count2[ $user_id ], 'Expected to have two pages for user with post type "page".' );
     787    }
     788
     789    /**
     790     * Ensure different users use different caches in count_many_users_posts().
     791     *
     792     * @ticket 63045
     793     *
     794     * @covers ::count_many_users_posts
     795     */
     796    public function test_different_users_use_different_caches() {
     797        $user_1 = self::$user_ids[0];
     798        $user_2 = self::$user_ids[1];
     799
     800        // Create one post for user 1, two for user 2.
     801        self::factory()->post->create(
     802            array(
     803                'post_author' => $user_1,
     804                'post_type'   => 'post',
     805            )
     806        );
     807        self::factory()->post->create(
     808            array(
     809                'post_author' => $user_2,
     810                'post_type'   => 'post',
     811            )
     812        );
     813        self::factory()->post->create(
     814            array(
     815                'post_author' => $user_2,
     816                'post_type'   => 'post',
     817            )
     818        );
     819
     820        $start_queries = get_num_queries();
     821        $count1        = count_many_users_posts( array( $user_1 ), 'post', false );
     822        $end_queries   = get_num_queries();
     823        $this->assertSame( 1, $end_queries - $start_queries, 'Expected to hit database for first call to count_many_users_posts() with user 1.' );
     824        $this->assertSame( '1', $count1[ $user_1 ], 'Expected to have one post for user 1 with post type "post".' );
     825
     826        $start_queries = get_num_queries();
     827        $count2        = count_many_users_posts( array( $user_2 ), 'post', false );
     828        $end_queries   = get_num_queries();
     829        $this->assertSame( 1, $end_queries - $start_queries, 'Expected to hit database for first call to count_many_users_posts() with user 2.' );
     830        $this->assertSame( '2', $count2[ $user_2 ], 'Expected to have two posts for user 2 with post type "post".' );
    605831    }
    606832
Note: See TracChangeset for help on using the changeset viewer.