From 47cf2c21112b2e455dfe38eb8d0f96e530b613bc Mon Sep 17 00:00:00 2001 From: hbhalodia Date: Thu, 17 Jul 2025 13:31:50 +0530 Subject: [PATCH 01/14] Fix: Update the comment count to not include children with unapproved parent --- src/wp-includes/comment.php | 54 ++++++++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/src/wp-includes/comment.php b/src/wp-includes/comment.php index aabe9f60dbb4a..c9d4f9490de8e 100644 --- a/src/wp-includes/comment.php +++ b/src/wp-includes/comment.php @@ -2812,7 +2812,59 @@ function wp_update_comment_count_now( $post_id ) { $new = apply_filters( 'pre_wp_update_comment_count_now', null, $old, $post_id ); if ( is_null( $new ) ) { - $new = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->comments WHERE comment_post_ID = %d AND comment_approved = '1'", $post_id ) ); + /** + * Get all the comments related to the post ID. + */ + $comments = $wpdb->get_results( $wpdb->prepare( "SELECT comment_ID, comment_parent, comment_approved FROM $wpdb->comments WHERE comment_post_ID = %d", $post_id ) ); + $comments_by_id = []; + + /** + * Create a lookup array by comment ID. + */ + foreach ( $comments as $comment ) { + $comments_by_id[ $comment->comment_ID ] = $comment; + } + + // Count for comment. + $comment_count = 0; + + /** + * Loop through each comment and check for approved comment. + */ + foreach ( $comments as $comment ) { + + // Proceed only if comment is approved for counting. + if ( $comment->comment_approved !== '1' ) { + continue; + } + + $parent_id = (int) $comment->comment_parent; + $has_unapproved = false; + + /** + * Check until we get the parent id as 0. + */ + while ( $parent_id !== 0 ) { + if ( ! isset( $comments_by_id[ $parent_id ] ) ) { + break; + } + + $parent_comment = $comments_by_id[ $parent_id ]; + + if ( $parent_comment->comment_approved !== '1' ) { + $has_unapproved = true; + break; + } + + $parent_id = (int) $parent_comment->comment_parent; + } + + if ( ! $has_unapproved ) { + $comment_count++; + } + } + + $new = $comment_count; } else { $new = (int) $new; } From 4bef578a25b7333d2664878fc9e5dcd8386452a0 Mon Sep 17 00:00:00 2001 From: hbhalodia Date: Thu, 17 Jul 2025 13:41:36 +0530 Subject: [PATCH 02/14] Fix: PHPCS feedbacks --- src/wp-includes/comment.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/wp-includes/comment.php b/src/wp-includes/comment.php index c9d4f9490de8e..01dd280081cdb 100644 --- a/src/wp-includes/comment.php +++ b/src/wp-includes/comment.php @@ -2816,7 +2816,7 @@ function wp_update_comment_count_now( $post_id ) { * Get all the comments related to the post ID. */ $comments = $wpdb->get_results( $wpdb->prepare( "SELECT comment_ID, comment_parent, comment_approved FROM $wpdb->comments WHERE comment_post_ID = %d", $post_id ) ); - $comments_by_id = []; + $comments_by_id = array(); /** * Create a lookup array by comment ID. @@ -2834,7 +2834,7 @@ function wp_update_comment_count_now( $post_id ) { foreach ( $comments as $comment ) { // Proceed only if comment is approved for counting. - if ( $comment->comment_approved !== '1' ) { + if ( '1' !== $comment->comment_approved ) { continue; } @@ -2844,14 +2844,14 @@ function wp_update_comment_count_now( $post_id ) { /** * Check until we get the parent id as 0. */ - while ( $parent_id !== 0 ) { + while ( 0 !== $parent_id ) { if ( ! isset( $comments_by_id[ $parent_id ] ) ) { break; } $parent_comment = $comments_by_id[ $parent_id ]; - if ( $parent_comment->comment_approved !== '1' ) { + if ( '1' !== $parent_comment->comment_approved ) { $has_unapproved = true; break; } From 01c7ce934a9f0ccb25ae302878517a46b1f95ee0 Mon Sep 17 00:00:00 2001 From: hbhalodia Date: Tue, 4 Nov 2025 16:20:16 +0530 Subject: [PATCH 03/14] Add the test case for the updated comment count scenario --- .../tests/comment/wpUpdateCommentCountNow.php | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/tests/phpunit/tests/comment/wpUpdateCommentCountNow.php b/tests/phpunit/tests/comment/wpUpdateCommentCountNow.php index 343693484596d..13abfdb765055 100644 --- a/tests/phpunit/tests/comment/wpUpdateCommentCountNow.php +++ b/tests/phpunit/tests/comment/wpUpdateCommentCountNow.php @@ -49,4 +49,94 @@ public function test_using_filter_adjusts_comment_count_without_an_additional_da public function _return_100() { return 100; } + + /** + * Test case where a trashed parent comment causes its child comments to be excluded from the comment count. + * + * @return void + */ + public function test_trashed_parent_comment_excludes_child_comments_from_count() { + $post_id = self::factory()->post->create(); + + /** + * Create 2 parent comment, and 2 child comment for parent 1. + */ + $parent_comment_id = self::factory()->comment->create( array( + 'comment_post_ID' => $post_id, + 'comment_approved' => 1, + ) ); + + self::factory()->comment->create( array( + 'comment_post_ID' => $post_id, + 'comment_approved' => 1, + ) ); + + self::factory()->comment->create( array( + 'comment_post_ID' => $post_id, + 'comment_parent' => $parent_comment_id, + 'comment_approved' => 1, + ) ); + + self::factory()->comment->create( array( + 'comment_post_ID' => $post_id, + 'comment_parent' => $parent_comment_id, + 'comment_approved' => 1, + ) ); + + wp_update_comment_count_now( $post_id ); + $this->assertSame( '4', get_comments_number( $post_id ) ); + + wp_update_comment( array( + 'comment_ID' => $parent_comment_id, + 'comment_approved' => 'trash', + ) ); + + wp_update_comment_count_now( $post_id ); + $this->assertSame( '1', get_comments_number( $post_id ) ); + } + + /** + * Test case for unapproved parent comment causing its child comments to be excluded from the comment count. + * + * @return void + */ + public function test_unapproved_parent_comment_excludes_child_comments_from_count() { + $post_id = self::factory()->post->create(); + + /** + * Create 2 parent comment, and 2 child comment for parent 1. + */ + $parent_comment_id = self::factory()->comment->create( array( + 'comment_post_ID' => $post_id, + 'comment_approved' => 1, + ) ); + + self::factory()->comment->create( array( + 'comment_post_ID' => $post_id, + 'comment_approved' => 1, + ) ); + + self::factory()->comment->create( array( + 'comment_post_ID' => $post_id, + 'comment_parent' => $parent_comment_id, + 'comment_approved' => 1, + ) ); + + self::factory()->comment->create( array( + 'comment_post_ID' => $post_id, + 'comment_parent' => $parent_comment_id, + 'comment_approved' => 1, + ) ); + + wp_update_comment_count_now( $post_id ); + $this->assertSame( '4', get_comments_number( $post_id ) ); + + wp_update_comment( array( + 'comment_ID' => $parent_comment_id, + 'comment_approved' => '0', + ) ); + + wp_update_comment_count_now( $post_id ); + $this->assertSame( '1', get_comments_number( $post_id ) ); + } } From 75dbcab284884f829a7b88ca2554c9bee43c1096 Mon Sep 17 00:00:00 2001 From: hbhalodia Date: Tue, 4 Nov 2025 16:30:10 +0530 Subject: [PATCH 04/14] Fix phpcs issue --- .../tests/comment/wpUpdateCommentCountNow.php | 100 ++++++++++-------- 1 file changed, 58 insertions(+), 42 deletions(-) diff --git a/tests/phpunit/tests/comment/wpUpdateCommentCountNow.php b/tests/phpunit/tests/comment/wpUpdateCommentCountNow.php index 13abfdb765055..52a97c00d2b5c 100644 --- a/tests/phpunit/tests/comment/wpUpdateCommentCountNow.php +++ b/tests/phpunit/tests/comment/wpUpdateCommentCountNow.php @@ -61,27 +61,35 @@ public function test_trashed_parent_comment_excludes_child_comments_from_count() /** * Create 2 parent comment, and 2 child comment for parent 1. */ - $parent_comment_id = self::factory()->comment->create( array( - 'comment_post_ID' => $post_id, - 'comment_approved' => 1, - ) ); - - self::factory()->comment->create( array( - 'comment_post_ID' => $post_id, - 'comment_approved' => 1, - ) ); - - self::factory()->comment->create( array( - 'comment_post_ID' => $post_id, - 'comment_parent' => $parent_comment_id, - 'comment_approved' => 1, - ) ); - - self::factory()->comment->create( array( - 'comment_post_ID' => $post_id, - 'comment_parent' => $parent_comment_id, - 'comment_approved' => 1, - ) ); + $parent_comment_id = self::factory()->comment->create( + array( + 'comment_post_ID' => $post_id, + 'comment_approved' => 1, + ) + ); + + self::factory()->comment->create( + array( + 'comment_post_ID' => $post_id, + 'comment_approved' => 1, + ) + ); + + self::factory()->comment->create( + array( + 'comment_post_ID' => $post_id, + 'comment_parent' => $parent_comment_id, + 'comment_approved' => 1, + ) + ); + + self::factory()->comment->create( + array( + 'comment_post_ID' => $post_id, + 'comment_parent' => $parent_comment_id, + 'comment_approved' => 1, + ) + ); wp_update_comment_count_now( $post_id ); $this->assertSame( '4', get_comments_number( $post_id ) ); @@ -106,27 +114,35 @@ public function test_unapproved_parent_comment_excludes_child_comments_from_coun /** * Create 2 parent comment, and 2 child comment for parent 1. */ - $parent_comment_id = self::factory()->comment->create( array( - 'comment_post_ID' => $post_id, - 'comment_approved' => 1, - ) ); - - self::factory()->comment->create( array( - 'comment_post_ID' => $post_id, - 'comment_approved' => 1, - ) ); - - self::factory()->comment->create( array( - 'comment_post_ID' => $post_id, - 'comment_parent' => $parent_comment_id, - 'comment_approved' => 1, - ) ); - - self::factory()->comment->create( array( - 'comment_post_ID' => $post_id, - 'comment_parent' => $parent_comment_id, - 'comment_approved' => 1, - ) ); + $parent_comment_id = self::factory()->comment->create( + array( + 'comment_post_ID' => $post_id, + 'comment_approved' => 1, + ) + ); + + self::factory()->comment->create( + array( + 'comment_post_ID' => $post_id, + 'comment_approved' => 1, + ) + ); + + self::factory()->comment->create( + array( + 'comment_post_ID' => $post_id, + 'comment_parent' => $parent_comment_id, + 'comment_approved' => 1, + ) + ); + + self::factory()->comment->create( + array( + 'comment_post_ID' => $post_id, + 'comment_parent' => $parent_comment_id, + 'comment_approved' => 1, + ) + ); wp_update_comment_count_now( $post_id ); $this->assertSame( '4', get_comments_number( $post_id ) ); From 158061f25e50fcd93cf1470d9fc22b7b83fec776 Mon Sep 17 00:00:00 2001 From: hbhalodia Date: Tue, 4 Nov 2025 16:31:25 +0530 Subject: [PATCH 05/14] Resolve phpcs multiline issue --- .../tests/comment/wpUpdateCommentCountNow.php | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/tests/phpunit/tests/comment/wpUpdateCommentCountNow.php b/tests/phpunit/tests/comment/wpUpdateCommentCountNow.php index 52a97c00d2b5c..8203a54fb73da 100644 --- a/tests/phpunit/tests/comment/wpUpdateCommentCountNow.php +++ b/tests/phpunit/tests/comment/wpUpdateCommentCountNow.php @@ -94,10 +94,12 @@ public function test_trashed_parent_comment_excludes_child_comments_from_count() wp_update_comment_count_now( $post_id ); $this->assertSame( '4', get_comments_number( $post_id ) ); - wp_update_comment( array( - 'comment_ID' => $parent_comment_id, - 'comment_approved' => 'trash', - ) ); + wp_update_comment( + array( + 'comment_ID' => $parent_comment_id, + 'comment_approved' => 'trash', + ) + ); wp_update_comment_count_now( $post_id ); $this->assertSame( '1', get_comments_number( $post_id ) ); @@ -147,10 +149,12 @@ public function test_unapproved_parent_comment_excludes_child_comments_from_coun wp_update_comment_count_now( $post_id ); $this->assertSame( '4', get_comments_number( $post_id ) ); - wp_update_comment( array( - 'comment_ID' => $parent_comment_id, - 'comment_approved' => '0', - ) ); + wp_update_comment( + array( + 'comment_ID' => $parent_comment_id, + 'comment_approved' => '0', + ) + ); wp_update_comment_count_now( $post_id ); $this->assertSame( '1', get_comments_number( $post_id ) ); From e64afbaed322cf0b58d4509f75c2cd27a6179003 Mon Sep 17 00:00:00 2001 From: hbhalodia Date: Tue, 4 Nov 2025 19:36:10 +0530 Subject: [PATCH 06/14] Add ticket number to unit test --- tests/phpunit/tests/comment/wpUpdateCommentCountNow.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/phpunit/tests/comment/wpUpdateCommentCountNow.php b/tests/phpunit/tests/comment/wpUpdateCommentCountNow.php index 8203a54fb73da..fa2da000c1a88 100644 --- a/tests/phpunit/tests/comment/wpUpdateCommentCountNow.php +++ b/tests/phpunit/tests/comment/wpUpdateCommentCountNow.php @@ -53,6 +53,8 @@ public function _return_100() { /** * Test case where a trashed parent comment causes its child comments to be excluded from the comment count. * + * @ticket 36409 + * * @return void */ public function test_trashed_parent_comment_excludes_child_comments_from_count() { @@ -108,6 +110,8 @@ public function test_trashed_parent_comment_excludes_child_comments_from_count() /** * Test case for unapproved parent comment causing its child comments to be excluded from the comment count. * + * @ticket 36409 + * * @return void */ public function test_unapproved_parent_comment_excludes_child_comments_from_count() { From bc41c7e5f2e47dd1ed7c3d2514bbaf6bd18e78e6 Mon Sep 17 00:00:00 2001 From: hbhalodia Date: Thu, 19 Feb 2026 11:01:19 +0530 Subject: [PATCH 07/14] Address feedbacks --- src/wp-includes/comment.php | 24 +++++++------------ .../tests/comment/wpUpdateCommentCountNow.php | 12 ++-------- 2 files changed, 11 insertions(+), 25 deletions(-) diff --git a/src/wp-includes/comment.php b/src/wp-includes/comment.php index 03fad35b99337..937412883c379 100644 --- a/src/wp-includes/comment.php +++ b/src/wp-includes/comment.php @@ -2875,28 +2875,24 @@ function wp_update_comment_count_now( $post_id ) { $new = apply_filters( 'pre_wp_update_comment_count_now', null, $old, $post_id ); if ( is_null( $new ) ) { - /** - * Get all the comments related to the post ID. - */ - $comments = $wpdb->get_results( $wpdb->prepare( "SELECT comment_ID, comment_parent, comment_approved FROM $wpdb->comments WHERE comment_post_ID = %d AND comment_type != 'note'", $post_id ) ); + $comments = get_comments( + array( + 'post_id' => $post_id, + 'type__not_in' => array( 'note' ), + ) + ); $comments_by_id = array(); - /** - * Create a lookup array by comment ID. - */ + // Create a lookup array by comment ID. foreach ( $comments as $comment ) { $comments_by_id[ $comment->comment_ID ] = $comment; } - // Count for comment. $comment_count = 0; - /** - * Loop through each comment and check for approved comment. - */ + // Loop through each comment and check for approved comment. foreach ( $comments as $comment ) { - // Proceed only if comment is approved for counting. if ( '1' !== $comment->comment_approved ) { continue; } @@ -2904,9 +2900,7 @@ function wp_update_comment_count_now( $post_id ) { $parent_id = (int) $comment->comment_parent; $has_unapproved = false; - /** - * Check until we get the parent id as 0. - */ + // Ensure all ancestor comments are approved. while ( 0 !== $parent_id ) { if ( ! isset( $comments_by_id[ $parent_id ] ) ) { break; diff --git a/tests/phpunit/tests/comment/wpUpdateCommentCountNow.php b/tests/phpunit/tests/comment/wpUpdateCommentCountNow.php index 0c72d068e4bd2..dc9f7069e3f84 100644 --- a/tests/phpunit/tests/comment/wpUpdateCommentCountNow.php +++ b/tests/phpunit/tests/comment/wpUpdateCommentCountNow.php @@ -91,15 +91,11 @@ public function _return_100() { * Test case where a trashed parent comment causes its child comments to be excluded from the comment count. * * @ticket 36409 - * - * @return void */ public function test_trashed_parent_comment_excludes_child_comments_from_count() { $post_id = self::factory()->post->create(); - /** - * Create 2 parent comment, and 2 child comment for parent 1. - */ + // Create 2 parent comment, and 2 child comment for parent 1. $parent_comment_id = self::factory()->comment->create( array( 'comment_post_ID' => $post_id, @@ -148,15 +144,11 @@ public function test_trashed_parent_comment_excludes_child_comments_from_count() * Test case for unapproved parent comment causing its child comments to be excluded from the comment count. * * @ticket 36409 - * - * @return void */ public function test_unapproved_parent_comment_excludes_child_comments_from_count() { $post_id = self::factory()->post->create(); - /** - * Create 2 parent comment, and 2 child comment for parent 1. - */ + // Create 2 parent comment, and 2 child comment for parent 1. $parent_comment_id = self::factory()->comment->create( array( 'comment_post_ID' => $post_id, From 086c901dcdc29e5edf36f2f21f003d7928881950 Mon Sep 17 00:00:00 2001 From: hbhalodia Date: Thu, 19 Feb 2026 11:51:44 +0530 Subject: [PATCH 08/14] Fix the tests --- src/wp-includes/comment.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/wp-includes/comment.php b/src/wp-includes/comment.php index 937412883c379..2b61d380ac0e0 100644 --- a/src/wp-includes/comment.php +++ b/src/wp-includes/comment.php @@ -2903,6 +2903,7 @@ function wp_update_comment_count_now( $post_id ) { // Ensure all ancestor comments are approved. while ( 0 !== $parent_id ) { if ( ! isset( $comments_by_id[ $parent_id ] ) ) { + $has_unapproved = true; break; } From b136e33af2aba53a02ed0652b572980b48ff2f27 Mon Sep 17 00:00:00 2001 From: hbhalodia Date: Thu, 19 Feb 2026 15:47:56 +0530 Subject: [PATCH 09/14] Fix test cases --- src/wp-includes/comment.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/wp-includes/comment.php b/src/wp-includes/comment.php index 2b61d380ac0e0..5593cb557ef9c 100644 --- a/src/wp-includes/comment.php +++ b/src/wp-includes/comment.php @@ -2877,8 +2877,9 @@ function wp_update_comment_count_now( $post_id ) { if ( is_null( $new ) ) { $comments = get_comments( array( - 'post_id' => $post_id, - 'type__not_in' => array( 'note' ), + 'post_id' => $post_id, + 'type__not_in' => array( 'note' ), + 'update_comment_meta_cache' => false, ) ); $comments_by_id = array(); From 9129e384b6a1ea63b16dca970c1a0e57f0510310 Mon Sep 17 00:00:00 2001 From: hbhalodia Date: Fri, 13 Mar 2026 10:45:40 +0530 Subject: [PATCH 10/14] Address the feedbacks on the PR --- src/wp-includes/comment.php | 23 ++++++------ .../tests/comment/wpUpdateCommentCountNow.php | 36 +++++++++++++------ 2 files changed, 38 insertions(+), 21 deletions(-) diff --git a/src/wp-includes/comment.php b/src/wp-includes/comment.php index 599bfb2b5a948..1c1e34317ca13 100644 --- a/src/wp-includes/comment.php +++ b/src/wp-includes/comment.php @@ -2875,13 +2875,7 @@ function wp_update_comment_count_now( $post_id ) { $new = apply_filters( 'pre_wp_update_comment_count_now', null, $old, $post_id ); if ( is_null( $new ) ) { - $comments = get_comments( - array( - 'post_id' => $post_id, - 'type__not_in' => array( 'note' ), - 'update_comment_meta_cache' => false, - ) - ); + $comments = $wpdb->get_results( $wpdb->prepare( "SELECT comment_ID, comment_parent, comment_approved FROM $wpdb->comments WHERE comment_post_ID = %d AND comment_type != 'note'", $post_id ) ); $comments_by_id = array(); // Create a lookup array by comment ID. @@ -2898,11 +2892,18 @@ function wp_update_comment_count_now( $post_id ) { continue; } - $parent_id = (int) $comment->comment_parent; - $has_unapproved = false; + $parent_id = (int) $comment->comment_parent; + $has_unapproved = false; + $visited_comments_ids = array(); - // Ensure all ancestor comments are approved. while ( 0 !== $parent_id ) { + if ( isset( $visited_comments_ids[ $parent_id ] ) ) { + $has_unapproved = true; + break; + } + + $visited_ids[ $parent_id ] = true; + if ( ! isset( $comments_by_id[ $parent_id ] ) ) { $has_unapproved = true; break; @@ -2919,7 +2920,7 @@ function wp_update_comment_count_now( $post_id ) { } if ( ! $has_unapproved ) { - $comment_count++; + ++$comment_count; } } diff --git a/tests/phpunit/tests/comment/wpUpdateCommentCountNow.php b/tests/phpunit/tests/comment/wpUpdateCommentCountNow.php index dc9f7069e3f84..11cbcce33b9ca 100644 --- a/tests/phpunit/tests/comment/wpUpdateCommentCountNow.php +++ b/tests/phpunit/tests/comment/wpUpdateCommentCountNow.php @@ -95,7 +95,7 @@ public function _return_100() { public function test_trashed_parent_comment_excludes_child_comments_from_count() { $post_id = self::factory()->post->create(); - // Create 2 parent comment, and 2 child comment for parent 1. + // Create 2 top-level parent comments, 2 child comments for parent 1, and a grandchild of parent 1. $parent_comment_id = self::factory()->comment->create( array( 'comment_post_ID' => $post_id, @@ -110,7 +110,7 @@ public function test_trashed_parent_comment_excludes_child_comments_from_count() ) ); - self::factory()->comment->create( + $child_comment_id = self::factory()->comment->create( array( 'comment_post_ID' => $post_id, 'comment_parent' => $parent_comment_id, @@ -126,8 +126,16 @@ public function test_trashed_parent_comment_excludes_child_comments_from_count() ) ); - wp_update_comment_count_now( $post_id ); - $this->assertSame( '4', get_comments_number( $post_id ) ); + self::factory()->comment->create( + array( + 'comment_post_ID' => $post_id, + 'comment_parent' => $child_comment_id, + 'comment_approved' => 1, + ) + ); + + $this->assertTrue( wp_update_comment_count_now( $post_id ) ); + $this->assertSame( '5', get_comments_number( $post_id ) ); wp_update_comment( array( @@ -136,7 +144,7 @@ public function test_trashed_parent_comment_excludes_child_comments_from_count() ) ); - wp_update_comment_count_now( $post_id ); + $this->assertTrue( wp_update_comment_count_now( $post_id ) ); $this->assertSame( '1', get_comments_number( $post_id ) ); } @@ -148,7 +156,7 @@ public function test_trashed_parent_comment_excludes_child_comments_from_count() public function test_unapproved_parent_comment_excludes_child_comments_from_count() { $post_id = self::factory()->post->create(); - // Create 2 parent comment, and 2 child comment for parent 1. + // Create 2 top-level parent comments, 2 child comments for parent 1, and a grandchild of parent 1. $parent_comment_id = self::factory()->comment->create( array( 'comment_post_ID' => $post_id, @@ -163,7 +171,7 @@ public function test_unapproved_parent_comment_excludes_child_comments_from_coun ) ); - self::factory()->comment->create( + $child_comment_id = self::factory()->comment->create( array( 'comment_post_ID' => $post_id, 'comment_parent' => $parent_comment_id, @@ -179,8 +187,16 @@ public function test_unapproved_parent_comment_excludes_child_comments_from_coun ) ); - wp_update_comment_count_now( $post_id ); - $this->assertSame( '4', get_comments_number( $post_id ) ); + self::factory()->comment->create( + array( + 'comment_post_ID' => $post_id, + 'comment_parent' => $child_comment_id, + 'comment_approved' => 1, + ) + ); + + $this->assertTrue( wp_update_comment_count_now( $post_id ) ); + $this->assertSame( '5', get_comments_number( $post_id ) ); wp_update_comment( array( @@ -189,7 +205,7 @@ public function test_unapproved_parent_comment_excludes_child_comments_from_coun ) ); - wp_update_comment_count_now( $post_id ); + $this->assertTrue( wp_update_comment_count_now( $post_id ) ); $this->assertSame( '1', get_comments_number( $post_id ) ); } } From b6689fff47a447784776950f45637a4536848db2 Mon Sep 17 00:00:00 2001 From: hbhalodia Date: Fri, 13 Mar 2026 10:50:32 +0530 Subject: [PATCH 11/14] Fix variable name --- src/wp-includes/comment.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/comment.php b/src/wp-includes/comment.php index 1c1e34317ca13..81fdd6974d68a 100644 --- a/src/wp-includes/comment.php +++ b/src/wp-includes/comment.php @@ -2902,7 +2902,7 @@ function wp_update_comment_count_now( $post_id ) { break; } - $visited_ids[ $parent_id ] = true; + $visited_comments_ids[ $parent_id ] = true; if ( ! isset( $comments_by_id[ $parent_id ] ) ) { $has_unapproved = true; From c3cda23385a39210c0a5638a8bc307c3fd880ef8 Mon Sep 17 00:00:00 2001 From: hbhalodia Date: Fri, 13 Mar 2026 11:12:36 +0530 Subject: [PATCH 12/14] Update to use memomization and reduce time complexity --- src/wp-includes/comment.php | 47 ++++++++++++++++++++++++++++++------- 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/src/wp-includes/comment.php b/src/wp-includes/comment.php index 81fdd6974d68a..6eb62af454f40 100644 --- a/src/wp-includes/comment.php +++ b/src/wp-includes/comment.php @@ -2883,26 +2883,51 @@ function wp_update_comment_count_now( $post_id ) { $comments_by_id[ $comment->comment_ID ] = $comment; } - $comment_count = 0; + $comment_count = 0; + $ancestor_approval_cache = array(); + + foreach ( $comments as $comment ) { + if ( '1' !== $comment->comment_approved ) { + $ancestor_approval_cache[ (int) $comment->comment_ID ] = false; + } + } - // Loop through each comment and check for approved comment. foreach ( $comments as $comment ) { if ( '1' !== $comment->comment_approved ) { continue; } - $parent_id = (int) $comment->comment_parent; - $has_unapproved = false; - $visited_comments_ids = array(); + $comment_id = (int) $comment->comment_ID; + + // Use cached result if this comment's ancestry was already resolved. + if ( isset( $ancestor_approval_cache[ $comment_id ] ) ) { + if ( $ancestor_approval_cache[ $comment_id ] ) { + ++$comment_count; + } + continue; + } + + // Walk the ancestor chain, collecting IDs to memoize afterwards. + $chain = array( $comment_id ); + $visited = array( + $comment_id => true, + ); + $parent_id = (int) $comment->comment_parent; + $has_unapproved = false; while ( 0 !== $parent_id ) { - if ( isset( $visited_comments_ids[ $parent_id ] ) ) { + if ( isset( $visited[ $parent_id ] ) ) { $has_unapproved = true; break; } - $visited_comments_ids[ $parent_id ] = true; + if ( isset( $ancestor_approval_cache[ $parent_id ] ) ) { + if ( ! $ancestor_approval_cache[ $parent_id ] ) { + $has_unapproved = true; + } + break; + } if ( ! isset( $comments_by_id[ $parent_id ] ) ) { $has_unapproved = true; @@ -2916,7 +2941,13 @@ function wp_update_comment_count_now( $post_id ) { break; } - $parent_id = (int) $parent_comment->comment_parent; + $visited[ $parent_id ] = true; + $chain[] = $parent_id; + $parent_id = (int) $parent_comment->comment_parent; + } + + foreach ( $chain as $chain_id ) { + $ancestor_approval_cache[ $chain_id ] = ! $has_unapproved; } if ( ! $has_unapproved ) { From e351f6f07201911b9a950b6a29c0c529730cc3f7 Mon Sep 17 00:00:00 2001 From: hbhalodia Date: Fri, 13 Mar 2026 11:21:57 +0530 Subject: [PATCH 13/14] Resolve copilot feedbacks --- tests/phpunit/tests/comment/wpUpdateCommentCountNow.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/phpunit/tests/comment/wpUpdateCommentCountNow.php b/tests/phpunit/tests/comment/wpUpdateCommentCountNow.php index 11cbcce33b9ca..09fde6c37f06b 100644 --- a/tests/phpunit/tests/comment/wpUpdateCommentCountNow.php +++ b/tests/phpunit/tests/comment/wpUpdateCommentCountNow.php @@ -95,7 +95,7 @@ public function _return_100() { public function test_trashed_parent_comment_excludes_child_comments_from_count() { $post_id = self::factory()->post->create(); - // Create 2 top-level parent comments, 2 child comments for parent 1, and a grandchild of parent 1. + // Create 2 top-level comments, 2 child comments for the first top-level comment, and a grandchild of that first comment. $parent_comment_id = self::factory()->comment->create( array( 'comment_post_ID' => $post_id, @@ -149,14 +149,14 @@ public function test_trashed_parent_comment_excludes_child_comments_from_count() } /** - * Test case for unapproved parent comment causing its child comments to be excluded from the comment count. + * Test case where an unapproved parent comment causes its child comments to be excluded from the comment count. * * @ticket 36409 */ public function test_unapproved_parent_comment_excludes_child_comments_from_count() { $post_id = self::factory()->post->create(); - // Create 2 top-level parent comments, 2 child comments for parent 1, and a grandchild of parent 1. + // Create 2 top-level comments, 2 child comments for the first top-level comment, and a grandchild of that first comment. $parent_comment_id = self::factory()->comment->create( array( 'comment_post_ID' => $post_id, From c97aa7847a83202fa4ed17e4b2e58a1b77d06024 Mon Sep 17 00:00:00 2001 From: hbhalodia Date: Fri, 13 Mar 2026 11:22:47 +0530 Subject: [PATCH 14/14] Cast results to array --- src/wp-includes/comment.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/comment.php b/src/wp-includes/comment.php index 6eb62af454f40..178528926d909 100644 --- a/src/wp-includes/comment.php +++ b/src/wp-includes/comment.php @@ -2875,7 +2875,7 @@ function wp_update_comment_count_now( $post_id ) { $new = apply_filters( 'pre_wp_update_comment_count_now', null, $old, $post_id ); if ( is_null( $new ) ) { - $comments = $wpdb->get_results( $wpdb->prepare( "SELECT comment_ID, comment_parent, comment_approved FROM $wpdb->comments WHERE comment_post_ID = %d AND comment_type != 'note'", $post_id ) ); + $comments = (array) $wpdb->get_results( $wpdb->prepare( "SELECT comment_ID, comment_parent, comment_approved FROM $wpdb->comments WHERE comment_post_ID = %d AND comment_type != 'note'", $post_id ) ); $comments_by_id = array(); // Create a lookup array by comment ID.