Make WordPress Core

Changeset 59939


Ignore:
Timestamp:
03/05/2025 10:36:38 PM (19 months ago)
Author:
peterwilsoncc
Message:

Widgets: Improve caching within get_calendar().

Improves caching of the get_calendar() function by:

  • fixing incorrect cache collisions for different initial post_type and week values, and,
  • ensuring parameter equivalents generate the same cache key, ie passing the same values in a different order.

Improves tests for the function by:

  • navigating to February 2025 in test set up to ensure the correct calendar month is displayed,
  • adding messages for tests with multiple assertions,
  • improving the tests for the calendar captions by wrapping the expected value in the HTML tag,
  • adding dedicated test for the different initial parameter,
  • ensuring caches do not collide for different parameters, and,
  • ensuring caches do collide for equivalent parameters.

Follow up to r4522, r59908, r59909, r59917 (reverted), r59918 (reverted), r59930.

Props peterwilsoncc, jorbin, audrasjb.
Fixes #34093.

Location:
trunk
Files:
2 edited

Legend:

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

    r59909 r59939  
    22932293        $args = apply_filters( 'get_calendar_args', wp_parse_args( $args, $defaults ) );
    22942294
    2295         $key   = md5( $m . $monthnum . $year );
     2295        if ( ! post_type_exists( $args['post_type'] ) ) {
     2296                $args['post_type'] = 'post';
     2297        }
     2298
     2299        $w = 0;
     2300        if ( isset( $_GET['w'] ) ) {
     2301                $w = (int) $_GET['w'];
     2302        }
     2303
     2304        /*
     2305         * Normalize the cache key.
     2306         *
     2307         * The following ensures the same cache key is used for the same parameter
     2308         * and parameter equivalents. This prevents `post_type > post, initial > true`
     2309         * from generating a different key from the same values in the reverse order.
     2310         *
     2311         * `display` is excluded from the cache key as the cache contains the same
     2312         * HTML regardless of this functions need to echo or return the output.
     2313         *
     2314         * The global values contain data generated by the URL querystring variables.
     2315         */
     2316        $cache_args = $args;
     2317        unset( $cache_args['display'] );
     2318
     2319        $cache_args['globals'] = array(
     2320                'm'        => $m,
     2321                'monthnum' => $monthnum,
     2322                'year'     => $year,
     2323                'week'     => $w,
     2324        );
     2325
     2326        wp_recursive_ksort( $cache_args );
     2327        $key   = md5( serialize( $cache_args ) );
    22962328        $cache = wp_cache_get( 'get_calendar', 'calendar' );
    22972329
     
    23132345
    23142346        $post_type = $args['post_type'];
    2315         if ( ! post_type_exists( $post_type ) ) {
    2316                 $post_type = 'post';
    2317         }
    23182347
    23192348        // Quick check. If we have no posts at all, abort!
     
    23282357        }
    23292358
    2330         if ( isset( $_GET['w'] ) ) {
    2331                 $w = (int) $_GET['w'];
    2332         }
    23332359        // week_begins = 0 stands for Sunday.
    23342360        $week_begins = (int) get_option( 'start_of_week' );
  • trunk/tests/phpunit/tests/general/getCalendar.php

    r59930 r59939  
    3131                        )
    3232                );
     33
     34                self::factory()->post->create(
     35                        array(
     36                                'post_type' => 'page',
     37                                'post_date' => '2025-02-03 12:00:00',
     38                        )
     39                );
     40        }
     41
     42        /**
     43         * Set up for each test.
     44         */
     45        public function set_up() {
     46                parent::set_up();
     47
     48                /*
     49                 * Navigate to February 2025.
     50                 *
     51                 * All posts within this test suite are published in February 2025,
     52                 * navigating to the month ensures that the correct month is displayed
     53                 * in the calendar to allow the assertions to pass.
     54                 */
     55                $this->go_to( '/?m=202502' );
    3356        }
    3457
     
    3962         */
    4063        public function test_get_calendar_display() {
    41                 $expected = '<table id="wp-calendar"';
    42                 $actual   = get_echo( 'get_calendar', array( array( 'display' => true ) ) );
    43                 $this->assertStringContainsString( $expected, $actual );
     64                $calendar_html = get_echo( 'get_calendar', array( array( 'display' => true ) ) );
     65                $this->assertStringContainsString( '<th scope="col" aria-label="Monday">M</th>', $calendar_html, 'Calendar is expected to use initials for day names' );
     66                $this->assertStringContainsString( '<table id="wp-calendar"', $calendar_html, 'Calendar is expected to contain the element table#wp-calendar' );
     67                $this->assertStringContainsString( 'Posts published on February 1, 2025', $calendar_html, 'Calendar is expected to display posts published on February 1, 2025.' );
     68                $this->assertStringContainsString( '<caption>February 2025</caption', $calendar_html, 'Calendar is expected to be captioned February 2025.' );
    4469        }
    4570
     
    5075         */
    5176        public function test_get_calendar_args_filter() {
    52                 $page_id = self::factory()->post->create(
    53                         array(
    54                                 'post_type' => 'page',
    55                                 'post_date' => '2025-02-03 12:00:00',
    56                         )
    57                 );
    58 
    5977                add_filter(
    6078                        'get_calendar_args',
     
    6785                $calendar_html = get_echo( 'get_calendar' );
    6886
    69                 remove_all_filters( 'get_calendar_args' );
     87                $this->assertStringContainsString( '<th scope="col" aria-label="Monday">M</th>', $calendar_html, 'Calendar is expected to use initials for day names' );
     88                $this->assertStringContainsString( '<table id="wp-calendar"', $calendar_html, 'Calendar is expected to contain the element table#wp-calendar' );
     89                $this->assertStringContainsString( 'Posts published on February 3, 2025', $calendar_html, 'Calendar is expected to display page published on February 3, 2025.' );
     90                $this->assertStringNotContainsString( 'Posts published on February 1, 2025', $calendar_html, 'Calendar is not expected to display posts published on February 1, 2025.' );
     91                $this->assertStringContainsString( '<caption>February 2025</caption', $calendar_html, 'Calendar is expected to be captioned February 2025.' );
     92        }
    7093
    71                 $this->assertStringContainsString( '<table id="wp-calendar"', $calendar_html );
     94        /**
     95         * Test that get_calendar() respects the args post type parameter.
     96         *
     97         * @ticket 34093
     98         */
     99        public function test_get_calendar_post_type_args() {
     100                $calendar_html = get_echo( 'get_calendar', array( array( 'post_type' => 'page' ) ) );
     101
     102                $this->assertStringContainsString( '<th scope="col" aria-label="Monday">M</th>', $calendar_html, 'Calendar is expected to use initials for day names' );
     103                $this->assertStringContainsString( '<table id="wp-calendar"', $calendar_html, 'Calendar is expected to contain the element table#wp-calendar' );
     104                $this->assertStringContainsString( 'Posts published on February 3, 2025', $calendar_html, 'Calendar is expected to display page published on February 3, 2025.' );
     105                $this->assertStringNotContainsString( 'Posts published on February 1, 2025', $calendar_html, 'Calendar is not expected to display posts published on February 1, 2025.' );
     106                $this->assertStringContainsString( '<caption>February 2025</caption', $calendar_html, 'Calendar is expected to be captioned February 2025.' );
     107        }
     108
     109        /**
     110         * Test that get_calendar() respects the args initial parameter.
     111         *
     112         * @ticket 34093
     113         */
     114        public function test_get_calendar_initial_args() {
     115                $first_calendar_html  = get_echo( 'get_calendar', array( array( 'initial' => true ) ) );
     116                $second_calendar_html = get_echo( 'get_calendar', array( array( 'initial' => false ) ) );
     117
     118                $this->assertStringContainsString( '<th scope="col" aria-label="Monday">M</th>', $first_calendar_html, 'First calendar is expected to use initials for day names' );
     119                $this->assertStringContainsString( '<th scope="col" aria-label="Monday">Mon</th>', $second_calendar_html, 'Second calendar is expected to use abbreviations for day names' );
     120        }
     121
     122        /**
     123         * Test that get_calendar() uses a different cache for different arguments.
     124         *
     125         * @ticket 34093
     126         */
     127        public function test_get_calendar_caching_accounts_for_args() {
     128                $first_calendar_html  = get_echo( 'get_calendar' );
     129                $second_calendar_html = get_echo( 'get_calendar', array( array( 'post_type' => 'page' ) ) );
     130
     131                $this->assertNotSame( $first_calendar_html, $second_calendar_html, 'Each calendar should be different' );
     132        }
     133
     134        /**
     135         * Test that get_calendar() uses the same cache for equivalent arguments.
     136         *
     137         * @ticket 34093
     138         */
     139        public function test_get_calendar_caching_accounts_for_equivalent_args() {
     140                get_echo( 'get_calendar', array( array( 'post_type' => 'page' ) ) );
     141
     142                $num_queries_start = get_num_queries();
     143                // Including an argument that is the same as the default value shouldn't miss the cache.
     144                get_echo(
     145                        'get_calendar',
     146                        array(
     147                                array(
     148                                        'post_type' => 'page',
     149                                        'initial'   => true,
     150                                ),
     151                        )
     152                );
     153
     154                // Changing the order of arguments shouldn't miss the cache.
     155                get_echo(
     156                        'get_calendar',
     157                        array(
     158                                array(
     159                                        'initial'   => true,
     160                                        'post_type' => 'page',
     161                                ),
     162                        )
     163                );
     164
     165                // Display param should be ignored for the cache.
     166                get_calendar(
     167                        array(
     168                                'post_type' => 'page',
     169                                'initial'   => true,
     170                                'display'   => false,
     171                        )
     172                );
     173                $num_queries_end = get_num_queries();
     174
     175                $this->assertSame( 0, $num_queries_end - $num_queries_start, 'Cache should be hit for subsequent equivalent calendar queries.' );
    72176        }
    73177
     
    84188                $second_calendar_html = get_calendar( false, false );
    85189
    86                 $this->assertStringContainsString( '<th scope="col" aria-label="Monday">Mon</th>', $first_calendar_html );
    87                 $this->assertStringContainsString( '<table id="wp-calendar"', $second_calendar_html );
    88                 $this->assertStringContainsString( '<th scope="col" aria-label="Monday">Mon</th>', $second_calendar_html );
     190                $this->assertStringContainsString( '<th scope="col" aria-label="Monday">Mon</th>', $first_calendar_html, 'Calendar is expected to use abbreviations for day names' );
     191                $this->assertStringContainsString( '<caption>February 2025</caption>', $first_calendar_html, 'Calendar is expected to be captioned February 2025' );
     192                $this->assertStringContainsString( '<table id="wp-calendar"', $first_calendar_html, 'Calendar is expected to contain the element table#wp-calendar' );
     193                $this->assertSame( $first_calendar_html, $second_calendar_html, 'Both calendars should be identical' );
    89194        }
    90195}
Note: See TracChangeset for help on using the changeset viewer.