Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
9c9776c
Comments: Add wp_update_comment_counts() to recalculate stored counts.
adamsilverstein Jun 25, 2026
4936b7d
Merge branch 'feature/65537-excluded-comment-types-filter' into featu…
adamsilverstein Jul 11, 2026
5f41b08
Comments: Harden wp_update_comment_counts() for production-scale use.
adamsilverstein Jul 11, 2026
0e5f9b4
Merge branch 'feature/65537-excluded-comment-types-filter' into featu…
adamsilverstein Aug 12, 2026
491ecc3
Comments: Bound each arm of the recount batch query.
adamsilverstein Aug 12, 2026
2f77f06
Comments: Stop no-op recounts from flushing every cached comment query.
adamsilverstein Aug 12, 2026
5a87337
Comments: Say what wp_update_comment_counts() actually promises.
adamsilverstein Aug 12, 2026
db3c6d8
Merge branch 'feature/65537-excluded-comment-types-filter' into featu…
adamsilverstein Nov 8, 2026
3c5feed
Merge branch 'feature/65537-excluded-comment-types-filter' into featu…
adamsilverstein Nov 8, 2026
fb8a56f
Merge branch 'feature/65537-excluded-comment-types-filter' into featu…
adamsilverstein Nov 8, 2026
0f6d94a
Comments: Skip the cache bump when a full recount finds nothing to do.
adamsilverstein Nov 8, 2026
1a80ef8
Comments: Trim the wp_update_comment_counts() inline docs.
adamsilverstein Nov 8, 2026
305e8e7
Comments: Cover interleaved union arms in the recount tests.
adamsilverstein Nov 8, 2026
dd083bd
Merge branch 'feature/65537-excluded-comment-types-filter' into featu…
adamsilverstein Nov 8, 2026
f2e4f7f
Docs: Mark the new comment type APIs as @since 7.3.0.
adamsilverstein Nov 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 110 additions & 0 deletions src/wp-includes/comment.php
Original file line number Diff line number Diff line change
Expand Up @@ -3410,6 +3410,116 @@ function wp_update_comment_count_now( $post_id ) {
return true;
}

/**
* Recalculates the stored comment count for one or more posts.
*
* Counts are recomputed with wp_update_comment_count_now(), so they honor the
* {@see 'default_excluded_comment_types'} filter. Call this once after changing
* the excluded types, the same way flush_rewrite_rules() is called after
* changing rewrite rules.
*
* Each recount fires the `wp_update_comment_count` and `edit_post` hooks and is
* written immediately, ignoring wp_defer_comment_counting(). A full recount on a
* large site is slow, so run it from WP-CLI or a scheduled event rather than an
* activation hook. It is idempotent and safe to interrupt.
*
* Bumps the comment `last_changed` cache key when there is a post to recount.
* Performs no capability checks.
*
* @since 7.3.0
*
* @global wpdb $wpdb WordPress database abstraction object.
*
* @param int[]|int|null $post_ids Optional. Post ID or array of post IDs to recalculate.
* Default null, which recalculates every post that has
* at least one comment or a nonzero stored count.
* @return int Number of posts whose comment count was recalculated. Nonexistent
* post IDs are skipped.
*/
function wp_update_comment_counts( $post_ids = null ) {
global $wpdb;

$recalculated = 0;

if ( null !== $post_ids ) {
$post_ids = array_map( 'intval', (array) $post_ids );
$post_ids = array_unique(
array_filter(
$post_ids,
static function ( $post_id ) {
return $post_id > 0;
}
)
);

if ( ! $post_ids ) {
return 0;
}

wp_cache_set_last_changed( 'comment' );

foreach ( $post_ids as $post_id ) {
if ( wp_update_comment_count_now( $post_id ) ) {
++$recalculated;
}
}

return $recalculated;
}

/**
* Filters how many posts wp_update_comment_counts() recalculates per batch.
*
* @since 7.3.0
*
* @param int $batch_size Number of posts to look up per query. Default 1000.
*/
$batch_size = (int) apply_filters( 'wp_update_comment_counts_batch_size', 1000 );

if ( $batch_size < 1 ) {
$batch_size = 1;
}

/*
* Visit posts with comment rows plus posts with a nonzero stored count, in
* keyset batches. Each union arm needs its own ORDER BY and LIMIT because
* MySQL does not push the outer ones into the arms. The first N rows of the
* union of two ascending sets are always among the first N of each.
*/
$last_post_id = 0;

do {
$batch = $wpdb->get_col(
$wpdb->prepare(
"( SELECT DISTINCT comment_post_ID AS post_id FROM {$wpdb->comments} WHERE comment_post_ID > %d ORDER BY comment_post_ID LIMIT %d )
UNION
( SELECT ID AS post_id FROM {$wpdb->posts} WHERE comment_count <> 0 AND ID > %d ORDER BY ID LIMIT %d )
ORDER BY post_id
LIMIT %d",
$last_post_id,
$batch_size,
$last_post_id,
$batch_size,
$batch_size
)
);

if ( $batch && 0 === $last_post_id ) {
wp_cache_set_last_changed( 'comment' );
}

foreach ( $batch as $post_id ) {
if ( wp_update_comment_count_now( (int) $post_id ) ) {
++$recalculated;
}
}

$last_post_id = $batch ? (int) end( $batch ) : 0;
} while ( count( $batch ) === $batch_size );

return $recalculated;
}

//
// Ping and trackback functions.
//
Expand Down
Loading