diff --git a/src/wp-includes/general-template.php b/src/wp-includes/general-template.php index 527289901e..3b0875f876 100644 --- a/src/wp-includes/general-template.php +++ b/src/wp-includes/general-template.php @@ -2292,7 +2292,39 @@ function get_calendar( $args = array() ) { */ $args = apply_filters( 'get_calendar_args', wp_parse_args( $args, $defaults ) ); - $key = md5( $m . $monthnum . $year ); + if ( ! post_type_exists( $args['post_type'] ) ) { + $args['post_type'] = 'post'; + } + + $w = 0; + if ( isset( $_GET['w'] ) ) { + $w = (int) $_GET['w']; + } + + /* + * Normalize the cache key. + * + * The following ensures the same cache key is used for the same parameter + * and parameter equivalents. This prevents `post_type > post, initial > true` + * from generating a different key from the same values in the reverse order. + * + * `display` is excluded from the cache key as the cache contains the same + * HTML regardless of this functions need to echo or return the output. + * + * The global values contain data generated by the URL querystring variables. + */ + $cache_args = $args; + unset( $cache_args['display'] ); + + $cache_args['globals'] = array( + 'm' => $m, + 'monthnum' => $monthnum, + 'year' => $year, + 'week' => $w, + ); + + wp_recursive_ksort( $cache_args ); + $key = md5( serialize( $cache_args ) ); $cache = wp_cache_get( 'get_calendar', 'calendar' ); if ( $cache && is_array( $cache ) && isset( $cache[ $key ] ) ) { @@ -2312,9 +2344,6 @@ function get_calendar( $args = array() ) { } $post_type = $args['post_type']; - if ( ! post_type_exists( $post_type ) ) { - $post_type = 'post'; - } // Quick check. If we have no posts at all, abort! if ( ! $posts ) { @@ -2327,9 +2356,6 @@ function get_calendar( $args = array() ) { } } - if ( isset( $_GET['w'] ) ) { - $w = (int) $_GET['w']; - } // week_begins = 0 stands for Sunday. $week_begins = (int) get_option( 'start_of_week' ); diff --git a/tests/phpunit/tests/general/getCalendar.php b/tests/phpunit/tests/general/getCalendar.php index a2f4de4ee3..a8a3d1361f 100644 --- a/tests/phpunit/tests/general/getCalendar.php +++ b/tests/phpunit/tests/general/getCalendar.php @@ -30,6 +30,29 @@ class Tests_General_GetCalendar extends WP_UnitTestCase { 'post_date' => '2025-02-01 12:00:00', ) ); + + self::factory()->post->create( + array( + 'post_type' => 'page', + 'post_date' => '2025-02-03 12:00:00', + ) + ); + } + + /** + * Set up for each test. + */ + public function set_up() { + parent::set_up(); + + /* + * Navigate to February 2025. + * + * All posts within this test suite are published in February 2025, + * navigating to the month ensures that the correct month is displayed + * in the calendar to allow the assertions to pass. + */ + $this->go_to( '/?m=202502' ); } /** @@ -38,9 +61,11 @@ class Tests_General_GetCalendar extends WP_UnitTestCase { * @ticket 34093 */ public function test_get_calendar_display() { - $expected = ' true ) ) ); - $this->assertStringContainsString( $expected, $actual ); + $calendar_html = get_echo( 'get_calendar', array( array( 'display' => true ) ) ); + $this->assertStringContainsString( '', $calendar_html, 'Calendar is expected to use initials for day names' ); + $this->assertStringContainsString( '
M
assertStringContainsString( 'Posts published on February 1, 2025', $calendar_html, 'Calendar is expected to display posts published on February 1, 2025.' ); + $this->assertStringContainsString( '', $calendar_html, 'Calendar is expected to use initials for day names' ); + $this->assertStringContainsString( '
February 2025post->create( - array( - 'post_type' => 'page', - 'post_date' => '2025-02-03 12:00:00', - ) - ); - add_filter( 'get_calendar_args', function ( $args ) { @@ -66,9 +84,95 @@ class Tests_General_GetCalendar extends WP_UnitTestCase { $calendar_html = get_echo( 'get_calendar' ); - remove_all_filters( 'get_calendar_args' ); + $this->assertStringContainsString( '
M
assertStringContainsString( 'Posts published on February 3, 2025', $calendar_html, 'Calendar is expected to display page published on February 3, 2025.' ); + $this->assertStringNotContainsString( 'Posts published on February 1, 2025', $calendar_html, 'Calendar is not expected to display posts published on February 1, 2025.' ); + $this->assertStringContainsString( '
February 2025assertStringContainsString( ' 'page' ) ) ); + + $this->assertStringContainsString( '', $calendar_html, 'Calendar is expected to use initials for day names' ); + $this->assertStringContainsString( '
M
assertStringContainsString( 'Posts published on February 3, 2025', $calendar_html, 'Calendar is expected to display page published on February 3, 2025.' ); + $this->assertStringNotContainsString( 'Posts published on February 1, 2025', $calendar_html, 'Calendar is not expected to display posts published on February 1, 2025.' ); + $this->assertStringContainsString( '', $first_calendar_html, 'First calendar is expected to use initials for day names' ); + $this->assertStringContainsString( '', $second_calendar_html, 'Second calendar is expected to use abbreviations for day names' ); + } + + /** + * Test that get_calendar() uses a different cache for different arguments. + * + * @ticket 34093 + */ + public function test_get_calendar_caching_accounts_for_args() { + $first_calendar_html = get_echo( 'get_calendar' ); + $second_calendar_html = get_echo( 'get_calendar', array( array( 'post_type' => 'page' ) ) ); + + $this->assertNotSame( $first_calendar_html, $second_calendar_html, 'Each calendar should be different' ); + } + + /** + * Test that get_calendar() uses the same cache for equivalent arguments. + * + * @ticket 34093 + */ + public function test_get_calendar_caching_accounts_for_equivalent_args() { + get_echo( 'get_calendar', array( array( 'post_type' => 'page' ) ) ); + + $num_queries_start = get_num_queries(); + // Including an argument that is the same as the default value shouldn't miss the cache. + get_echo( + 'get_calendar', + array( + array( + 'post_type' => 'page', + 'initial' => true, + ), + ) + ); + + // Changing the order of arguments shouldn't miss the cache. + get_echo( + 'get_calendar', + array( + array( + 'initial' => true, + 'post_type' => 'page', + ), + ) + ); + + // Display param should be ignored for the cache. + get_calendar( + array( + 'post_type' => 'page', + 'initial' => true, + 'display' => false, + ) + ); + $num_queries_end = get_num_queries(); + + $this->assertSame( 0, $num_queries_end - $num_queries_start, 'Cache should be hit for subsequent equivalent calendar queries.' ); } /** @@ -83,8 +187,9 @@ class Tests_General_GetCalendar extends WP_UnitTestCase { $second_calendar_html = get_calendar( false, false ); - $this->assertStringContainsString( '', $first_calendar_html ); - $this->assertStringContainsString( '
February 2025 true ) ) ); + $second_calendar_html = get_echo( 'get_calendar', array( array( 'initial' => false ) ) ); + + $this->assertStringContainsString( '
MMonMon
assertStringContainsString( '', $second_calendar_html ); + $this->assertStringContainsString( '', $first_calendar_html, 'Calendar is expected to use abbreviations for day names' ); + $this->assertStringContainsString( '', $first_calendar_html, 'Calendar is expected to be captioned February 2025' ); + $this->assertStringContainsString( '
MonMon
February 2025
assertSame( $first_calendar_html, $second_calendar_html, 'Both calendars should be identical' ); } }