Commit 5ff846ea12 for wordpress.org
commit 5ff846ea12a94819a98f367abc022dd7e598854c
Author: westonruter <westonruter@git.wordpress.org>
Date: Tue Oct 20 05:50:52 2026 +0000
Posts, Post Types: Add `pre_wp_count_attachments` filter.
Introduces the `pre_wp_count_attachments` filter to `wp_count_attachments()`, the counterpart of `pre_wp_count_posts`. Returning a `stdClass` object short-circuits the function, skipping the object cache lookup and both of its queries. Since the `counts` cache group is non-persistent, those queries otherwise run on every request that needs the counts, and on a site with millions of attachments the `GROUP BY post_mime_type` aggregate alone can take several seconds. The filter allows the counts to be served instead from a value pre-computed in the background.
Unlike post statuses, the set of MIME types is not known ahead of time, so only a missing trash count is filled in with zero. The filtered value should therefore contain a count for every MIME type matching `$mime_type`, and only those types. As with `pre_wp_count_posts`, the value is not cached and is passed through the `wp_count_attachments` filter.
The `pre_wp_count_posts` filter introduced in r64245 is brought in line with the new one. A filtered value now short-circuits only when it is a `stdClass` object: any other non-null value was previously cast to an object, turning `false` into a nonsensical result, and filling in missing counts on another class can trigger a dynamic property deprecation. Both filters also clone the returned object, so that a callback which keeps it, such as in a static variable, does not have it modified by core or by later callbacks on the `wp_count_posts` and `wp_count_attachments` filters.
Developed in https://github.com/WordPress/wordpress-develop/pull/14210.
Follow-up to r64245.
Props jdeep, westonruter, peterwilsoncc.
See #66098.
Fixes #66291.
Built from https://develop.svn.wordpress.org/trunk@64589
git-svn-id: http://core.svn.wordpress.org/trunk@63739 1a063a9b-81f0-0310-95a4-ce76da25c4cd
diff --git a/wp-includes/post.php b/wp-includes/post.php
index de0ff1d0c8..78fa26a1b0 100644
--- a/wp-includes/post.php
+++ b/wp-includes/post.php
@@ -3544,12 +3544,12 @@ function wp_count_posts( $type = 'post', $perm = '' ) {
/**
* Filters the post counts before the query is run.
*
- * Returning a non-null value short-circuits wp_count_posts(), skipping both
- * the object cache lookup and the database query. This allows the counts to
- * be served from another source, such as a value pre-computed in the
- * background, when an exact real-time count is not required.
+ * Returning a stdClass object short-circuits wp_count_posts(), skipping
+ * both the object cache lookup and the database query. This allows the
+ * counts to be served from another source, such as a value pre-computed in
+ * the background, when an exact real-time count is not required.
*
- * Any registered post status missing from the filtered value are added
+ * Any registered post status missing from the filtered value is added
* to the result and set to zero.
*
* The returned value is not cached and passed through the
@@ -3565,8 +3565,8 @@ function wp_count_posts( $type = 'post', $perm = '' ) {
*/
$counts = apply_filters( 'pre_wp_count_posts', null, $type, $perm );
- if ( null !== $counts ) {
- $counts = (object) $counts;
+ if ( $counts instanceof stdClass ) {
+ $counts = clone $counts;
foreach ( get_post_stati() as $status ) {
if ( ! isset( $counts->{$status} ) ) {
@@ -3669,6 +3669,47 @@ function wp_count_posts( $type = 'post', $perm = '' ) {
function wp_count_attachments( $mime_type = '' ) {
global $wpdb;
+ /**
+ * Filters the attachment counts before the query is run.
+ *
+ * Returning a stdClass object short-circuits wp_count_attachments(),
+ * skipping both the object cache lookup and the database queries. This
+ * allows the counts to be served from another source, such as a value
+ * pre-computed in the background, when an exact real-time count is not
+ * required.
+ *
+ * If the filtered value does not contain a trash count, one is added to the
+ * result and set to zero. Missing MIME types are not added, as they depend on
+ * the attachments that exist. The filtered value should therefore contain a
+ * count for every MIME type matching `$mime_type` that has attachments,
+ * including types first uploaded after the value was computed, as callers
+ * may read the count for any MIME type that exists. It should also contain
+ * only MIME types matching `$mime_type`, with a trash count limited to
+ * attachments of those types, as the default query does.
+ *
+ * The returned value is not cached and passed through the
+ * {@see 'wp_count_attachments'} filter.
+ *
+ * @since 7.2.0
+ *
+ * @param stdClass|null $counts An object containing the attachment counts by
+ * mime type, or null to run the default query.
+ * Default null.
+ * @param string|string[] $mime_type Array or comma-separated list of MIME patterns.
+ */
+ $counts = apply_filters( 'pre_wp_count_attachments', null, $mime_type );
+
+ if ( $counts instanceof stdClass ) {
+ $counts = clone $counts;
+
+ if ( ! isset( $counts->trash ) ) {
+ $counts->trash = 0;
+ }
+
+ /** This filter is documented in wp-includes/post.php */
+ return apply_filters( 'wp_count_attachments', $counts, $mime_type );
+ }
+
$cache_key = sprintf(
'attachments%s',
! empty( $mime_type ) ? ':' . str_replace( '/', '_', implode( '-', (array) $mime_type ) ) : ''
diff --git a/wp-includes/version.php b/wp-includes/version.php
index 4ee11ee863..2a6ea03638 100644
--- a/wp-includes/version.php
+++ b/wp-includes/version.php
@@ -16,7 +16,7 @@
*
* @global string $wp_version
*/
-$wp_version = '7.2-alpha-64588';
+$wp_version = '7.2-alpha-64589';
/**
* Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.