Make WordPress Core

Changeset 59939


Ignore:
Timestamp:
03/05/2025 10:36:38 PM (3 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.