From 7e44a2fef718271ec4fc48c52387afd92a6f5392 Mon Sep 17 00:00:00 2001 From: Boone Gorges Date: Fri, 25 Sep 2015 05:10:40 +0000 Subject: [PATCH] Use `WP_Comment_Query` in `get_page_of_comment()`. This change allows `get_page_of_comment()` to use `WP_Comment_Query`'s native caching mechanisms. Props boonebgorges, Viper007Bond, wmertens, jeremyfelt. Fixes #11334. git-svn-id: https://develop.svn.wordpress.org/trunk@34535 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/comment-functions.php | 39 +++-- .../tests/comment/getPageOfComment.php | 136 +++++++++++++++++- 2 files changed, 158 insertions(+), 17 deletions(-) diff --git a/src/wp-includes/comment-functions.php b/src/wp-includes/comment-functions.php index 7499b97c57..810eef4ba3 100644 --- a/src/wp-includes/comment-functions.php +++ b/src/wp-includes/comment-functions.php @@ -821,9 +821,17 @@ function get_comment_pages_count( $comments = null, $per_page = null, $threaded * @since 2.7.0 * * @global wpdb $wpdb - * - * @param int $comment_ID Comment ID. - * @param array $args Optional args. + * @param int $comment_ID Comment ID. + * @param array $args { + * Array of optional arguments. + * @type string $type Limit paginated comments to those matching a given type. Accepts 'comment', + * 'trackback', 'pingback', 'pings' (trackbacks and pingbacks), or 'all'. + * Default is 'all'. + * @type int $per_page Per-page count to use when calculating pagination. Defaults to the value of the + * 'comments_per_page' option. + * @type int|string $max_depth If greater than 1, comment page will be determined for the top-level parent of + * `$comment_ID`. Defaults to the value of the 'thread_comments_depth' option. + * } * * @return int|null Comment page number or null on error. */ function get_page_of_comment( $comment_ID, $args = array() ) { @@ -855,23 +863,28 @@ function get_page_of_comment( $comment_ID, $args = array() ) { if ( $args['max_depth'] > 1 && 0 != $comment->comment_parent ) return get_page_of_comment( $comment->comment_parent, $args ); - $allowedtypes = array( - 'comment' => '', - 'pingback' => 'pingback', - 'trackback' => 'trackback', + $comment_args = array( + 'type' => $args['type'], + 'post_ID' => $comment->comment_post_ID, + 'fields' => 'ids', + 'status' => 'approve', + 'date_query' => array( + array( + 'column' => "$wpdb->comments.comment_date_gmt", + 'before' => $comment->comment_date_gmt, + ) + ), ); - $comtypewhere = ( 'all' != $args['type'] && isset($allowedtypes[$args['type']]) ) ? " AND comment_type = '" . $allowedtypes[$args['type']] . "'" : ''; - - // Count comments older than this one - $oldercoms = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(comment_ID) FROM $wpdb->comments WHERE comment_post_ID = %d AND comment_parent = 0 AND comment_approved = '1' AND comment_date_gmt < '%s'" . $comtypewhere, $comment->comment_post_ID, $comment->comment_date_gmt ) ); + $older_comment_ids = get_comments( $comment_args ); + $older_comment_count = count( $older_comment_ids ); // No older comments? Then it's page #1. - if ( 0 == $oldercoms ) + if ( 0 == $older_comment_count ) return 1; // Divide comments older than this one by comments per page to get this comment's page number - return ceil( ( $oldercoms + 1 ) / $args['per_page'] ); + return ceil( ( $older_comment_count + 1 ) / $args['per_page'] ); } /** diff --git a/tests/phpunit/tests/comment/getPageOfComment.php b/tests/phpunit/tests/comment/getPageOfComment.php index 5e03a16780..a4fd4cd865 100644 --- a/tests/phpunit/tests/comment/getPageOfComment.php +++ b/tests/phpunit/tests/comment/getPageOfComment.php @@ -6,10 +6,6 @@ */ class Tests_Comment_GetPageOfComment extends WP_UnitTestCase { - public function setUp() { - parent::setUp(); - } - public function test_last_comment() { $p = $this->factory->post->create(); @@ -38,4 +34,136 @@ class Tests_Comment_GetPageOfComment extends WP_UnitTestCase { $this->assertEquals( 1, get_page_of_comment( $comment_first[0], array( 'per_page' => 3 ) ) ); $this->assertEquals( 1, get_page_of_comment( $comment_first[0], array( 'per_page' => 10 ) ) ); } + + public function test_type_pings() { + $p = $this->factory->post->create(); + $now = time(); + + $trackbacks = array(); + for ( $i = 0; $i <= 3; $i++ ) { + $trackbacks[ $i ] = $this->factory->comment->create( array( 'comment_post_ID' => $p, 'comment_type' => 'trackback', 'comment_date_gmt' => date( 'Y-m-d H:i:s', $now ) ) ); + $now -= 10 * $i; + } + + $pingbacks = array(); + for ( $i = 0; $i <= 6; $i++ ) { + $pingbacks[ $i ] = $this->factory->comment->create( array( 'comment_post_ID' => $p, 'comment_type' => 'pingback', 'comment_date_gmt' => date( 'Y-m-d H:i:s', $now ) ) ); + $now -= 10 * $i; + } + + $this->assertEquals( 2, get_page_of_comment( $trackbacks[0], array( 'per_page' => 2, 'type' => 'trackback' ) ) ); + $this->assertEquals( 3, get_page_of_comment( $pingbacks[0], array( 'per_page' => 2, 'type' => 'pingback' ) ) ); + $this->assertEquals( 5, get_page_of_comment( $trackbacks[0], array( 'per_page' => 2, 'type' => 'pings' ) ) ); + } + + /** + * @ticket 11334 + */ + public function test_subsequent_calls_should_hit_cache() { + global $wpdb; + + $p = $this->factory->post->create(); + $c = $this->factory->comment->create( array( 'comment_post_ID' => $p ) ); + + // Prime cache. + $page_1 = get_page_of_comment( $c, array( 'per_page' => 3 ) ); + + $num_queries = $wpdb->num_queries; + $page_2 = get_page_of_comment( $c, array( 'per_page' => 3 ) ); + + $this->assertSame( $page_1, $page_2 ); + $this->assertSame( $num_queries, $wpdb->num_queries ); + } + + /** + * @ticket 11334 + */ + public function test_cache_hits_should_be_sensitive_to_comment_type() { + global $wpdb; + + $p = $this->factory->post->create(); + $comment = $this->factory->comment->create( array( 'comment_post_ID' => $p, 'comment_type' => 'comment' ) ); + + $now = time(); + $trackbacks = array(); + for ( $i = 0; $i <= 5; $i++ ) { + $trackbacks[ $i ] = $this->factory->comment->create( array( 'comment_post_ID' => $p, 'comment_type' => 'trackback', 'comment_date_gmt' => date( 'Y-m-d H:i:s', $now - ( 10 * $i ) ) ) ); + } + + // Prime cache for trackbacks. + $page_trackbacks = get_page_of_comment( $trackbacks[1], array( 'per_page' => 3, 'type' => 'trackback' ) ); + $this->assertEquals( 2, $page_trackbacks ); + + $num_queries = $wpdb->num_queries; + $page_comments = get_page_of_comment( $comment, array( 'per_page' => 3, 'type' => 'comment' ) ); + $this->assertEquals( 1, $page_comments ); + + $this->assertNotEquals( $num_queries, $wpdb->num_queries ); + } + + /** + * @ticket 11334 + */ + public function test_cache_should_be_invalidated_when_comment_is_approved() { + $p = $this->factory->post->create(); + $c = $this->factory->comment->create( array( 'comment_post_ID' => $p, 'comment_approved' => 0 ) ); + + // Prime cache. + $page_1 = get_page_of_comment( $c, array( 'per_page' => 3 ) ); + + // Approve comment. + wp_set_comment_status( $c, 'approve' ); + + $this->assertFalse( wp_cache_get( $c, 'comment_pages' ) ); + } + + /** + * @ticket 11334 + */ + public function test_cache_should_be_invalidated_when_comment_is_deleted() { + $p = $this->factory->post->create(); + $c = $this->factory->comment->create( array( 'comment_post_ID' => $p ) ); + + // Prime cache. + $page_1 = get_page_of_comment( $c, array( 'per_page' => 3 ) ); + + // Trash comment. + wp_trash_comment( $c ); + + $this->assertFalse( wp_cache_get( $c, 'comment_pages' ) ); + } + + /** + * @ticket 11334 + */ + public function test_cache_should_be_invalidated_when_comment_is_spammed() { + $p = $this->factory->post->create(); + $c = $this->factory->comment->create( array( 'comment_post_ID' => $p ) ); + + // Prime cache. + $page_1 = get_page_of_comment( $c, array( 'per_page' => 3 ) ); + + // Spam comment. + wp_spam_comment( $c ); + + $this->assertFalse( wp_cache_get( $c, 'comment_pages' ) ); + } + + /** + * @ticket 11334 + */ + public function test_cache_should_be_invalidated_when_older_comment_is_published() { + $now = time(); + + $p = $this->factory->post->create(); + $c1 = $this->factory->comment->create( array( 'comment_post_ID' => $p, 'comment_date_gmt' => date( 'Y-m-d H:i:s', $now ) ) ); + $c2 = $this->factory->comment->create( array( 'comment_post_ID' => $p, 'comment_date_gmt' => date( 'Y-m-d H:i:s', $now - 20 ) ) ); + $c3 = $this->factory->comment->create( array( 'comment_post_ID' => $p, 'comment_approved' => 0, 'comment_date_gmt' => date( 'Y-m-d H:i:s', $now - 30 ) ) ); + + $this->assertEquals( 1, get_page_of_comment( $c1, array( 'per_page' => 2 ) ) ); + + wp_set_comment_status( $c3, '1' ); + + $this->assertEquals( 2, get_page_of_comment( $c1, array( 'per_page' => 2 ) ) ); + } }