From e7d9b93d2b6c076be5e81e397cafbaa92cadfbd6 Mon Sep 17 00:00:00 2001 From: Christoph Daum Date: Mon, 14 Apr 2025 18:31:55 +0200 Subject: [PATCH 01/14] refactor(media): add transient caching for media library months query Extract the months query from wp_enqueue_media() into get_media_library_months_with_files() and cache the result in a transient. The transient is invalidated in clean_post_cache() when an attachment is modified. --- src/wp-includes/media.php | 46 ++++++++++++++++++++++++++++----------- src/wp-includes/post.php | 4 ++++ 2 files changed, 37 insertions(+), 13 deletions(-) diff --git a/src/wp-includes/media.php b/src/wp-includes/media.php index efb82399ed688..27411b068a12e 100644 --- a/src/wp-includes/media.php +++ b/src/wp-includes/media.php @@ -4869,10 +4869,11 @@ function wp_enqueue_media( $args = array() ) { /** * Allows overriding the list of months displayed in the media library. * - * By default (if this filter does not return an array), a query will be - * run to determine the months that have media items. This query can be - * expensive for large media libraries, so it may be desirable for sites to - * override this behavior. + * By default, if this filter does not return an array, + * `get_media_library_months_with_files()` will run a query to determine + * the months that have media items. The result is stored in a transient + * and automatically invalidated when attachments are created, updated, + * or deleted. * * @since 4.7.4 * @@ -4883,16 +4884,13 @@ function wp_enqueue_media( $args = array() ) { */ $months = apply_filters( 'media_library_months_with_files', null ); if ( ! is_array( $months ) ) { - $months = $wpdb->get_results( - $wpdb->prepare( - "SELECT DISTINCT YEAR( post_date ) AS year, MONTH( post_date ) AS month - FROM $wpdb->posts - WHERE post_type = %s - ORDER BY post_date DESC", - 'attachment' - ) - ); + $months = get_transient( 'media_library_months_with_files' ); + if ( false === $months ) { + $months = get_media_library_months_with_files(); + set_transient( 'media_library_months_with_files', $months ); + } } + foreach ( $months as $month_year ) { $month_year->text = sprintf( /* translators: 1: Month, 2: Year. */ @@ -5152,6 +5150,28 @@ function wp_enqueue_media( $args = array() ) { do_action( 'wp_enqueue_media' ); } +/** + * Retrieves the months that have media items. + * + * @since tbd + * + * @global wpdb $wpdb WordPress database abstraction object. + * + * @return stdClass[] Array of objects with `month` and `year` properties. + */ +function get_media_library_months_with_files(): array { + global $wpdb; + return $wpdb->get_results( + $wpdb->prepare( + "SELECT DISTINCT YEAR( post_date ) AS year, MONTH( post_date ) AS month + FROM $wpdb->posts + WHERE post_type = %s + ORDER BY post_date DESC", + 'attachment' + ) + ); +} + /** * Retrieves media attached to the passed post. * diff --git a/src/wp-includes/post.php b/src/wp-includes/post.php index c9759d13634a2..cb8b0b5f4808f 100644 --- a/src/wp-includes/post.php +++ b/src/wp-includes/post.php @@ -7791,6 +7791,10 @@ function clean_post_cache( $post ) { do_action( 'clean_page_cache', $post->ID ); } + if ( 'attachment' === $post->post_type ) { + delete_transient( 'media_library_months_with_files' ); + } + wp_cache_set_posts_last_changed(); } From 409acfe7362507ff1179dc21fefb6b6ba5533f0b Mon Sep 17 00:00:00 2001 From: Dennis Snell Date: Mon, 23 Feb 2026 12:47:19 -0600 Subject: [PATCH 02/14] Hyperlink the docblock to get_media_library_months_with_files(). --- src/wp-includes/media.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/media.php b/src/wp-includes/media.php index 27411b068a12e..9b499897ef43b 100644 --- a/src/wp-includes/media.php +++ b/src/wp-includes/media.php @@ -4870,7 +4870,7 @@ function wp_enqueue_media( $args = array() ) { * Allows overriding the list of months displayed in the media library. * * By default, if this filter does not return an array, - * `get_media_library_months_with_files()` will run a query to determine + * {@see get_media_library_months_with_files()} will run a query to determine * the months that have media items. The result is stored in a transient * and automatically invalidated when attachments are created, updated, * or deleted. From 52a87d67a8b792371fdbc2102bef752134d14f9b Mon Sep 17 00:00:00 2001 From: Christoph Daum Date: Mon, 23 Feb 2026 20:19:03 +0100 Subject: [PATCH 03/14] Prefix get_media_library_months_with_files() with wp_ --- src/wp-includes/media.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/wp-includes/media.php b/src/wp-includes/media.php index 9b499897ef43b..5c7e2e06d5ed5 100644 --- a/src/wp-includes/media.php +++ b/src/wp-includes/media.php @@ -4870,7 +4870,7 @@ function wp_enqueue_media( $args = array() ) { * Allows overriding the list of months displayed in the media library. * * By default, if this filter does not return an array, - * {@see get_media_library_months_with_files()} will run a query to determine + * {@see wp_get_media_library_months_with_files()} will run a query to determine * the months that have media items. The result is stored in a transient * and automatically invalidated when attachments are created, updated, * or deleted. @@ -4886,7 +4886,7 @@ function wp_enqueue_media( $args = array() ) { if ( ! is_array( $months ) ) { $months = get_transient( 'media_library_months_with_files' ); if ( false === $months ) { - $months = get_media_library_months_with_files(); + $months = wp_get_media_library_months_with_files(); set_transient( 'media_library_months_with_files', $months ); } } @@ -5159,7 +5159,7 @@ function wp_enqueue_media( $args = array() ) { * * @return stdClass[] Array of objects with `month` and `year` properties. */ -function get_media_library_months_with_files(): array { +function wp_get_media_library_months_with_files(): array { global $wpdb; return $wpdb->get_results( $wpdb->prepare( From ce3c55a5790ec7977cf702e3f18e1a25f1ccdb64 Mon Sep 17 00:00:00 2001 From: Christoph Daum Date: Mon, 23 Feb 2026 20:29:07 +0100 Subject: [PATCH 04/14] Docs: Use object shape notation for return type Apply @westonruter's suggestion to use object shape notation for wp_get_media_library_months_with_files() return type, with string types matching the actual wpdb query output. --- src/wp-includes/media.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/media.php b/src/wp-includes/media.php index 5c7e2e06d5ed5..22b270f3323e4 100644 --- a/src/wp-includes/media.php +++ b/src/wp-includes/media.php @@ -5157,7 +5157,7 @@ function wp_enqueue_media( $args = array() ) { * * @global wpdb $wpdb WordPress database abstraction object. * - * @return stdClass[] Array of objects with `month` and `year` properties. + * @return array Array of objects with `month` and `year` properties. */ function wp_get_media_library_months_with_files(): array { global $wpdb; From 6f8c4e30a76a01c588acde4b40dd35a17c6f9472 Mon Sep 17 00:00:00 2001 From: Christoph Daum Date: Tue, 24 Feb 2026 19:34:10 +0100 Subject: [PATCH 05/14] Rename function and improve docblock Rename wp_get_media_library_months_with_files() to wp_get_media_library_attachment_months() for clarity and improve the docblock with a usage example and a return description focused on purpose rather than shape. --- src/wp-includes/media.php | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/wp-includes/media.php b/src/wp-includes/media.php index 22b270f3323e4..0360c5c71d01d 100644 --- a/src/wp-includes/media.php +++ b/src/wp-includes/media.php @@ -4870,7 +4870,7 @@ function wp_enqueue_media( $args = array() ) { * Allows overriding the list of months displayed in the media library. * * By default, if this filter does not return an array, - * {@see wp_get_media_library_months_with_files()} will run a query to determine + * {@see wp_get_media_library_attachment_months()} will run a query to determine * the months that have media items. The result is stored in a transient * and automatically invalidated when attachments are created, updated, * or deleted. @@ -4886,7 +4886,7 @@ function wp_enqueue_media( $args = array() ) { if ( ! is_array( $months ) ) { $months = get_transient( 'media_library_months_with_files' ); if ( false === $months ) { - $months = wp_get_media_library_months_with_files(); + $months = wp_get_media_library_attachment_months(); set_transient( 'media_library_months_with_files', $months ); } } @@ -5151,15 +5151,24 @@ function wp_enqueue_media( $args = array() ) { } /** - * Retrieves the months that have media items. + * Retrieves the months that have media library attachments. + * + * Example: + * + * $months = wp_get_media_library_attachment_months(); + * // Returns e.g.: + * // array( + * // (object) array( 'year' => '2025', 'month' => '2' ), + * // (object) array( 'year' => '2024', 'month' => '11' ), + * // ) * * @since tbd * * @global wpdb $wpdb WordPress database abstraction object. * - * @return array Array of objects with `month` and `year` properties. + * @return array Months with associated attachment post dates. */ -function wp_get_media_library_months_with_files(): array { +function wp_get_media_library_attachment_months(): array { global $wpdb; return $wpdb->get_results( $wpdb->prepare( From 959d814726403d1b1d94321f05fab28518828e4d Mon Sep 17 00:00:00 2001 From: Christoph Daum Date: Tue, 24 Feb 2026 23:31:54 +0100 Subject: [PATCH 06/14] Rename transient and fix docblock example style Rename transient from media_library_months_with_files to wp_media_library_attachment_months to align with the function name. Update the docblock example to use the assertion style matching other core examples. --- src/wp-includes/media.php | 13 ++++++------- src/wp-includes/post.php | 2 +- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/wp-includes/media.php b/src/wp-includes/media.php index 0360c5c71d01d..a177f67fdbaa8 100644 --- a/src/wp-includes/media.php +++ b/src/wp-includes/media.php @@ -4884,10 +4884,10 @@ function wp_enqueue_media( $args = array() ) { */ $months = apply_filters( 'media_library_months_with_files', null ); if ( ! is_array( $months ) ) { - $months = get_transient( 'media_library_months_with_files' ); + $months = get_transient( 'wp_media_library_attachment_months' ); if ( false === $months ) { $months = wp_get_media_library_attachment_months(); - set_transient( 'media_library_months_with_files', $months ); + set_transient( 'wp_media_library_attachment_months', $months ); } } @@ -5156,11 +5156,10 @@ function wp_enqueue_media( $args = array() ) { * Example: * * $months = wp_get_media_library_attachment_months(); - * // Returns e.g.: - * // array( - * // (object) array( 'year' => '2025', 'month' => '2' ), - * // (object) array( 'year' => '2024', 'month' => '11' ), - * // ) + * $months === array( + * (object) array( 'year' => '2025', 'month' => '2' ), + * (object) array( 'year' => '2024', 'month' => '11' ), + * ); * * @since tbd * diff --git a/src/wp-includes/post.php b/src/wp-includes/post.php index cb8b0b5f4808f..2c3aa0380ecc9 100644 --- a/src/wp-includes/post.php +++ b/src/wp-includes/post.php @@ -7792,7 +7792,7 @@ function clean_post_cache( $post ) { } if ( 'attachment' === $post->post_type ) { - delete_transient( 'media_library_months_with_files' ); + delete_transient( 'wp_media_library_attachment_months' ); } wp_cache_set_posts_last_changed(); From 52b5ed300ab9de3c2c7f5edf15f03beef0b34277 Mon Sep 17 00:00:00 2001 From: Christoph Daum Date: Tue, 24 Feb 2026 23:45:29 +0100 Subject: [PATCH 07/14] Cast year and month to int before returning Ensures wp_get_media_library_attachment_months() returns integer values instead of strings from the database query, producing cleaner typed output. --- src/wp-includes/media.php | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/wp-includes/media.php b/src/wp-includes/media.php index a177f67fdbaa8..8320a5f90c4bd 100644 --- a/src/wp-includes/media.php +++ b/src/wp-includes/media.php @@ -5157,19 +5157,20 @@ function wp_enqueue_media( $args = array() ) { * * $months = wp_get_media_library_attachment_months(); * $months === array( - * (object) array( 'year' => '2025', 'month' => '2' ), - * (object) array( 'year' => '2024', 'month' => '11' ), + * (object) array( 'year' => 2025, 'month' => 2 ), + * (object) array( 'year' => 2024, 'month' => 11 ), * ); * * @since tbd * * @global wpdb $wpdb WordPress database abstraction object. * - * @return array Months with associated attachment post dates. + * @return array Months with associated attachment post dates. */ function wp_get_media_library_attachment_months(): array { global $wpdb; - return $wpdb->get_results( + + $results = $wpdb->get_results( $wpdb->prepare( "SELECT DISTINCT YEAR( post_date ) AS year, MONTH( post_date ) AS month FROM $wpdb->posts @@ -5178,6 +5179,13 @@ function wp_get_media_library_attachment_months(): array { 'attachment' ) ); + + foreach ( $results as $row ) { + $row->year = (int) $row->year; + $row->month = (int) $row->month; + } + + return $results; } /** From 7160b13fdba378c373740151dfe7eafab0c8c1e3 Mon Sep 17 00:00:00 2001 From: Christoph Daum Date: Wed, 25 Feb 2026 15:23:34 +0100 Subject: [PATCH 08/14] Encapsulate filter and transient inside wp_get_media_library_attachment_months() Move the media_library_months_with_files filter and transient cache logic from wp_media_view_settings() into the function itself, so both call sites (grid view and legacy list view) benefit from caching and filter support. Replace the raw SQL query in media_upload_library_form() with a function call. --- src/wp-admin/includes/media.php | 7 +--- src/wp-includes/media.php | 60 ++++++++++++++++++--------------- 2 files changed, 34 insertions(+), 33 deletions(-) diff --git a/src/wp-admin/includes/media.php b/src/wp-admin/includes/media.php index 1d45224f5b7e4..e64d6c2cf1a51 100644 --- a/src/wp-admin/includes/media.php +++ b/src/wp-admin/includes/media.php @@ -2852,12 +2852,7 @@ function media_upload_library_form( $errors ) {
get_results( - "SELECT DISTINCT YEAR( post_date ) AS year, MONTH( post_date ) AS month - FROM $wpdb->posts - WHERE post_type = 'attachment' - ORDER BY post_date DESC" - ); + $months = wp_get_media_library_attachment_months(); $month_count = count( $months ); $selected_month = isset( $_GET['m'] ) ? (int) $_GET['m'] : 0; diff --git a/src/wp-includes/media.php b/src/wp-includes/media.php index 8320a5f90c4bd..f411421bcf54a 100644 --- a/src/wp-includes/media.php +++ b/src/wp-includes/media.php @@ -4866,30 +4866,7 @@ function wp_enqueue_media( $args = array() ) { ); } - /** - * Allows overriding the list of months displayed in the media library. - * - * By default, if this filter does not return an array, - * {@see wp_get_media_library_attachment_months()} will run a query to determine - * the months that have media items. The result is stored in a transient - * and automatically invalidated when attachments are created, updated, - * or deleted. - * - * @since 4.7.4 - * - * @link https://core.trac.wordpress.org/ticket/31071 - * - * @param stdClass[]|null $months An array of objects with `month` and `year` - * properties, or `null` for default behavior. - */ - $months = apply_filters( 'media_library_months_with_files', null ); - if ( ! is_array( $months ) ) { - $months = get_transient( 'wp_media_library_attachment_months' ); - if ( false === $months ) { - $months = wp_get_media_library_attachment_months(); - set_transient( 'wp_media_library_attachment_months', $months ); - } - } + $months = wp_get_media_library_attachment_months(); foreach ( $months as $month_year ) { $month_year->text = sprintf( @@ -5153,6 +5130,9 @@ function wp_enqueue_media( $args = array() ) { /** * Retrieves the months that have media library attachments. * + * Results are cached in a transient and automatically invalidated when + * attachments are created, updated, or deleted. + * * Example: * * $months = wp_get_media_library_attachment_months(); @@ -5170,7 +5150,31 @@ function wp_enqueue_media( $args = array() ) { function wp_get_media_library_attachment_months(): array { global $wpdb; - $results = $wpdb->get_results( + /** + * Allows overriding the list of months displayed in the media library. + * + * Returning an array from this filter will bypass both the transient cache + * and the database query. + * + * @since 4.7.4 + * @since tbd Moved to {@see wp_get_media_library_attachment_months()}. + * + * @link https://core.trac.wordpress.org/ticket/31071 + * + * @param stdClass[]|null $months An array of objects with `month` and `year` + * properties, or `null` for default behavior. + */ + $months = apply_filters( 'media_library_months_with_files', null ); + if ( is_array( $months ) ) { + return $months; + } + + $months = get_transient( 'wp_media_library_attachment_months' ); + if ( false !== $months ) { + return $months; + } + + $months = $wpdb->get_results( $wpdb->prepare( "SELECT DISTINCT YEAR( post_date ) AS year, MONTH( post_date ) AS month FROM $wpdb->posts @@ -5180,12 +5184,14 @@ function wp_get_media_library_attachment_months(): array { ) ); - foreach ( $results as $row ) { + foreach ( $months as $row ) { $row->year = (int) $row->year; $row->month = (int) $row->month; } - return $results; + set_transient( 'wp_media_library_attachment_months', $months, WEEK_IN_SECONDS ); + + return $months; } /** From a7042ee4f29af2a9044e0f6bb8eec47cbe9ef828 Mon Sep 17 00:00:00 2001 From: Christoph Daum Date: Wed, 25 Feb 2026 15:30:10 +0100 Subject: [PATCH 09/14] Docs: Reorder param properties to match return type --- src/wp-includes/media.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/media.php b/src/wp-includes/media.php index f411421bcf54a..e1a5eec932b75 100644 --- a/src/wp-includes/media.php +++ b/src/wp-includes/media.php @@ -5161,7 +5161,7 @@ function wp_get_media_library_attachment_months(): array { * * @link https://core.trac.wordpress.org/ticket/31071 * - * @param stdClass[]|null $months An array of objects with `month` and `year` + * @param stdClass[]|null $months An array of objects with `year` and `month` * properties, or `null` for default behavior. */ $months = apply_filters( 'media_library_months_with_files', null ); From ad1cec4228254bd6c3bc0a53bcc09787b1226b50 Mon Sep 17 00:00:00 2001 From: Christoph Daum Date: Wed, 25 Feb 2026 15:57:09 +0100 Subject: [PATCH 10/14] test(media): Add tests for wp_get_media_library_attachment_months() --- .../wpGetMediaLibraryAttachmentMonths.php | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 tests/phpunit/tests/media/wpGetMediaLibraryAttachmentMonths.php diff --git a/tests/phpunit/tests/media/wpGetMediaLibraryAttachmentMonths.php b/tests/phpunit/tests/media/wpGetMediaLibraryAttachmentMonths.php new file mode 100644 index 0000000000000..362859283c5a5 --- /dev/null +++ b/tests/phpunit/tests/media/wpGetMediaLibraryAttachmentMonths.php @@ -0,0 +1,95 @@ +post->create( + array( + 'post_type' => 'attachment', + 'post_date' => '2025-03-15 00:00:00', + ) + ); + self::factory()->post->create( + array( + 'post_type' => 'attachment', + 'post_date' => '2024-11-01 00:00:00', + ) + ); + + delete_transient( 'wp_media_library_attachment_months' ); + + $months = wp_get_media_library_attachment_months(); + + $this->assertCount( 2, $months ); + $this->assertSame( 2025, $months[0]->year ); + $this->assertSame( 3, $months[0]->month ); + $this->assertSame( 2024, $months[1]->year ); + $this->assertSame( 11, $months[1]->month ); + + // Verify the result was cached. + $this->assertEquals( $months, get_transient( 'wp_media_library_attachment_months' ) ); + } + + /** + * Tests that the filter bypasses transient and query. + * + * @ticket 63279 + */ + public function test_filter_overrides_result() { + self::factory()->post->create( + array( + 'post_type' => 'attachment', + 'post_date' => '2025-06-01 00:00:00', + ) + ); + + delete_transient( 'wp_media_library_attachment_months' ); + + // Confirm the function returns data without the filter. + $this->assertNotEmpty( wp_get_media_library_attachment_months() ); + + delete_transient( 'wp_media_library_attachment_months' ); + + $override = array( + (object) array( + 'year' => 2020, + 'month' => 1, + ), + ); + + add_filter( + 'media_library_months_with_files', + static function () use ( $override ) { + return $override; + } + ); + + $months = wp_get_media_library_attachment_months(); + + $this->assertSame( $override, $months ); + } + + /** + * Tests that the transient is deleted when a new attachment is created. + * + * @ticket 63279 + */ + public function test_transient_is_invalidated_on_new_attachment() { + set_transient( 'wp_media_library_attachment_months', array() ); + + self::factory()->post->create( array( 'post_type' => 'attachment' ) ); + + $this->assertFalse( get_transient( 'wp_media_library_attachment_months' ) ); + } +} From b7f1f6869926929043463df401aecca8b535ef9b Mon Sep 17 00:00:00 2001 From: Christoph Daum Date: Fri, 27 Feb 2026 21:27:10 +0100 Subject: [PATCH 11/14] refactor: Revert filter consolidation into wp_get_media_library_attachment_months() Move the media_library_months_with_files filter back to the two call sites (wp_enqueue_media and media_upload_library_form) where it originally lived. Simplify the function to just the raw query without transient caching or int casting. Remove the now-unnecessary transient invalidation from clean_post_cache(). Update tests to match. --- src/wp-admin/includes/media.php | 6 +- src/wp-includes/media.php | 61 +++++++--------- src/wp-includes/post.php | 4 -- .../wpGetMediaLibraryAttachmentMonths.php | 69 ++----------------- 4 files changed, 34 insertions(+), 106 deletions(-) diff --git a/src/wp-admin/includes/media.php b/src/wp-admin/includes/media.php index e64d6c2cf1a51..f559778fbaf04 100644 --- a/src/wp-admin/includes/media.php +++ b/src/wp-admin/includes/media.php @@ -2852,7 +2852,11 @@ function media_upload_library_form( $errors ) {
text = sprintf( @@ -5137,44 +5155,20 @@ function wp_enqueue_media( $args = array() ) { * * $months = wp_get_media_library_attachment_months(); * $months === array( - * (object) array( 'year' => 2025, 'month' => 2 ), - * (object) array( 'year' => 2024, 'month' => 11 ), + * (object) array( 'year' => '2025', 'month' => '2' ), + * (object) array( 'year' => '2024', 'month' => '11' ), * ); * * @since tbd * * @global wpdb $wpdb WordPress database abstraction object. * - * @return array Months with associated attachment post dates. + * @return array Months with associated attachment post dates. */ function wp_get_media_library_attachment_months(): array { global $wpdb; - /** - * Allows overriding the list of months displayed in the media library. - * - * Returning an array from this filter will bypass both the transient cache - * and the database query. - * - * @since 4.7.4 - * @since tbd Moved to {@see wp_get_media_library_attachment_months()}. - * - * @link https://core.trac.wordpress.org/ticket/31071 - * - * @param stdClass[]|null $months An array of objects with `year` and `month` - * properties, or `null` for default behavior. - */ - $months = apply_filters( 'media_library_months_with_files', null ); - if ( is_array( $months ) ) { - return $months; - } - - $months = get_transient( 'wp_media_library_attachment_months' ); - if ( false !== $months ) { - return $months; - } - - $months = $wpdb->get_results( + return $wpdb->get_results( $wpdb->prepare( "SELECT DISTINCT YEAR( post_date ) AS year, MONTH( post_date ) AS month FROM $wpdb->posts @@ -5183,15 +5177,6 @@ function wp_get_media_library_attachment_months(): array { 'attachment' ) ); - - foreach ( $months as $row ) { - $row->year = (int) $row->year; - $row->month = (int) $row->month; - } - - set_transient( 'wp_media_library_attachment_months', $months, WEEK_IN_SECONDS ); - - return $months; } /** diff --git a/src/wp-includes/post.php b/src/wp-includes/post.php index 2c3aa0380ecc9..c9759d13634a2 100644 --- a/src/wp-includes/post.php +++ b/src/wp-includes/post.php @@ -7791,10 +7791,6 @@ function clean_post_cache( $post ) { do_action( 'clean_page_cache', $post->ID ); } - if ( 'attachment' === $post->post_type ) { - delete_transient( 'wp_media_library_attachment_months' ); - } - wp_cache_set_posts_last_changed(); } diff --git a/tests/phpunit/tests/media/wpGetMediaLibraryAttachmentMonths.php b/tests/phpunit/tests/media/wpGetMediaLibraryAttachmentMonths.php index 362859283c5a5..c5ad4d271a09f 100644 --- a/tests/phpunit/tests/media/wpGetMediaLibraryAttachmentMonths.php +++ b/tests/phpunit/tests/media/wpGetMediaLibraryAttachmentMonths.php @@ -9,11 +9,11 @@ class Tests_Media_wpGetMediaLibraryAttachmentMonths extends WP_UnitTestCase { /** - * Tests that the function returns the correct months and caches the result. + * Tests that the function returns the correct months. * * @ticket 63279 */ - public function test_returns_months_and_caches_result() { + public function test_returns_months() { self::factory()->post->create( array( 'post_type' => 'attachment', @@ -27,69 +27,12 @@ public function test_returns_months_and_caches_result() { ) ); - delete_transient( 'wp_media_library_attachment_months' ); - $months = wp_get_media_library_attachment_months(); $this->assertCount( 2, $months ); - $this->assertSame( 2025, $months[0]->year ); - $this->assertSame( 3, $months[0]->month ); - $this->assertSame( 2024, $months[1]->year ); - $this->assertSame( 11, $months[1]->month ); - - // Verify the result was cached. - $this->assertEquals( $months, get_transient( 'wp_media_library_attachment_months' ) ); - } - - /** - * Tests that the filter bypasses transient and query. - * - * @ticket 63279 - */ - public function test_filter_overrides_result() { - self::factory()->post->create( - array( - 'post_type' => 'attachment', - 'post_date' => '2025-06-01 00:00:00', - ) - ); - - delete_transient( 'wp_media_library_attachment_months' ); - - // Confirm the function returns data without the filter. - $this->assertNotEmpty( wp_get_media_library_attachment_months() ); - - delete_transient( 'wp_media_library_attachment_months' ); - - $override = array( - (object) array( - 'year' => 2020, - 'month' => 1, - ), - ); - - add_filter( - 'media_library_months_with_files', - static function () use ( $override ) { - return $override; - } - ); - - $months = wp_get_media_library_attachment_months(); - - $this->assertSame( $override, $months ); - } - - /** - * Tests that the transient is deleted when a new attachment is created. - * - * @ticket 63279 - */ - public function test_transient_is_invalidated_on_new_attachment() { - set_transient( 'wp_media_library_attachment_months', array() ); - - self::factory()->post->create( array( 'post_type' => 'attachment' ) ); - - $this->assertFalse( get_transient( 'wp_media_library_attachment_months' ) ); + $this->assertSame( '2025', $months[0]->year ); + $this->assertSame( '3', $months[0]->month ); + $this->assertSame( '2024', $months[1]->year ); + $this->assertSame( '11', $months[1]->month ); } } From 018ca412c9976d4b2ec6e003ed93d7887b51e859 Mon Sep 17 00:00:00 2001 From: Christoph Daum Date: Fri, 27 Feb 2026 21:50:10 +0100 Subject: [PATCH 12/14] refactor: Move filter into wp_get_media_library_attachment_months() Consolidate the media_library_months_with_files filter inside the function so both call sites just use the getter. No transient caching. Add test for filter override behavior. --- src/wp-admin/includes/media.php | 6 +-- src/wp-includes/media.php | 43 +++++++++---------- .../wpGetMediaLibraryAttachmentMonths.php | 32 ++++++++++++++ 3 files changed, 54 insertions(+), 27 deletions(-) diff --git a/src/wp-admin/includes/media.php b/src/wp-admin/includes/media.php index f559778fbaf04..e64d6c2cf1a51 100644 --- a/src/wp-admin/includes/media.php +++ b/src/wp-admin/includes/media.php @@ -2852,11 +2852,7 @@ function media_upload_library_form( $errors ) {
text = sprintf( @@ -5148,9 +5130,6 @@ function wp_enqueue_media( $args = array() ) { /** * Retrieves the months that have media library attachments. * - * Results are cached in a transient and automatically invalidated when - * attachments are created, updated, or deleted. - * * Example: * * $months = wp_get_media_library_attachment_months(); @@ -5168,6 +5147,26 @@ function wp_enqueue_media( $args = array() ) { function wp_get_media_library_attachment_months(): array { global $wpdb; + /** + * Allows overriding the list of months displayed in the media library. + * + * By default (if this filter does not return an array), a query will be + * run to determine the months that have media items. This query can be + * expensive for large media libraries, so it may be desirable for sites to + * override this behavior. + * + * @since 4.7.4 + * + * @link https://core.trac.wordpress.org/ticket/31071 + * + * @param array|null $months An array of objects with `month` and `year` + * properties, or `null` for default behavior. + */ + $months = apply_filters( 'media_library_months_with_files', null ); + if ( is_array( $months ) ) { + return $months; + } + return $wpdb->get_results( $wpdb->prepare( "SELECT DISTINCT YEAR( post_date ) AS year, MONTH( post_date ) AS month diff --git a/tests/phpunit/tests/media/wpGetMediaLibraryAttachmentMonths.php b/tests/phpunit/tests/media/wpGetMediaLibraryAttachmentMonths.php index c5ad4d271a09f..eb870b901aecd 100644 --- a/tests/phpunit/tests/media/wpGetMediaLibraryAttachmentMonths.php +++ b/tests/phpunit/tests/media/wpGetMediaLibraryAttachmentMonths.php @@ -35,4 +35,36 @@ public function test_returns_months() { $this->assertSame( '2024', $months[1]->year ); $this->assertSame( '11', $months[1]->month ); } + + /** + * Tests that the filter overrides the query result. + * + * @ticket 63279 + */ + public function test_filter_overrides_result() { + self::factory()->post->create( + array( + 'post_type' => 'attachment', + 'post_date' => '2025-06-01 00:00:00', + ) + ); + + $override = array( + (object) array( + 'year' => '2020', + 'month' => '1', + ), + ); + + add_filter( + 'media_library_months_with_files', + static function () use ( $override ) { + return $override; + } + ); + + $months = wp_get_media_library_attachment_months(); + + $this->assertSame( $override, $months ); + } } From 95b9d625d2e126ee5ffc244a2c4e811eb28acfc3 Mon Sep 17 00:00:00 2001 From: Christoph Daum Date: Fri, 27 Feb 2026 21:53:33 +0100 Subject: [PATCH 13/14] Revert "refactor: Move filter into wp_get_media_library_attachment_months()" This reverts commit 018ca412c9976d4b2ec6e003ed93d7887b51e859. --- src/wp-admin/includes/media.php | 6 ++- src/wp-includes/media.php | 43 ++++++++++--------- .../wpGetMediaLibraryAttachmentMonths.php | 32 -------------- 3 files changed, 27 insertions(+), 54 deletions(-) diff --git a/src/wp-admin/includes/media.php b/src/wp-admin/includes/media.php index e64d6c2cf1a51..f559778fbaf04 100644 --- a/src/wp-admin/includes/media.php +++ b/src/wp-admin/includes/media.php @@ -2852,7 +2852,11 @@ function media_upload_library_form( $errors ) {
text = sprintf( @@ -5130,6 +5148,9 @@ function wp_enqueue_media( $args = array() ) { /** * Retrieves the months that have media library attachments. * + * Results are cached in a transient and automatically invalidated when + * attachments are created, updated, or deleted. + * * Example: * * $months = wp_get_media_library_attachment_months(); @@ -5147,26 +5168,6 @@ function wp_enqueue_media( $args = array() ) { function wp_get_media_library_attachment_months(): array { global $wpdb; - /** - * Allows overriding the list of months displayed in the media library. - * - * By default (if this filter does not return an array), a query will be - * run to determine the months that have media items. This query can be - * expensive for large media libraries, so it may be desirable for sites to - * override this behavior. - * - * @since 4.7.4 - * - * @link https://core.trac.wordpress.org/ticket/31071 - * - * @param array|null $months An array of objects with `month` and `year` - * properties, or `null` for default behavior. - */ - $months = apply_filters( 'media_library_months_with_files', null ); - if ( is_array( $months ) ) { - return $months; - } - return $wpdb->get_results( $wpdb->prepare( "SELECT DISTINCT YEAR( post_date ) AS year, MONTH( post_date ) AS month diff --git a/tests/phpunit/tests/media/wpGetMediaLibraryAttachmentMonths.php b/tests/phpunit/tests/media/wpGetMediaLibraryAttachmentMonths.php index eb870b901aecd..c5ad4d271a09f 100644 --- a/tests/phpunit/tests/media/wpGetMediaLibraryAttachmentMonths.php +++ b/tests/phpunit/tests/media/wpGetMediaLibraryAttachmentMonths.php @@ -35,36 +35,4 @@ public function test_returns_months() { $this->assertSame( '2024', $months[1]->year ); $this->assertSame( '11', $months[1]->month ); } - - /** - * Tests that the filter overrides the query result. - * - * @ticket 63279 - */ - public function test_filter_overrides_result() { - self::factory()->post->create( - array( - 'post_type' => 'attachment', - 'post_date' => '2025-06-01 00:00:00', - ) - ); - - $override = array( - (object) array( - 'year' => '2020', - 'month' => '1', - ), - ); - - add_filter( - 'media_library_months_with_files', - static function () use ( $override ) { - return $override; - } - ); - - $months = wp_get_media_library_attachment_months(); - - $this->assertSame( $override, $months ); - } } From 4e736b12079b2ac5d82d507b663ab81764f12e39 Mon Sep 17 00:00:00 2001 From: Christoph Daum Date: Fri, 27 Feb 2026 22:02:32 +0100 Subject: [PATCH 14/14] docs: Remove outdated caching information from wp_get_media_library_attachment_months() docblock --- src/wp-includes/media.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/wp-includes/media.php b/src/wp-includes/media.php index fc3264634ed05..a2cb91ec9c489 100644 --- a/src/wp-includes/media.php +++ b/src/wp-includes/media.php @@ -5148,9 +5148,6 @@ function wp_enqueue_media( $args = array() ) { /** * Retrieves the months that have media library attachments. * - * Results are cached in a transient and automatically invalidated when - * attachments are created, updated, or deleted. - * * Example: * * $months = wp_get_media_library_attachment_months();