From 6a0dfab714dc66bf42027c0e63915aa53824c37d Mon Sep 17 00:00:00 2001 From: adamsilverstein Date: Wed, 24 Jun 2026 17:15:54 -0700 Subject: [PATCH 01/14] Comments: Generalize ping handling with an `is_ping` comment type flag. Pingbacks and trackbacks were singled out by hard-coded `comment_type` string comparisons in `separate_comments()` and `Walker_Comment`, so the "this is a ping, not a human comment" distinction could not be expressed by a registered comment type. Add an `is_ping` property to `WP_Comment_Type` (default false) and mark the built-in `pingback` and `trackback` types with it. `separate_comments()` now groups any registered ping type into the `pings` bucket, and `Walker_Comment::start_el()` renders any ping type with the compact ping markup. Built-in behaviour is unchanged. See #35214. --- src/wp-includes/class-walker-comment.php | 4 +++- src/wp-includes/class-wp-comment-type.php | 14 ++++++++++++++ src/wp-includes/comment.php | 10 +++++++++- 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/wp-includes/class-walker-comment.php b/src/wp-includes/class-walker-comment.php index 1c0c98795500e..1af31a2003316 100644 --- a/src/wp-includes/class-walker-comment.php +++ b/src/wp-includes/class-walker-comment.php @@ -202,7 +202,9 @@ public function start_el( &$output, $data_object, $depth = 0, $args = array(), $ add_filter( 'comment_text', array( $this, 'filter_comment_text' ), 40, 2 ); } - if ( ( 'pingback' === $comment->comment_type || 'trackback' === $comment->comment_type ) && $args['short_ping'] ) { + $is_ping = $comment_type_object && $comment_type_object->is_ping; + + if ( $is_ping && $args['short_ping'] ) { ob_start(); $this->ping( $comment, $depth, $args ); $output .= ob_get_clean(); diff --git a/src/wp-includes/class-wp-comment-type.php b/src/wp-includes/class-wp-comment-type.php index a8c0463eb16d7..ece67efc0e49e 100644 --- a/src/wp-includes/class-wp-comment-type.php +++ b/src/wp-includes/class-wp-comment-type.php @@ -119,6 +119,19 @@ final class WP_Comment_Type { */ public $render_callback = null; + /** + * Whether the comment type represents a ping (a notification from another site) + * rather than a human-authored comment. + * + * Ping types (such as `pingback` and `trackback`) are grouped together by + * {@see separate_comments()} and rendered with the compact ping markup by + * {@see Walker_Comment}. Default false. + * + * @since 7.1.0 + * @var bool + */ + public $is_ping = false; + /** * Whether the comment type is hierarchical. * @@ -208,6 +221,7 @@ public function set_props( $args ) { 'internal' => false, 'show_ui' => null, 'render_callback' => null, + 'is_ping' => false, '_builtin' => false, ); diff --git a/src/wp-includes/comment.php b/src/wp-includes/comment.php index 4e3f6f58f0996..3ea28aae46939 100644 --- a/src/wp-includes/comment.php +++ b/src/wp-includes/comment.php @@ -303,6 +303,7 @@ function create_initial_comment_types() { 'singular_name' => __( 'Pingback' ), ), 'public' => true, + 'is_ping' => true, '_builtin' => true, ) ); @@ -315,6 +316,7 @@ function create_initial_comment_types() { 'singular_name' => __( 'Trackback' ), ), 'public' => true, + 'is_ping' => true, '_builtin' => true, ) ); @@ -367,6 +369,10 @@ function create_initial_comment_types() { * excluded from default public-facing contexts. Default false. * @type bool $show_ui Whether to generate and allow a UI for managing this comment * type in the admin. Default is value of $public. + * @type bool $is_ping Whether the comment type represents a ping (a notification from + * another site) rather than a human-authored comment. Ping types are + * grouped together by separate_comments() and rendered with compact + * ping markup by Walker_Comment. Default false. * @type callable $render_callback Callback used to render a comment of this type in comment * lists. Receives the same arguments as the `callback` argument * of wp_list_comments() (the comment, the arguments, and the @@ -1256,7 +1262,9 @@ function separate_comments( &$comments ) { $comments_by_type[ $type ][] = &$comments[ $i ]; - if ( 'trackback' === $type || 'pingback' === $type ) { + $comment_type_object = get_comment_type_object( $type ); + + if ( $comment_type_object && $comment_type_object->is_ping ) { $comments_by_type['pings'][] = &$comments[ $i ]; } } From 4bbd85ca66acb6dc3c668b92b19908801cd0dc83 Mon Sep 17 00:00:00 2001 From: adamsilverstein Date: Wed, 24 Jun 2026 17:15:59 -0700 Subject: [PATCH 02/14] Comments: Add tests for `is_ping` comment type grouping. Cover the new flag end to end: the `WP_Comment_Type` default and storage, the built-in pingback/trackback types being marked as pings (and comment/note not), `separate_comments()` grouping a registered ping type into the `pings` bucket while leaving non-ping types out, and `Walker_Comment` rendering a registered ping type with the compact ping markup. See #35214. --- .../tests/comment/separateComments.php | 108 ++++++++++++++++++ tests/phpunit/tests/comment/types.php | 16 +++ tests/phpunit/tests/comment/walker.php | 29 +++++ tests/phpunit/tests/comment/wpCommentType.php | 12 ++ 4 files changed, 165 insertions(+) create mode 100644 tests/phpunit/tests/comment/separateComments.php diff --git a/tests/phpunit/tests/comment/separateComments.php b/tests/phpunit/tests/comment/separateComments.php new file mode 100644 index 0000000000000..b9145f88a836d --- /dev/null +++ b/tests/phpunit/tests/comment/separateComments.php @@ -0,0 +1,108 @@ +post->create(); + } + + /** + * Builds a comment of the given type on the shared post. + * + * @param string $comment_type Comment type slug. + * @return WP_Comment The created comment object. + */ + private function make_comment( $comment_type ) { + return get_comment( + self::factory()->comment->create( + array( + 'comment_post_ID' => self::$post_id, + 'comment_type' => $comment_type, + ) + ) + ); + } + + /** + * The standard buckets are always present, even when empty. + * + * @ticket 35214 + */ + public function test_default_buckets_are_always_present() { + $comments = array(); + $separated = separate_comments( $comments ); + + $this->assertArrayHasKey( 'comment', $separated ); + $this->assertArrayHasKey( 'trackback', $separated ); + $this->assertArrayHasKey( 'pingback', $separated ); + $this->assertArrayHasKey( 'pings', $separated ); + } + + /** + * Built-in pingbacks and trackbacks are grouped into the 'pings' bucket. + * + * @ticket 35214 + */ + public function test_built_in_pings_are_grouped_into_pings_bucket() { + $comments = array( + $this->make_comment( 'comment' ), + $this->make_comment( 'pingback' ), + $this->make_comment( 'trackback' ), + ); + + $separated = separate_comments( $comments ); + + $this->assertCount( 1, $separated['comment'] ); + $this->assertCount( 1, $separated['pingback'] ); + $this->assertCount( 1, $separated['trackback'] ); + $this->assertCount( 2, $separated['pings'] ); + } + + /** + * A registered comment type marked as a ping is grouped into 'pings'. + * + * @ticket 35214 + */ + public function test_registered_ping_type_is_grouped_into_pings_bucket() { + register_comment_type( 'webmention', array( 'is_ping' => true ) ); + + $comments = array( $this->make_comment( 'webmention' ) ); + + $separated = separate_comments( $comments ); + + $this->assertCount( 1, $separated['webmention'] ); + $this->assertCount( 1, $separated['pings'] ); + + unregister_comment_type( 'webmention' ); + } + + /** + * A registered comment type that is not a ping stays out of the 'pings' bucket. + * + * @ticket 35214 + */ + public function test_non_ping_type_is_not_grouped_into_pings_bucket() { + register_comment_type( 'review' ); + + $comments = array( $this->make_comment( 'review' ) ); + + $separated = separate_comments( $comments ); + + $this->assertCount( 1, $separated['review'] ); + $this->assertCount( 0, $separated['pings'] ); + + unregister_comment_type( 'review' ); + } +} diff --git a/tests/phpunit/tests/comment/types.php b/tests/phpunit/tests/comment/types.php index 54f8e2aa9ad33..5b83bb57ba63b 100644 --- a/tests/phpunit/tests/comment/types.php +++ b/tests/phpunit/tests/comment/types.php @@ -118,6 +118,22 @@ public function test_built_in_note_type_is_internal_and_non_public() { $this->assertFalse( $note->public ); } + /** + * @ticket 35214 + */ + public function test_built_in_ping_types_are_marked_as_pings() { + $this->assertTrue( get_comment_type_object( 'pingback' )->is_ping ); + $this->assertTrue( get_comment_type_object( 'trackback' )->is_ping ); + } + + /** + * @ticket 35214 + */ + public function test_built_in_non_ping_types_are_not_marked_as_pings() { + $this->assertFalse( get_comment_type_object( 'comment' )->is_ping ); + $this->assertFalse( get_comment_type_object( 'note' )->is_ping ); + } + /** * @ticket 35214 */ diff --git a/tests/phpunit/tests/comment/walker.php b/tests/phpunit/tests/comment/walker.php index 434b2c12b3d38..c012ef4465ec0 100644 --- a/tests/phpunit/tests/comment/walker.php +++ b/tests/phpunit/tests/comment/walker.php @@ -144,6 +144,35 @@ public function test_explicit_callback_takes_precedence_over_render_callback() { unregister_comment_type( 'review' ); } + /** + * A registered ping type renders with the compact ping markup when short_ping is on. + * + * @ticket 35214 + */ + public function test_registered_ping_type_renders_as_ping() { + register_comment_type( 'webmention', array( 'is_ping' => true ) ); + + $comment_id = self::factory()->comment->create( + array( + 'comment_post_ID' => $this->post_id, + 'comment_type' => 'webmention', + 'comment_content' => 'A webmention body', + 'comment_approved' => '1', + ) + ); + + $output = $this->render_comments( + array( get_comment( $comment_id ) ), + array( 'short_ping' => true ) + ); + + // The compact ping markup prints the "Pingback:" label and omits the comment body. + $this->assertStringContainsString( 'Pingback:', $output ); + $this->assertStringNotContainsString( 'A webmention body', $output ); + + unregister_comment_type( 'webmention' ); + } + /** * Built-in comment types without a render_callback render normally. * diff --git a/tests/phpunit/tests/comment/wpCommentType.php b/tests/phpunit/tests/comment/wpCommentType.php index 9dd8c3a37187b..1b8327a9836e8 100644 --- a/tests/phpunit/tests/comment/wpCommentType.php +++ b/tests/phpunit/tests/comment/wpCommentType.php @@ -25,6 +25,18 @@ public function test_instance_defaults() { $this->assertTrue( $comment_type->show_ui ); $this->assertFalse( $comment_type->hierarchical ); $this->assertNull( $comment_type->render_callback ); + $this->assertFalse( $comment_type->is_ping ); + } + + /** + * @ticket 35214 + * + * @covers ::set_props + */ + public function test_is_ping_is_stored() { + $comment_type = new WP_Comment_Type( 'foo', array( 'is_ping' => true ) ); + + $this->assertTrue( $comment_type->is_ping ); } /** From 3a5a4019ae7bce569d31a1d1e14752d66fe3355b Mon Sep 17 00:00:00 2001 From: adamsilverstein Date: Wed, 24 Jun 2026 20:30:10 -0700 Subject: [PATCH 03/14] Comments: Cover both branches of is_ping rendering in Walker_Comment. Add the complementary cases for the is_ping && short_ping guard: the built-in pingback still renders as a compact ping (regression guard for the move from hard-coded type strings to the is_ping flag), a ping type renders its full markup when short_ping is off, and a non-ping type is never rendered as a ping even with short_ping enabled. See #35214. --- tests/phpunit/tests/comment/walker.php | 83 ++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/tests/phpunit/tests/comment/walker.php b/tests/phpunit/tests/comment/walker.php index c012ef4465ec0..5259d2495fba9 100644 --- a/tests/phpunit/tests/comment/walker.php +++ b/tests/phpunit/tests/comment/walker.php @@ -173,6 +173,89 @@ public function test_registered_ping_type_renders_as_ping() { unregister_comment_type( 'webmention' ); } + /** + * The built-in pingback type still renders with the compact ping markup. + * + * Guards the refactor from hard-coded `pingback`/`trackback` string checks to + * the `is_ping` flag: built-in ping rendering must remain unchanged. + * + * @ticket 35214 + */ + public function test_built_in_pingback_still_renders_as_ping() { + $comment_id = self::factory()->comment->create( + array( + 'comment_post_ID' => $this->post_id, + 'comment_type' => 'pingback', + 'comment_content' => 'A pingback body', + 'comment_approved' => '1', + ) + ); + + $output = $this->render_comments( + array( get_comment( $comment_id ) ), + array( 'short_ping' => true ) + ); + + $this->assertStringContainsString( 'Pingback:', $output ); + $this->assertStringNotContainsString( 'A pingback body', $output ); + } + + /** + * A ping type renders its full markup (not the compact ping) when short_ping is off. + * + * @ticket 35214 + */ + public function test_ping_type_renders_full_markup_when_short_ping_disabled() { + register_comment_type( 'webmention', array( 'is_ping' => true ) ); + + $comment_id = self::factory()->comment->create( + array( + 'comment_post_ID' => $this->post_id, + 'comment_type' => 'webmention', + 'comment_content' => 'A webmention body', + 'comment_approved' => '1', + ) + ); + + $output = $this->render_comments( + array( get_comment( $comment_id ) ), + array( 'short_ping' => false ) + ); + + // With short_ping off the full markup is rendered, including the comment body. + $this->assertStringContainsString( 'A webmention body', $output ); + + unregister_comment_type( 'webmention' ); + } + + /** + * A non-ping type is never rendered as a ping, even with short_ping enabled. + * + * @ticket 35214 + */ + public function test_non_ping_type_is_not_rendered_as_ping_with_short_ping() { + register_comment_type( 'review' ); + + $comment_id = self::factory()->comment->create( + array( + 'comment_post_ID' => $this->post_id, + 'comment_type' => 'review', + 'comment_content' => 'A review body', + 'comment_approved' => '1', + ) + ); + + $output = $this->render_comments( + array( get_comment( $comment_id ) ), + array( 'short_ping' => true ) + ); + + $this->assertStringContainsString( 'A review body', $output ); + $this->assertStringNotContainsString( 'Pingback:', $output ); + + unregister_comment_type( 'review' ); + } + /** * Built-in comment types without a render_callback render normally. * From c803649d4dad097bb7c3ddda2e11ffe222c2ccce Mon Sep 17 00:00:00 2001 From: adamsilverstein Date: Sat, 11 Jul 2026 10:27:49 -0700 Subject: [PATCH 04/14] Comments: Document is_ping interactions and pin grouping edge cases. - The is_ping docs now state the 'short_ping' condition, that a registered 'render_callback' takes precedence over the compact ping markup, and the classic-theme-only scope. - Add the missing @since 7.1.0 changelog entries to separate_comments() and extend Walker_Comment::start_el()'s. - Update the wp_list_comments() 'type' argument doc: 'pings' now means every registered comment type with 'is_ping'. - Tests: pin the render_callback-beats-short_ping precedence, the unregistered-type bucket (no 'pings' membership, matching the old hard-coded behavior), and the legacy empty-string 'comment' bucket. Drop cleanup-only unregister calls now handled by the test framework. --- src/wp-includes/class-walker-comment.php | 3 +- src/wp-includes/class-wp-comment-type.php | 7 +++- src/wp-includes/comment-template.php | 6 ++- src/wp-includes/comment.php | 9 +++- .../tests/comment/separateComments.php | 31 ++++++++++++-- tests/phpunit/tests/comment/walker.php | 41 ++++++++++++++++--- 6 files changed, 82 insertions(+), 15 deletions(-) diff --git a/src/wp-includes/class-walker-comment.php b/src/wp-includes/class-walker-comment.php index 1f3c6ddfa7ce0..4487339f0c010 100644 --- a/src/wp-includes/class-walker-comment.php +++ b/src/wp-includes/class-walker-comment.php @@ -159,7 +159,8 @@ public function display_element( $element, &$children_elements, $max_depth, $dep * @since 5.9.0 Renamed `$comment` to `$data_object` and `$id` to `$current_object_id` * to match parent class for PHP 8 named parameter support. * @since 7.1.0 Comments of a registered comment type with a `render_callback` - * are rendered via that callback. + * are rendered via that callback, and short-ping rendering is + * driven by the comment type's `is_ping` property. * * @see Walker::start_el() * @see wp_list_comments() diff --git a/src/wp-includes/class-wp-comment-type.php b/src/wp-includes/class-wp-comment-type.php index 4f5eee3e935aa..9ae1e56222899 100644 --- a/src/wp-includes/class-wp-comment-type.php +++ b/src/wp-includes/class-wp-comment-type.php @@ -133,8 +133,11 @@ final class WP_Comment_Type { * rather than a human-authored comment. * * Ping types (such as `pingback` and `trackback`) are grouped together by - * {@see separate_comments()} and rendered with the compact ping markup by - * {@see Walker_Comment}. Default false. + * {@see separate_comments()} and, when the `short_ping` argument of + * wp_list_comments() is true, rendered with the compact ping markup by + * {@see Walker_Comment}. A registered `render_callback` takes precedence over + * the ping markup. Like `render_callback`, the rendering effects apply only to + * classic themes; block themes do not use Walker_Comment. Default false. * * @since 7.1.0 * @var bool diff --git a/src/wp-includes/comment-template.php b/src/wp-includes/comment-template.php index ce40ee014ac34..5c91c32042c75 100644 --- a/src/wp-includes/comment-template.php +++ b/src/wp-includes/comment-template.php @@ -2230,8 +2230,10 @@ function _get_comment_reply_id( $post = null ) { * 'div' will result in no additional list markup. Default 'ul'. * @type callable $callback Callback function to use. Default null. * @type callable $end-callback Callback function to use at the end. Default null. - * @type string $type Type of comments to list. Accepts 'all', 'comment', - * 'pingback', 'trackback', 'pings'. Default 'all'. + * @type string $type Type of comments to list. Accepts 'all', any comment type + * slug, or 'pings' (the comments of every registered comment + * type with the 'is_ping' property, which includes pingbacks + * and trackbacks). Default 'all'. * @type int $page Page ID to list comments for. Default empty. * @type int $per_page Number of comments to list per page. Default empty. * @type int $avatar_size Height and width dimensions of the avatar size. Default 32. diff --git a/src/wp-includes/comment.php b/src/wp-includes/comment.php index e7fd684869c2e..bf441a27e8a92 100644 --- a/src/wp-includes/comment.php +++ b/src/wp-includes/comment.php @@ -378,8 +378,10 @@ function create_initial_comment_types() { * default query exclusions in the future. Default false. * @type bool $is_ping Whether the comment type represents a ping (a notification * from another site) rather than a human-authored comment. - * Ping types are grouped together by separate_comments() and - * rendered with compact ping markup by Walker_Comment. + * Ping types are grouped together by separate_comments() and, + * when wp_list_comments() is called with 'short_ping', rendered + * with compact ping markup by Walker_Comment. A registered + * 'render_callback' takes precedence over the ping markup. * Default false. * @type callable $render_callback Callback used to render a comment of this type in comment * lists. Receives the same arguments as the `callback` argument @@ -1288,6 +1290,9 @@ function wp_check_comment_flood( $is_flood, $ip, $email, $date, $avoid_die = fal * Separates an array of comments into an array keyed by comment_type. * * @since 2.7.0 + * @since 7.1.0 The 'pings' group contains the comments of every registered + * comment type with the `is_ping` property, rather than only + * pingbacks and trackbacks. * * @param WP_Comment[] $comments Array of comments. * @return array Array of comments keyed by comment type. diff --git a/tests/phpunit/tests/comment/separateComments.php b/tests/phpunit/tests/comment/separateComments.php index b9145f88a836d..4bd1cce490673 100644 --- a/tests/phpunit/tests/comment/separateComments.php +++ b/tests/phpunit/tests/comment/separateComments.php @@ -84,8 +84,6 @@ public function test_registered_ping_type_is_grouped_into_pings_bucket() { $this->assertCount( 1, $separated['webmention'] ); $this->assertCount( 1, $separated['pings'] ); - - unregister_comment_type( 'webmention' ); } /** @@ -102,7 +100,34 @@ public function test_non_ping_type_is_not_grouped_into_pings_bucket() { $this->assertCount( 1, $separated['review'] ); $this->assertCount( 0, $separated['pings'] ); + } + + /** + * An unregistered comment type gets its own bucket and stays out of 'pings', + * matching the previous hard-coded behavior. + * + * @ticket 35214 + */ + public function test_unregistered_type_gets_own_bucket_and_is_not_a_ping() { + $comments = array( $this->make_comment( 'webmention' ) ); + + $separated = separate_comments( $comments ); - unregister_comment_type( 'review' ); + $this->assertCount( 1, $separated['webmention'] ); + $this->assertCount( 0, $separated['pings'] ); + } + + /** + * A comment stored with the legacy empty string type lands in the 'comment' bucket. + * + * @ticket 35214 + */ + public function test_legacy_empty_type_lands_in_comment_bucket() { + $comments = array( $this->make_comment( '' ) ); + + $separated = separate_comments( $comments ); + + $this->assertCount( 1, $separated['comment'] ); + $this->assertCount( 0, $separated['pings'] ); } } diff --git a/tests/phpunit/tests/comment/walker.php b/tests/phpunit/tests/comment/walker.php index 05ff57a607ae0..39a4f9e7968fc 100644 --- a/tests/phpunit/tests/comment/walker.php +++ b/tests/phpunit/tests/comment/walker.php @@ -168,8 +168,6 @@ public function test_registered_ping_type_renders_as_ping() { // The compact ping markup prints the "Pingback:" label and omits the comment body. $this->assertStringContainsString( 'Pingback:', $output ); $this->assertStringNotContainsString( 'A webmention body', $output ); - - unregister_comment_type( 'webmention' ); } /** @@ -223,8 +221,6 @@ public function test_ping_type_renders_full_markup_when_short_ping_disabled() { // With short_ping off the full markup is rendered, including the comment body. $this->assertStringContainsString( 'A webmention body', $output ); - - unregister_comment_type( 'webmention' ); } /** @@ -251,8 +247,43 @@ public function test_non_ping_type_is_not_rendered_as_ping_with_short_ping() { $this->assertStringContainsString( 'A review body', $output ); $this->assertStringNotContainsString( 'Pingback:', $output ); + } + + /** + * A render_callback wins over the compact ping markup for ping types. + * + * This pins the precedence chain: explicit wp_list_comments() 'callback', + * then 'render_callback', then is_ping short-ping markup, then default markup. + * + * @ticket 35214 + */ + public function test_render_callback_takes_precedence_over_short_ping_markup() { + register_comment_type( + 'webmention', + array( + 'is_ping' => true, + 'render_callback' => static function ( $comment ) { + echo '
  • ' . esc_html( $comment->comment_content ); + }, + ) + ); - unregister_comment_type( 'review' ); + $comment_id = self::factory()->comment->create( + array( + 'comment_post_ID' => $this->post_id, + 'comment_type' => 'webmention', + 'comment_content' => 'A webmention body', + 'comment_approved' => '1', + ) + ); + + $output = $this->render_comments( + array( get_comment( $comment_id ) ), + array( 'short_ping' => true ) + ); + + $this->assertStringContainsString( '
  • A webmention body', $output ); + $this->assertStringNotContainsString( 'Pingback:', $output ); } /** From 306bb484140c428707da8ea17032d2fd59fcd928 Mon Sep 17 00:00:00 2001 From: adamsilverstein Date: Wed, 12 Aug 2026 17:24:32 -0700 Subject: [PATCH 05/14] Comments: Label a registered ping type with its own name in the compact markup. `Walker_Comment::ping()` prints a literal "Pingback:". That has been imprecise since 3.6, when trackbacks started using the same markup, but it was at least never wrong by more than one word. Now that any type can opt into ping grouping, a webmention rendering as "Pingback: example.com" is plainly wrong output for exactly the audience the flag is for. Use the type's `singular_name` for registered, non-built-in types, matching what `comment_type()` already does on this branch, and leave the string alone for the built-ins so their markup is byte-identical. The test that asserted the literal "Pingback:" for a webmention was pinning the bug, so replace it: one test for the compact shape (label, author link, no comment body), one for the label itself. Also add the end-to-end case the suite was missing - `wp_list_comments()` with `type => 'pings'` listing a registered ping type - which is where a theme actually meets the grouping change. --- src/wp-includes/class-walker-comment.php | 17 ++++- tests/phpunit/tests/comment/walker.php | 80 ++++++++++++++++++++++-- 2 files changed, 91 insertions(+), 6 deletions(-) diff --git a/src/wp-includes/class-walker-comment.php b/src/wp-includes/class-walker-comment.php index 4487339f0c010..23a921474a183 100644 --- a/src/wp-includes/class-walker-comment.php +++ b/src/wp-includes/class-walker-comment.php @@ -263,6 +263,7 @@ public function end_el( &$output, $data_object, $depth = 0, $args = array() ) { * Outputs a pingback comment. * * @since 3.6.0 + * @since 7.1.0 A registered, non-built-in ping type is labeled with its singular name. * * @see wp_list_comments() * @@ -272,10 +273,24 @@ public function end_el( &$output, $data_object, $depth = 0, $args = array() ) { */ protected function ping( $comment, $depth, $args ) { $tag = ( 'div' === $args['style'] ) ? 'div' : 'li'; + + /* + * The built-in ping types share the 'Pingback:' label - trackbacks have carried it + * since 3.6 - so their markup is unchanged. A registered ping type would be + * mislabeled by it, so use its own singular name instead, matching comment_type(). + */ + $comment_type_object = get_comment_type_object( $comment->comment_type ); + + if ( $comment_type_object && ! $comment_type_object->_builtin && isset( $comment_type_object->labels->singular_name ) ) { + /* translators: %s: Singular name of a registered comment type, e.g. "Webmention". */ + $label = sprintf( __( '%s:' ), esc_html( $comment_type_object->labels->singular_name ) ); + } else { + $label = __( 'Pingback:' ); + } ?> < id="comment-" >
    - ', '' ); ?> + ', '' ); ?>
    true ) ); + register_comment_type( + 'webmention', + array( + 'is_ping' => true, + 'labels' => array( 'singular_name' => 'Webmention' ), + ) + ); $comment_id = self::factory()->comment->create( array( @@ -239,9 +246,72 @@ public function test_registered_ping_type_renders_as_ping() { array( 'short_ping' => true ) ); - // The compact ping markup prints the "Pingback:" label and omits the comment body. - $this->assertStringContainsString( 'Pingback:', $output ); - $this->assertStringNotContainsString( 'A webmention body', $output ); + $this->assertStringContainsString( '
    ', $output, 'The compact ping markup should be used.' ); + $this->assertStringContainsString( 'class="url"', $output, 'The author link should be rendered.' ); + $this->assertStringNotContainsString( 'A webmention body', $output, 'The comment body should be omitted.' ); + } + + /** + * The user-visible end of the grouping change: a theme listing 'pings' gets the + * registered ping type alongside the built-ins, and nothing else. + * + * @ticket 35214 + */ + public function test_wp_list_comments_type_pings_lists_registered_ping_types() { + register_comment_type( 'webmention', array( 'is_ping' => true ) ); + + $comments = array(); + + foreach ( array( 'comment', 'pingback', 'webmention' ) as $comment_type ) { + $comments[] = get_comment( + self::factory()->comment->create( + array( + 'comment_post_ID' => $this->post_id, + 'comment_type' => $comment_type, + 'comment_author' => "Author of a $comment_type", + 'comment_approved' => '1', + ) + ) + ); + } + + $output = $this->render_comments( $comments, array( 'type' => 'pings' ) ); + + $this->assertStringContainsString( 'Author of a webmention', $output ); + $this->assertStringContainsString( 'Author of a pingback', $output ); + $this->assertStringNotContainsString( 'Author of a comment', $output ); + } + + /** + * The compact ping markup labels a registered type with its own singular name. The + * built-in "Pingback:" would be plainly wrong for anything else. + * + * @ticket 35214 + */ + public function test_registered_ping_type_renders_with_its_own_label() { + register_comment_type( + 'webmention', + array( + 'is_ping' => true, + 'labels' => array( 'singular_name' => 'Webmention' ), + ) + ); + + $comment_id = self::factory()->comment->create( + array( + 'comment_post_ID' => $this->post_id, + 'comment_type' => 'webmention', + 'comment_approved' => '1', + ) + ); + + $output = $this->render_comments( + array( get_comment( $comment_id ) ), + array( 'short_ping' => true ) + ); + + $this->assertStringContainsString( 'Webmention:', $output ); + $this->assertStringNotContainsString( 'Pingback:', $output ); } /** From 36dccee116a61fb5d527dfac8dc474a57b26bac9 Mon Sep 17 00:00:00 2001 From: adamsilverstein Date: Wed, 12 Aug 2026 17:24:54 -0700 Subject: [PATCH 06/14] Comments: Expand a 'pings' comment query to every registered ping type. `separate_comments()` and `wp_list_comments()` group pings by the `is_ping` flag, while `WP_Comment_Query` kept expanding the 'pings' token to 'pingback' and 'trackback'. A theme reading `$wp_query->comments_by_type['pings']` and a plugin calling `get_comments( array( 'type' => 'pings' ) )` would disagree about which comments are pings, so land the query side in the same release rather than ship that window. The resolved set goes into the comment query cache key. Which types are pings depends on what is registered, which changes when a plugin is activated, and the comment `last_changed` salt only moves when a comment does - so without this a 'pings' query would keep serving results cached before the type existed. Registration happens on 'init', so the built-in pair remains the fallback for callers that query before then. --- src/wp-includes/class-wp-comment-query.php | 63 +++++++++++++++- tests/phpunit/tests/comment/query.php | 84 ++++++++++++++++++++++ 2 files changed, 144 insertions(+), 3 deletions(-) diff --git a/src/wp-includes/class-wp-comment-query.php b/src/wp-includes/class-wp-comment-query.php index f80864d31c8bc..f19d7fc8ab43a 100644 --- a/src/wp-includes/class-wp-comment-query.php +++ b/src/wp-includes/class-wp-comment-query.php @@ -119,6 +119,17 @@ class WP_Comment_Query { */ public $max_num_pages = 0; + /** + * Comment types a 'pings' type token expands to. + * + * Resolved once per query in get_comments(), so that the set folded into the cache + * key is the same one get_comment_ids() builds the SQL from. Null until resolved. + * + * @since 7.1.0 + * @var string[]|null + */ + protected $ping_comment_types = null; + /** * Make private/protected methods readable for backward compatibility. * @@ -149,6 +160,7 @@ public function __call( $name, $arguments ) { * @since 4.9.0 Introduced the `$paged` argument. * @since 5.1.0 Introduced the `$meta_compare_key` argument. * @since 5.3.0 Introduced the `$meta_type_key` argument. + * @since 7.1.0 A `$type` of 'pings' expands to every comment type registered with `is_ping`. * * @param string|array $query { * Optional. Array or query string of comment query parameters. Default empty. @@ -251,7 +263,8 @@ public function __call( $name, $arguments ) { * 'approve' (`comment_status=1`), 'all', or a custom * comment status. Default 'all'. * @type string|string[] $type Include comments of a given type, or array of types. - * Accepts 'comment', 'pings' (includes 'pingback' and + * Accepts 'comment', 'pings' (every comment type registered + * with `is_ping`, which includes 'pingback' and * 'trackback'), or any custom type string. Default empty. * @type string[] $type__in Include comments from a given array of comment types. * Default empty. @@ -455,6 +468,21 @@ public function get_comments() { $_args = wp_array_slice_assoc( $this->query_vars, array_keys( $this->query_var_defaults ) ); unset( $_args['fields'], $_args['update_comment_meta_cache'], $_args['update_comment_post_cache'] ); + /* + * A 'pings' token expands to the registered ping types, which a plugin can change + * from one request to the next, so the resolved set belongs in the cache key. The + * comment last_changed salt only moves when a comment does, and would not catch it. + */ + $type_query_vars = array_merge( + (array) $this->query_vars['type'], + (array) $this->query_vars['type__in'], + (array) $this->query_vars['type__not_in'] + ); + + if ( in_array( 'pings', $type_query_vars, true ) ) { + $_args['ping_comment_types'] = $this->get_ping_comment_types(); + } + $key = md5( serialize( $_args ) ); $last_changed = wp_cache_get_last_changed( 'comment' ); @@ -806,8 +834,9 @@ protected function get_comment_ids() { break; case 'pings': - $comment_types[ $operator ][] = "'pingback'"; - $comment_types[ $operator ][] = "'trackback'"; + foreach ( $this->get_ping_comment_types() as $ping_type ) { + $comment_types[ $operator ][] = $wpdb->prepare( '%s', $ping_type ); + } break; default: @@ -1006,6 +1035,34 @@ protected function get_comment_ids() { } } + /** + * Resolves the comment types a 'pings' type token expands to. + * + * Matches how separate_comments() and wp_list_comments() group pings, so that a + * query for 'pings' returns the comments a theme would list under that heading. + * + * @since 7.1.0 + * + * @return string[] Comment type names. + */ + protected function get_ping_comment_types() { + if ( null === $this->ping_comment_types ) { + $ping_types = get_comment_types( array( 'is_ping' => true ), 'names' ); + + /* + * The built-in ping types, for the window before comment types are registered + * on 'init' and for any install running without the registry. + */ + if ( ! $ping_types ) { + $ping_types = array( 'pingback', 'trackback' ); + } + + $this->ping_comment_types = array_values( $ping_types ); + } + + return $this->ping_comment_types; + } + /** * Populates found_comments and max_num_pages properties for the current * query if the limit clause was used. diff --git a/tests/phpunit/tests/comment/query.php b/tests/phpunit/tests/comment/query.php index dc870a78ae494..c54d1d055d8e7 100644 --- a/tests/phpunit/tests/comment/query.php +++ b/tests/phpunit/tests/comment/query.php @@ -342,6 +342,90 @@ public function test_query_type_pings() { $this->assertSameSets( array( $c2, $c3 ), $found ); } + /** + * A 'pings' query returns every registered ping type, matching what + * separate_comments() groups under the same name. + * + * @ticket 35214 + * + * @covers WP_Comment_Query::query + */ + public function test_query_type_pings_includes_registered_ping_types() { + register_comment_type( 'webmention', array( 'is_ping' => true ) ); + + $pingback = self::factory()->comment->create( + array( + 'comment_post_ID' => self::$post_id, + 'comment_approved' => '1', + 'comment_type' => 'pingback', + ) + ); + $mention = self::factory()->comment->create( + array( + 'comment_post_ID' => self::$post_id, + 'comment_approved' => '1', + 'comment_type' => 'webmention', + ) + ); + self::factory()->comment->create( + array( + 'comment_post_ID' => self::$post_id, + 'comment_approved' => '1', + 'comment_type' => 'review', + ) + ); + + $q = new WP_Comment_Query(); + $found = $q->query( + array( + 'type' => 'pings', + 'fields' => 'ids', + ) + ); + + $this->assertSameSets( array( $pingback, $mention ), $found ); + } + + /** + * The registered ping types are part of the 'pings' cache key. Otherwise a plugin + * registering a ping type would keep serving results cached before it existed, since + * the comment last_changed salt only moves when a comment does. + * + * @ticket 35214 + * + * @covers WP_Comment_Query::get_comments + */ + public function test_query_type_pings_is_not_served_from_a_stale_cache() { + $pingback = self::factory()->comment->create( + array( + 'comment_post_ID' => self::$post_id, + 'comment_approved' => '1', + 'comment_type' => 'pingback', + ) + ); + $mention = self::factory()->comment->create( + array( + 'comment_post_ID' => self::$post_id, + 'comment_approved' => '1', + 'comment_type' => 'webmention', + ) + ); + + $args = array( + 'type' => 'pings', + 'fields' => 'ids', + ); + + $before = ( new WP_Comment_Query() )->query( $args ); + + register_comment_type( 'webmention', array( 'is_ping' => true ) ); + + $after = ( new WP_Comment_Query() )->query( $args ); + + $this->assertSameSets( array( $pingback ), $before, 'Before registration the type is not a ping.' ); + $this->assertSameSets( array( $pingback, $mention ), $after, 'After registration it is.' ); + } + /** * Comments and custom * From 9aea3a5e5aeecf7b670f21e14208c0d3fd47920a Mon Sep 17 00:00:00 2001 From: adamsilverstein Date: Wed, 12 Aug 2026 17:24:54 -0700 Subject: [PATCH 07/14] Docs: Describe what the `is_ping` flag does and does not reach. The flag's docs covered grouping and rendering but not the two things a plugin author most needs to know before using it: that the compact ping markup now labels the comment with the type's `singular_name`, so a ping type ought to register one, and that the flag stops at display and grouping. Validation, default comment status, and notification emails still key on the 'pingback' and 'trackback' type names, so a custom ping type gains none of their handling - and, just as importantly, none of their bypasses. --- src/wp-includes/class-wp-comment-type.php | 18 +++++++++++++----- src/wp-includes/comment.php | 12 ++++++++---- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/wp-includes/class-wp-comment-type.php b/src/wp-includes/class-wp-comment-type.php index 79ab410b81589..4f8b6095e796d 100644 --- a/src/wp-includes/class-wp-comment-type.php +++ b/src/wp-includes/class-wp-comment-type.php @@ -146,11 +146,19 @@ final class WP_Comment_Type { * rather than a human-authored comment. * * Ping types (such as `pingback` and `trackback`) are grouped together by - * {@see separate_comments()} and, when the `short_ping` argument of - * wp_list_comments() is true, rendered with the compact ping markup by - * {@see Walker_Comment}. A registered `render_callback` takes precedence over - * the ping markup. Like `render_callback`, the rendering effects apply only to - * classic themes; block themes do not use Walker_Comment. Default false. + * {@see separate_comments()} and returned by a `'pings'` type query in + * {@see WP_Comment_Query}. When the `short_ping` argument of wp_list_comments() + * is true, they are rendered with the compact ping markup by + * {@see Walker_Comment}, which labels the comment with the type's + * `singular_name`, so a ping type should register one. A registered + * `render_callback` takes precedence over the ping markup. Like + * `render_callback`, the rendering effects apply only to classic themes; block + * themes do not use Walker_Comment. + * + * The flag drives grouping and display only. It does not change how a comment of + * this type is validated, moderated, or notified about: the pingback and trackback + * paths in {@see check_comment()}, {@see get_default_comment_status()}, and the + * notification emails are still keyed to those two type names. Default false. * * @since 7.1.0 * @var bool diff --git a/src/wp-includes/comment.php b/src/wp-includes/comment.php index 9044c3e0e2cd5..2a1b8e09890d3 100644 --- a/src/wp-includes/comment.php +++ b/src/wp-includes/comment.php @@ -453,10 +453,14 @@ function create_initial_comment_types() { * Default false. * @type bool $is_ping Whether the comment type represents a ping (a notification * from another site) rather than a human-authored comment. - * Ping types are grouped together by separate_comments() and, - * when wp_list_comments() is called with 'short_ping', rendered - * with compact ping markup by Walker_Comment. A registered - * 'render_callback' takes precedence over the ping markup. + * Ping types are grouped together by separate_comments(), + * returned by a 'pings' type query, and, when + * wp_list_comments() is called with 'short_ping', rendered + * with compact ping markup by Walker_Comment, labeled with the + * type's singular name. A registered 'render_callback' takes + * precedence over the ping markup. The flag drives grouping + * and display only; validation, moderation, and notification + * still key on the 'pingback' and 'trackback' type names. * Default false. * @type callable $render_callback Callback used to render a comment of this type in comment * lists. Receives the same arguments as the `callback` argument From d72ec04c8990f4b042dae0edf8da95793eff7781 Mon Sep 17 00:00:00 2001 From: adamsilverstein Date: Sun, 23 Aug 2026 08:49:22 -0700 Subject: [PATCH 08/14] Comments: Fold the resolved ping types into the descendant cache keys. get_comments() folds the registered ping types into its cache key so a plugin registering a ping type invalidates 'pings' results, but fill_descendants() built its per-parent child ID keys from the raw query vars alone. The cached child lists are filtered by the resolved ping set, so after registering a new ping type a threaded 'pings' query returned the updated top-level comments while still hiding the new type's children until something bumped the comment last_changed salt - indefinitely, under a persistent object cache. Extract the key construction into a shared get_cache_key_args() helper used by both paths so they cannot drift apart again. As a side effect the descendant keys now also drop 'fields' and the two update_*_cache flags, which never affected which children exist. See #35214. --- src/wp-includes/class-wp-comment-query.php | 62 +++++++++++++--------- tests/phpunit/tests/comment/query.php | 51 ++++++++++++++++++ 2 files changed, 88 insertions(+), 25 deletions(-) diff --git a/src/wp-includes/class-wp-comment-query.php b/src/wp-includes/class-wp-comment-query.php index f19d7fc8ab43a..1496f6a0955aa 100644 --- a/src/wp-includes/class-wp-comment-query.php +++ b/src/wp-includes/class-wp-comment-query.php @@ -461,29 +461,7 @@ public function get_comments() { return $comment_data; } - /* - * Only use the args defined in the query_var_defaults to compute the key, - * but ignore 'fields', 'update_comment_meta_cache', 'update_comment_post_cache' which does not affect query results. - */ - $_args = wp_array_slice_assoc( $this->query_vars, array_keys( $this->query_var_defaults ) ); - unset( $_args['fields'], $_args['update_comment_meta_cache'], $_args['update_comment_post_cache'] ); - - /* - * A 'pings' token expands to the registered ping types, which a plugin can change - * from one request to the next, so the resolved set belongs in the cache key. The - * comment last_changed salt only moves when a comment does, and would not catch it. - */ - $type_query_vars = array_merge( - (array) $this->query_vars['type'], - (array) $this->query_vars['type__in'], - (array) $this->query_vars['type__not_in'] - ); - - if ( in_array( 'pings', $type_query_vars, true ) ) { - $_args['ping_comment_types'] = $this->get_ping_comment_types(); - } - - $key = md5( serialize( $_args ) ); + $key = md5( serialize( $this->get_cache_key_args() ) ); $last_changed = wp_cache_get_last_changed( 'comment' ); $cache_key = "get_comments:$key"; @@ -1035,13 +1013,47 @@ protected function get_comment_ids() { } } + /** + * Builds the normalized set of query vars that comment query cache keys hash. + * + * Only uses the args defined in the query_var_defaults, ignoring 'fields', + * 'update_comment_meta_cache', and 'update_comment_post_cache', which do not + * affect query results. + * + * A 'pings' token expands to the registered ping types, which a plugin can change + * from one request to the next, so the resolved set belongs in the cache key. The + * comment last_changed salt only moves when a comment does, and would not catch + * it. Both the main query cache in get_comments() and the per-parent descendant + * caches in fill_descendants() hash these args, so the two cannot disagree. + * + * @since 7.2.0 + * + * @return array Query vars to hash into a cache key. + */ + protected function get_cache_key_args() { + $_args = wp_array_slice_assoc( $this->query_vars, array_keys( $this->query_var_defaults ) ); + unset( $_args['fields'], $_args['update_comment_meta_cache'], $_args['update_comment_post_cache'] ); + + $type_query_vars = array_merge( + (array) $this->query_vars['type'], + (array) $this->query_vars['type__in'], + (array) $this->query_vars['type__not_in'] + ); + + if ( in_array( 'pings', $type_query_vars, true ) ) { + $_args['ping_comment_types'] = $this->get_ping_comment_types(); + } + + return $_args; + } + /** * Resolves the comment types a 'pings' type token expands to. * * Matches how separate_comments() and wp_list_comments() group pings, so that a * query for 'pings' returns the comments a theme would list under that heading. * - * @since 7.1.0 + * @since 7.2.0 * * @return string[] Comment type names. */ @@ -1105,7 +1117,7 @@ protected function fill_descendants( $comments ) { 0 => wp_list_pluck( $comments, 'comment_ID' ), ); - $key = md5( serialize( wp_array_slice_assoc( $this->query_vars, array_keys( $this->query_var_defaults ) ) ) ); + $key = md5( serialize( $this->get_cache_key_args() ) ); $last_changed = wp_cache_get_last_changed( 'comment' ); // Fetch an entire level of the descendant tree at a time. diff --git a/tests/phpunit/tests/comment/query.php b/tests/phpunit/tests/comment/query.php index c54d1d055d8e7..6cc7ba5d384c1 100644 --- a/tests/phpunit/tests/comment/query.php +++ b/tests/phpunit/tests/comment/query.php @@ -426,6 +426,57 @@ public function test_query_type_pings_is_not_served_from_a_stale_cache() { $this->assertSameSets( array( $pingback, $mention ), $after, 'After registration it is.' ); } + /** + * The resolved ping set is part of the per-parent descendant cache keys too. + * Otherwise a threaded 'pings' query run before a ping type existed would keep + * serving that parent's children from the stale child ID cache even though the + * top-level results update. + * + * @ticket 35214 + * + * @covers WP_Comment_Query::fill_descendants + */ + public function test_threaded_pings_descendants_are_not_served_from_a_stale_cache() { + $pingback = self::factory()->comment->create( + array( + 'comment_post_ID' => self::$post_id, + 'comment_approved' => '1', + 'comment_type' => 'pingback', + ) + ); + $child = self::factory()->comment->create( + array( + 'comment_post_ID' => self::$post_id, + 'comment_approved' => '1', + 'comment_type' => 'webmention', + 'comment_parent' => $pingback, + ) + ); + + $args = array( + 'type' => 'pings', + 'hierarchical' => 'threaded', + 'post_id' => self::$post_id, + ); + + $before = ( new WP_Comment_Query() )->query( $args ); + + register_comment_type( 'webmention', array( 'is_ping' => true ) ); + + $after = ( new WP_Comment_Query() )->query( $args ); + + $this->assertSame( + array(), + $before[ $pingback ]->get_children(), + 'Before registration the child type is not a ping, so the parent has no children.' + ); + $this->assertSame( + array( $child ), + array_values( array_map( 'intval', wp_list_pluck( $after[ $pingback ]->get_children(), 'comment_ID' ) ) ), + 'After registration the child should not be hidden by a stale descendant cache.' + ); + } + /** * Comments and custom * From 32661c63f3f473f44869decb8460d85dfda83649 Mon Sep 17 00:00:00 2001 From: adamsilverstein Date: Sun, 23 Aug 2026 08:50:23 -0700 Subject: [PATCH 09/14] Comments: Keep 'Pingback:' for ping types registered without labels. Every registered comment type gets the default 'Comment' singular name from set_props(), so the singular-name guard in Walker_Comment::ping() could never fail and a ping type registered without labels rendered as 'Comment:' - a worse mislabel than the 'Pingback:' it replaced. Fall back to 'Pingback:' whenever the type inherited the default singular name, and pin it with a test (the existing suite only registered ping types that supplied a label). Also give the '%s:' string a translator context, matching the existing '%s:' prefixes in post-template.php and general-template.php so it does not merge with unrelated uses in the POT. See #35214. --- src/wp-includes/class-walker-comment.php | 14 +++++++++--- tests/phpunit/tests/comment/walker.php | 27 ++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/src/wp-includes/class-walker-comment.php b/src/wp-includes/class-walker-comment.php index 3c40f6afe285f..bd49ec3872c66 100644 --- a/src/wp-includes/class-walker-comment.php +++ b/src/wp-includes/class-walker-comment.php @@ -278,12 +278,20 @@ protected function ping( $comment, $depth, $args ) { * The built-in ping types share the 'Pingback:' label - trackbacks have carried it * since 3.6 - so their markup is unchanged. A registered ping type would be * mislabeled by it, so use its own singular name instead, matching comment_type(). + * A ping type registered without labels inherits the default 'Comment' singular + * name, which is even more wrong for a ping, so such types keep 'Pingback:' too. */ $comment_type_object = get_comment_type_object( $comment->comment_type ); - - if ( $comment_type_object && ! $comment_type_object->_builtin && isset( $comment_type_object->labels->singular_name ) ) { + $default_labels = WP_Comment_Type::get_default_labels(); + + if ( + $comment_type_object + && ! $comment_type_object->_builtin + && isset( $comment_type_object->labels->singular_name ) + && $default_labels['singular_name'][0] !== $comment_type_object->labels->singular_name + ) { /* translators: %s: Singular name of a registered comment type, e.g. "Webmention". */ - $label = sprintf( __( '%s:' ), esc_html( $comment_type_object->labels->singular_name ) ); + $label = sprintf( _x( '%s:', 'comment type label' ), esc_html( $comment_type_object->labels->singular_name ) ); } else { $label = __( 'Pingback:' ); } diff --git a/tests/phpunit/tests/comment/walker.php b/tests/phpunit/tests/comment/walker.php index 2f18dabff5658..5cb68b774f940 100644 --- a/tests/phpunit/tests/comment/walker.php +++ b/tests/phpunit/tests/comment/walker.php @@ -314,6 +314,33 @@ public function test_registered_ping_type_renders_with_its_own_label() { $this->assertStringNotContainsString( 'Pingback:', $output ); } + /** + * A ping type registered without labels inherits the default 'Comment' singular name, + * which is even more wrong for a ping than 'Pingback:'. The compact markup falls back + * to 'Pingback:' rather than labeling a ping 'Comment:'. + * + * @ticket 35214 + */ + public function test_ping_type_without_labels_falls_back_to_pingback_label() { + register_comment_type( 'webmention', array( 'is_ping' => true ) ); + + $comment_id = self::factory()->comment->create( + array( + 'comment_post_ID' => $this->post_id, + 'comment_type' => 'webmention', + 'comment_approved' => '1', + ) + ); + + $output = $this->render_comments( + array( get_comment( $comment_id ) ), + array( 'short_ping' => true ) + ); + + $this->assertStringContainsString( 'Pingback:', $output ); + $this->assertStringNotContainsString( 'Comment:', $output ); + } + /** * The built-in pingback type still renders with the compact ping markup. * From c52070c03dc993a27a2080b442194d6190e5cd0f Mon Sep 17 00:00:00 2001 From: adamsilverstein Date: Sun, 23 Aug 2026 08:52:25 -0700 Subject: [PATCH 10/14] Comments: Stamp the is_ping work for 7.2.0 and settle its edges. The branch was written while trunk was 7.1-alpha; trunk is now 7.2-alpha, so the six @since tags this branch adds pointed at a release that will never contain them. Beyond the stamps, resolve the smaller review findings around the 'pings' expansion: reset the memoized ping type set in parse_query() so reusing one WP_Comment_Query instance cannot build SQL and cache keys from a set resolved before a registration change; correct the empty-registry fallback rationale (comment types are registered directly in wp-settings.php, not on 'init', so only a partial bootstrap hits it); update the two get_page_of_comment() docs that still described 'pings' as trackbacks and pingbacks; and repair the test docblocks displaced when the ping tests were inserted between test_comment_type_exists() and its annotations. See #35214. --- src/wp-includes/class-walker-comment.php | 2 +- src/wp-includes/class-wp-comment-query.php | 16 ++++++++++------ src/wp-includes/class-wp-comment-type.php | 2 +- src/wp-includes/comment.php | 8 +++++--- tests/phpunit/tests/comment/types.php | 6 +++++- 5 files changed, 22 insertions(+), 12 deletions(-) diff --git a/src/wp-includes/class-walker-comment.php b/src/wp-includes/class-walker-comment.php index bd49ec3872c66..fb8e0a9eb5dce 100644 --- a/src/wp-includes/class-walker-comment.php +++ b/src/wp-includes/class-walker-comment.php @@ -263,7 +263,7 @@ public function end_el( &$output, $data_object, $depth = 0, $args = array() ) { * Outputs a pingback comment. * * @since 3.6.0 - * @since 7.1.0 A registered, non-built-in ping type is labeled with its singular name. + * @since 7.2.0 A registered, non-built-in ping type is labeled with its singular name. * * @see wp_list_comments() * diff --git a/src/wp-includes/class-wp-comment-query.php b/src/wp-includes/class-wp-comment-query.php index 1496f6a0955aa..32f0a2e5a1b15 100644 --- a/src/wp-includes/class-wp-comment-query.php +++ b/src/wp-includes/class-wp-comment-query.php @@ -122,10 +122,11 @@ class WP_Comment_Query { /** * Comment types a 'pings' type token expands to. * - * Resolved once per query in get_comments(), so that the set folded into the cache - * key is the same one get_comment_ids() builds the SQL from. Null until resolved. + * Resolved lazily, once per parsed query, so that the set folded into the cache + * keys is the same one get_comment_ids() builds the SQL from. Reset by + * parse_query(); null until resolved. * - * @since 7.1.0 + * @since 7.2.0 * @var string[]|null */ protected $ping_comment_types = null; @@ -160,7 +161,7 @@ public function __call( $name, $arguments ) { * @since 4.9.0 Introduced the `$paged` argument. * @since 5.1.0 Introduced the `$meta_compare_key` argument. * @since 5.3.0 Introduced the `$meta_type_key` argument. - * @since 7.1.0 A `$type` of 'pings' expands to every comment type registered with `is_ping`. + * @since 7.2.0 A `$type` of 'pings' expands to every comment type registered with `is_ping`. * * @param string|array $query { * Optional. Array or query string of comment query parameters. Default empty. @@ -356,6 +357,9 @@ public function parse_query( $query = '' ) { $this->query_vars = wp_parse_args( $query, $this->query_var_defaults ); + // Re-resolve the ping types per query, in case the registry changed in between. + $this->ping_comment_types = null; + /** * Fires after the comment query vars have been parsed. * @@ -1062,8 +1066,8 @@ protected function get_ping_comment_types() { $ping_types = get_comment_types( array( 'is_ping' => true ), 'names' ); /* - * The built-in ping types, for the window before comment types are registered - * on 'init' and for any install running without the registry. + * The built-in ping types, for queries that run before create_initial_comment_types() + * in a partial bootstrap and for any install running without the registry. */ if ( ! $ping_types ) { $ping_types = array( 'pingback', 'trackback' ); diff --git a/src/wp-includes/class-wp-comment-type.php b/src/wp-includes/class-wp-comment-type.php index 7394aee541ed5..a0d2a50baaaa8 100644 --- a/src/wp-includes/class-wp-comment-type.php +++ b/src/wp-includes/class-wp-comment-type.php @@ -160,7 +160,7 @@ final class WP_Comment_Type { * paths in {@see check_comment()}, {@see get_default_comment_status()}, and the * notification emails are still keyed to those two type names. Default false. * - * @since 7.1.0 + * @since 7.2.0 * @var bool */ public $is_ping = false; diff --git a/src/wp-includes/comment.php b/src/wp-includes/comment.php index f10804c818a5c..125bfda12295a 100644 --- a/src/wp-includes/comment.php +++ b/src/wp-includes/comment.php @@ -1400,7 +1400,7 @@ function wp_check_comment_flood( $is_flood, $ip, $email, $date, $avoid_die = fal * Separates an array of comments into an array keyed by comment_type. * * @since 2.7.0 - * @since 7.1.0 The 'pings' group contains the comments of every registered + * @since 7.2.0 The 'pings' group contains the comments of every registered * comment type with the `is_ping` property, rather than only * pingbacks and trackbacks. * @@ -1508,7 +1508,8 @@ function get_comment_pages_count( $comments = null, $per_page = null, $threaded * * @type string $type Limit paginated comments to those matching a given type. * Accepts 'comment', 'trackback', 'pingback', 'pings' - * (trackbacks and pingbacks), or 'all'. Default 'all'. + * (the comments of every registered comment type with + * `is_ping`), or 'all'. Default '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 @@ -1608,7 +1609,8 @@ function get_page_of_comment( $comment_id, $args = array() ) { * * @type string $type Limit paginated comments to those matching a given type. * Accepts 'comment', 'trackback', 'pingback', 'pings' - * (trackbacks and pingbacks), or 'all'. Default 'all'. + * (the comments of every registered comment type with + * `is_ping`), or 'all'. Default 'all'. * @type int $post_id ID of the post. * @type string $fields Comment fields to return. * @type bool $count Whether to return a comment count (true) or array diff --git a/tests/phpunit/tests/comment/types.php b/tests/phpunit/tests/comment/types.php index d104f5a90b56d..a088710fa51ad 100644 --- a/tests/phpunit/tests/comment/types.php +++ b/tests/phpunit/tests/comment/types.php @@ -102,7 +102,7 @@ public function test_built_in_note_type_is_internal_and_non_public() { /** * @ticket 35214 * - * @covers ::comment_type_exists + * @covers ::create_initial_comment_types */ public function test_built_in_ping_types_are_marked_as_pings() { $this->assertTrue( get_comment_type_object( 'pingback' )->is_ping ); @@ -111,6 +111,8 @@ public function test_built_in_ping_types_are_marked_as_pings() { /** * @ticket 35214 + * + * @covers ::create_initial_comment_types */ public function test_built_in_non_ping_types_are_not_marked_as_pings() { $this->assertFalse( get_comment_type_object( 'comment' )->is_ping ); @@ -119,6 +121,8 @@ public function test_built_in_non_ping_types_are_not_marked_as_pings() { /** * @ticket 35214 + * + * @covers ::comment_type_exists */ public function test_comment_type_exists() { $this->assertFalse( comment_type_exists( 'foo' ) ); From e8fa4d09afa42be574865e7bfdc6e30c6b817335 Mon Sep 17 00:00:00 2001 From: adamsilverstein Date: Sun, 8 Nov 2026 09:24:26 -0500 Subject: [PATCH 11/14] Comments: Tighten the `is_ping` documentation. The docblocks and inline comments added for the `is_ping` flag narrated design history and repeated the same rationale in several places. Trim them to what a caller needs: which surfaces the flag drives, that `render_callback` still takes precedence, and that validation and notifications still key on the 'pingback' and 'trackback' names. No functional change. See #35214. --- src/wp-includes/class-walker-comment.php | 9 ++---- src/wp-includes/class-wp-comment-query.php | 36 ++++++++-------------- src/wp-includes/class-wp-comment-type.php | 23 ++++---------- src/wp-includes/comment.php | 22 +++++-------- 4 files changed, 29 insertions(+), 61 deletions(-) diff --git a/src/wp-includes/class-walker-comment.php b/src/wp-includes/class-walker-comment.php index fb8e0a9eb5dce..132dd92e60cf9 100644 --- a/src/wp-includes/class-walker-comment.php +++ b/src/wp-includes/class-walker-comment.php @@ -263,7 +263,7 @@ public function end_el( &$output, $data_object, $depth = 0, $args = array() ) { * Outputs a pingback comment. * * @since 3.6.0 - * @since 7.2.0 A registered, non-built-in ping type is labeled with its singular name. + * @since 7.2.0 A registered ping type with its own singular name is labeled with it. * * @see wp_list_comments() * @@ -275,11 +275,8 @@ protected function ping( $comment, $depth, $args ) { $tag = ( 'div' === $args['style'] ) ? 'div' : 'li'; /* - * The built-in ping types share the 'Pingback:' label - trackbacks have carried it - * since 3.6 - so their markup is unchanged. A registered ping type would be - * mislabeled by it, so use its own singular name instead, matching comment_type(). - * A ping type registered without labels inherits the default 'Comment' singular - * name, which is even more wrong for a ping, so such types keep 'Pingback:' too. + * Built-in types keep the 'Pingback:' label they have always used. So does a + * registered type without its own singular name, which would read 'Comment:'. */ $comment_type_object = get_comment_type_object( $comment->comment_type ); $default_labels = WP_Comment_Type::get_default_labels(); diff --git a/src/wp-includes/class-wp-comment-query.php b/src/wp-includes/class-wp-comment-query.php index 1093106450a22..9863afa3a74a4 100644 --- a/src/wp-includes/class-wp-comment-query.php +++ b/src/wp-includes/class-wp-comment-query.php @@ -120,11 +120,9 @@ class WP_Comment_Query { public $max_num_pages = 0; /** - * Comment types a 'pings' type token expands to. + * Comment types a 'pings' type token expands to, or null until resolved. * - * Resolved lazily, once per parsed query, so that the set folded into the cache - * keys is the same one get_comment_ids() builds the SQL from. Reset by - * parse_query(); null until resolved. + * Resolved once per parsed query so the cache keys and the SQL use the same set. * * @since 7.2.0 * @var string[]|null @@ -265,8 +263,8 @@ public function __call( $name, $arguments ) { * comment status. Default 'all'. * @type string|string[] $type Include comments of a given type, or array of types. * Accepts 'comment', 'pings' (every comment type registered - * with `is_ping`, which includes 'pingback' and - * 'trackback'), or any custom type string. Default empty. + * with `is_ping`, including 'pingback' and 'trackback'), + * or any custom type string. Default empty. * @type string[] $type__in Include comments from a given array of comment types. * Default empty. * @type string[] $type__not_in Exclude comments from a given array of comment types. @@ -357,7 +355,7 @@ public function parse_query( $query = '' ) { $this->query_vars = wp_parse_args( $query, $this->query_var_defaults ); - // Re-resolve the ping types per query, in case the registry changed in between. + // The ping type registry may have changed since the last query. $this->ping_comment_types = null; /** @@ -1023,17 +1021,11 @@ protected function get_comment_ids() { } /** - * Builds the normalized set of query vars that comment query cache keys hash. + * Returns the query vars that get_comments() and fill_descendants() hash into cache keys. * - * Only uses the args defined in the query_var_defaults, ignoring 'fields', - * 'update_comment_meta_cache', and 'update_comment_post_cache', which do not - * affect query results. - * - * A 'pings' token expands to the registered ping types, which a plugin can change - * from one request to the next, so the resolved set belongs in the cache key. The - * comment last_changed salt only moves when a comment does, and would not catch - * it. Both the main query cache in get_comments() and the per-parent descendant - * caches in fill_descendants() hash these args, so the two cannot disagree. + * Ignores 'fields', 'update_comment_meta_cache', and 'update_comment_post_cache', + * which do not affect query results. Adds the resolved ping types when 'pings' is + * queried, since registering a ping type does not bump the comment last_changed value. * * @since 7.2.0 * @@ -1057,10 +1049,9 @@ protected function get_cache_key_args() { } /** - * Resolves the comment types a 'pings' type token expands to. + * Returns the comment types a 'pings' type token expands to. * - * Matches how separate_comments() and wp_list_comments() group pings, so that a - * query for 'pings' returns the comments a theme would list under that heading. + * Matches the 'pings' group built by separate_comments(). * * @since 7.2.0 * @@ -1070,10 +1061,7 @@ protected function get_ping_comment_types() { if ( null === $this->ping_comment_types ) { $ping_types = get_comment_types( array( 'is_ping' => true ), 'names' ); - /* - * The built-in ping types, for queries that run before create_initial_comment_types() - * in a partial bootstrap and for any install running without the registry. - */ + // Queries can run before create_initial_comment_types() registers the built-ins. if ( ! $ping_types ) { $ping_types = array( 'pingback', 'trackback' ); } diff --git a/src/wp-includes/class-wp-comment-type.php b/src/wp-includes/class-wp-comment-type.php index 96fcaefc056f4..b4b2944e7b777 100644 --- a/src/wp-includes/class-wp-comment-type.php +++ b/src/wp-includes/class-wp-comment-type.php @@ -142,23 +142,12 @@ final class WP_Comment_Type { public $render_callback = null; /** - * Whether the comment type represents a ping (a notification from another site) - * rather than a human-authored comment. - * - * Ping types (such as `pingback` and `trackback`) are grouped together by - * {@see separate_comments()} and returned by a `'pings'` type query in - * {@see WP_Comment_Query}. When the `short_ping` argument of wp_list_comments() - * is true, they are rendered with the compact ping markup by - * {@see Walker_Comment}, which labels the comment with the type's - * `singular_name`, so a ping type should register one. A registered - * `render_callback` takes precedence over the ping markup. Like - * `render_callback`, the rendering effects apply only to classic themes; block - * themes do not use Walker_Comment. - * - * The flag drives grouping and display only. It does not change how a comment of - * this type is validated, moderated, or notified about: the pingback and trackback - * paths in {@see check_comment()}, {@see get_default_comment_status()}, and the - * notification emails are still keyed to those two type names. Default false. + * Whether the comment type is a ping (a notification from another site). + * + * Ping types are grouped under 'pings' by separate_comments() and WP_Comment_Query, + * and rendered with the compact ping markup by Walker_Comment when `short_ping` is + * set, unless the type has a `render_callback`. Validation, moderation, and + * notifications still key on the 'pingback' and 'trackback' type names. Default false. * * @since 7.2.0 * @var bool diff --git a/src/wp-includes/comment.php b/src/wp-includes/comment.php index d0b4c85fb1d4a..3d3cdb0d4ac2d 100644 --- a/src/wp-includes/comment.php +++ b/src/wp-includes/comment.php @@ -452,17 +452,13 @@ function create_initial_comment_types(): void { * counts by default. Core does not currently act on this * argument. * Default false. - * @type bool $is_ping Whether the comment type represents a ping (a notification - * from another site) rather than a human-authored comment. - * Ping types are grouped together by separate_comments(), - * returned by a 'pings' type query, and, when - * wp_list_comments() is called with 'short_ping', rendered - * with compact ping markup by Walker_Comment, labeled with the - * type's singular name. A registered 'render_callback' takes - * precedence over the ping markup. The flag drives grouping - * and display only; validation, moderation, and notification - * still key on the 'pingback' and 'trackback' type names. - * Default false. + * @type bool $is_ping Whether the comment type is a ping (a notification from + * another site). Ping types are grouped under 'pings' by + * separate_comments() and comment queries, and rendered + * with compact ping markup, labeled with the type's singular + * name, when wp_list_comments() is called with 'short_ping'. + * Validation, moderation, and notifications still key on + * the 'pingback' and 'trackback' type names. Default false. * @type callable $render_callback Callback used to render a comment of this type in comment * lists. Receives the same arguments as the `callback` argument * of wp_list_comments() (the comment, the arguments, and the @@ -1418,9 +1414,7 @@ function wp_check_comment_flood( $is_flood, $ip, $email, $date, $avoid_die = fal * Separates an array of comments into an array keyed by comment_type. * * @since 2.7.0 - * @since 7.2.0 The 'pings' group contains the comments of every registered - * comment type with the `is_ping` property, rather than only - * pingbacks and trackbacks. + * @since 7.2.0 The 'pings' group includes every comment type registered with `is_ping`. * * @param WP_Comment[] $comments Array of comments. * @return array Array of comments keyed by comment type. From 2b2e70439b66bb7e684137d9ce970ba1def3730e Mon Sep 17 00:00:00 2001 From: adamsilverstein Date: Sun, 8 Nov 2026 09:24:26 -0500 Subject: [PATCH 12/14] Comments: Add native types to the ping grouping helpers. PHPStan level 10 flagged the new ping lines: get_comment_types() returns names or objects, and a registered label can be any value, so esc_html() could receive a non-string. Declare `: array` returns on the two new protected WP_Comment_Query helpers, narrow the resolved ping names, and only use a ping type's singular name when it is a string. Strings are the only labels that render today, so output is unchanged. See #35214. --- src/wp-includes/class-walker-comment.php | 1 + src/wp-includes/class-wp-comment-query.php | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/wp-includes/class-walker-comment.php b/src/wp-includes/class-walker-comment.php index 132dd92e60cf9..2894d98f098cc 100644 --- a/src/wp-includes/class-walker-comment.php +++ b/src/wp-includes/class-walker-comment.php @@ -285,6 +285,7 @@ protected function ping( $comment, $depth, $args ) { $comment_type_object && ! $comment_type_object->_builtin && isset( $comment_type_object->labels->singular_name ) + && is_string( $comment_type_object->labels->singular_name ) && $default_labels['singular_name'][0] !== $comment_type_object->labels->singular_name ) { /* translators: %s: Singular name of a registered comment type, e.g. "Webmention". */ diff --git a/src/wp-includes/class-wp-comment-query.php b/src/wp-includes/class-wp-comment-query.php index 9863afa3a74a4..7f53c74f882ce 100644 --- a/src/wp-includes/class-wp-comment-query.php +++ b/src/wp-includes/class-wp-comment-query.php @@ -1029,9 +1029,9 @@ protected function get_comment_ids() { * * @since 7.2.0 * - * @return array Query vars to hash into a cache key. + * @return array Query vars to hash into a cache key. */ - protected function get_cache_key_args() { + protected function get_cache_key_args(): array { $_args = wp_array_slice_assoc( $this->query_vars, array_keys( $this->query_var_defaults ) ); unset( $_args['fields'], $_args['update_comment_meta_cache'], $_args['update_comment_post_cache'] ); @@ -1057,8 +1057,9 @@ protected function get_cache_key_args() { * * @return string[] Comment type names. */ - protected function get_ping_comment_types() { + protected function get_ping_comment_types(): array { if ( null === $this->ping_comment_types ) { + /** @var string[] $ping_types */ $ping_types = get_comment_types( array( 'is_ping' => true ), 'names' ); // Queries can run before create_initial_comment_types() registers the built-ins. From 741e96712aba71b53702fc25dc7fbf125db8e656 Mon Sep 17 00:00:00 2001 From: adamsilverstein Date: Sun, 8 Nov 2026 09:24:26 -0500 Subject: [PATCH 13/14] Comments: Test that excluding 'pings' excludes registered ping types. The 'pings' token expands to the registered ping types for both the IN and NOT IN clauses, but only the IN side was covered. Add a `type__not_in` test and declare `: void` on the new ping tests to match the rest of the comment type suite. See #35214. --- tests/phpunit/tests/comment/query.php | 51 +++++++++++++++++-- .../tests/comment/separateComments.php | 4 +- tests/phpunit/tests/comment/types.php | 4 +- tests/phpunit/tests/comment/walker.php | 16 +++--- tests/phpunit/tests/comment/wpCommentType.php | 2 +- 5 files changed, 61 insertions(+), 16 deletions(-) diff --git a/tests/phpunit/tests/comment/query.php b/tests/phpunit/tests/comment/query.php index df14e56d59f2a..6d4c9f2ca1151 100644 --- a/tests/phpunit/tests/comment/query.php +++ b/tests/phpunit/tests/comment/query.php @@ -350,7 +350,7 @@ public function test_query_type_pings() { * * @covers WP_Comment_Query::query */ - public function test_query_type_pings_includes_registered_ping_types() { + public function test_query_type_pings_includes_registered_ping_types(): void { register_comment_type( 'webmention', array( 'is_ping' => true ) ); $pingback = self::factory()->comment->create( @@ -386,6 +386,51 @@ public function test_query_type_pings_includes_registered_ping_types() { $this->assertSameSets( array( $pingback, $mention ), $found ); } + /** + * Excluding 'pings' excludes every registered ping type. + * + * @ticket 35214 + * + * @covers WP_Comment_Query::query + */ + public function test_query_type__not_in_pings_excludes_registered_ping_types(): void { + register_comment_type( 'webmention', array( 'is_ping' => true ) ); + + $comment = self::factory()->comment->create( + array( + 'comment_post_ID' => self::$post_id, + 'comment_approved' => '1', + ) + ); + $review = self::factory()->comment->create( + array( + 'comment_post_ID' => self::$post_id, + 'comment_approved' => '1', + 'comment_type' => 'review', + ) + ); + + foreach ( array( 'pingback', 'trackback', 'webmention' ) as $ping_type ) { + self::factory()->comment->create( + array( + 'comment_post_ID' => self::$post_id, + 'comment_approved' => '1', + 'comment_type' => $ping_type, + ) + ); + } + + $q = new WP_Comment_Query(); + $found = $q->query( + array( + 'type__not_in' => 'pings', + 'fields' => 'ids', + ) + ); + + $this->assertSameSets( array( $comment, $review ), $found ); + } + /** * The registered ping types are part of the 'pings' cache key. Otherwise a plugin * registering a ping type would keep serving results cached before it existed, since @@ -395,7 +440,7 @@ public function test_query_type_pings_includes_registered_ping_types() { * * @covers WP_Comment_Query::get_comments */ - public function test_query_type_pings_is_not_served_from_a_stale_cache() { + public function test_query_type_pings_is_not_served_from_a_stale_cache(): void { $pingback = self::factory()->comment->create( array( 'comment_post_ID' => self::$post_id, @@ -436,7 +481,7 @@ public function test_query_type_pings_is_not_served_from_a_stale_cache() { * * @covers WP_Comment_Query::fill_descendants */ - public function test_threaded_pings_descendants_are_not_served_from_a_stale_cache() { + public function test_threaded_pings_descendants_are_not_served_from_a_stale_cache(): void { $pingback = self::factory()->comment->create( array( 'comment_post_ID' => self::$post_id, diff --git a/tests/phpunit/tests/comment/separateComments.php b/tests/phpunit/tests/comment/separateComments.php index 021a871958918..c5f61569b0446 100644 --- a/tests/phpunit/tests/comment/separateComments.php +++ b/tests/phpunit/tests/comment/separateComments.php @@ -111,7 +111,7 @@ public function test_groups_hold_references_to_the_passed_array() { * * @ticket 35214 */ - public function test_registered_ping_type_is_grouped_into_pings_bucket() { + public function test_registered_ping_type_is_grouped_into_pings_bucket(): void { register_comment_type( 'webmention', array( 'is_ping' => true ) ); $comments = array( $this->create_comment( 'webmention' ) ); @@ -127,7 +127,7 @@ public function test_registered_ping_type_is_grouped_into_pings_bucket() { * * @ticket 35214 */ - public function test_non_ping_type_is_not_grouped_into_pings_bucket() { + public function test_non_ping_type_is_not_grouped_into_pings_bucket(): void { register_comment_type( 'review' ); $comments = array( $this->create_comment( 'review' ) ); diff --git a/tests/phpunit/tests/comment/types.php b/tests/phpunit/tests/comment/types.php index ff7d8bb505fc6..915d00869be2c 100644 --- a/tests/phpunit/tests/comment/types.php +++ b/tests/phpunit/tests/comment/types.php @@ -117,7 +117,7 @@ public function test_built_in_note_type_is_internal_and_non_public(): void { * * @covers ::create_initial_comment_types */ - public function test_built_in_ping_types_are_marked_as_pings() { + public function test_built_in_ping_types_are_marked_as_pings(): void { $this->assertTrue( get_comment_type_object( 'pingback' )->is_ping ); $this->assertTrue( get_comment_type_object( 'trackback' )->is_ping ); } @@ -127,7 +127,7 @@ public function test_built_in_ping_types_are_marked_as_pings() { * * @covers ::create_initial_comment_types */ - public function test_built_in_non_ping_types_are_not_marked_as_pings() { + public function test_built_in_non_ping_types_are_not_marked_as_pings(): void { $this->assertFalse( get_comment_type_object( 'comment' )->is_ping ); $this->assertFalse( get_comment_type_object( 'note' )->is_ping ); } diff --git a/tests/phpunit/tests/comment/walker.php b/tests/phpunit/tests/comment/walker.php index 5cb68b774f940..255c65c119361 100644 --- a/tests/phpunit/tests/comment/walker.php +++ b/tests/phpunit/tests/comment/walker.php @@ -223,7 +223,7 @@ public function test_explicit_callback_takes_precedence_over_render_callback() { * * @ticket 35214 */ - public function test_registered_ping_type_renders_as_ping() { + public function test_registered_ping_type_renders_as_ping(): void { register_comment_type( 'webmention', array( @@ -257,7 +257,7 @@ public function test_registered_ping_type_renders_as_ping() { * * @ticket 35214 */ - public function test_wp_list_comments_type_pings_lists_registered_ping_types() { + public function test_wp_list_comments_type_pings_lists_registered_ping_types(): void { register_comment_type( 'webmention', array( 'is_ping' => true ) ); $comments = array(); @@ -288,7 +288,7 @@ public function test_wp_list_comments_type_pings_lists_registered_ping_types() { * * @ticket 35214 */ - public function test_registered_ping_type_renders_with_its_own_label() { + public function test_registered_ping_type_renders_with_its_own_label(): void { register_comment_type( 'webmention', array( @@ -321,7 +321,7 @@ public function test_registered_ping_type_renders_with_its_own_label() { * * @ticket 35214 */ - public function test_ping_type_without_labels_falls_back_to_pingback_label() { + public function test_ping_type_without_labels_falls_back_to_pingback_label(): void { register_comment_type( 'webmention', array( 'is_ping' => true ) ); $comment_id = self::factory()->comment->create( @@ -349,7 +349,7 @@ public function test_ping_type_without_labels_falls_back_to_pingback_label() { * * @ticket 35214 */ - public function test_built_in_pingback_still_renders_as_ping() { + public function test_built_in_pingback_still_renders_as_ping(): void { $comment_id = self::factory()->comment->create( array( 'comment_post_ID' => $this->post_id, @@ -373,7 +373,7 @@ public function test_built_in_pingback_still_renders_as_ping() { * * @ticket 35214 */ - public function test_ping_type_renders_full_markup_when_short_ping_disabled() { + public function test_ping_type_renders_full_markup_when_short_ping_disabled(): void { register_comment_type( 'webmention', array( 'is_ping' => true ) ); $comment_id = self::factory()->comment->create( @@ -399,7 +399,7 @@ public function test_ping_type_renders_full_markup_when_short_ping_disabled() { * * @ticket 35214 */ - public function test_non_ping_type_is_not_rendered_as_ping_with_short_ping() { + public function test_non_ping_type_is_not_rendered_as_ping_with_short_ping(): void { register_comment_type( 'review' ); $comment_id = self::factory()->comment->create( @@ -428,7 +428,7 @@ public function test_non_ping_type_is_not_rendered_as_ping_with_short_ping() { * * @ticket 35214 */ - public function test_render_callback_takes_precedence_over_short_ping_markup() { + public function test_render_callback_takes_precedence_over_short_ping_markup(): void { register_comment_type( 'webmention', array( diff --git a/tests/phpunit/tests/comment/wpCommentType.php b/tests/phpunit/tests/comment/wpCommentType.php index 32f04fc84193b..4461603b1e8da 100644 --- a/tests/phpunit/tests/comment/wpCommentType.php +++ b/tests/phpunit/tests/comment/wpCommentType.php @@ -32,7 +32,7 @@ public function test_instance_defaults(): void { * * @covers ::set_props */ - public function test_is_ping_is_stored() { + public function test_is_ping_is_stored(): void { $comment_type = new WP_Comment_Type( 'foo', array( 'is_ping' => true ) ); $this->assertTrue( $comment_type->is_ping ); From cd134e36df3edca0d0810e4ae7703116bcf4eeef Mon Sep 17 00:00:00 2001 From: adamsilverstein Date: Sun, 8 Nov 2026 09:32:11 -0500 Subject: [PATCH 14/14] Docs: Mark the new comment type APIs as @since 7.3.0. Trunk is in the 7.2 beta, so these additions can no longer ship in 7.2.0. --- src/wp-includes/class-walker-comment.php | 2 +- src/wp-includes/class-wp-comment-query.php | 8 ++++---- src/wp-includes/class-wp-comment-type.php | 2 +- src/wp-includes/comment.php | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/wp-includes/class-walker-comment.php b/src/wp-includes/class-walker-comment.php index 4706ccaa6fde8..a0c4c99a02179 100644 --- a/src/wp-includes/class-walker-comment.php +++ b/src/wp-includes/class-walker-comment.php @@ -260,7 +260,7 @@ public function end_el( &$output, $data_object, $depth = 0, $args = array() ) { * Outputs a pingback comment. * * @since 3.6.0 - * @since 7.2.0 A registered ping type with its own singular name is labeled with it. + * @since 7.3.0 A registered ping type with its own singular name is labeled with it. * * @see wp_list_comments() * diff --git a/src/wp-includes/class-wp-comment-query.php b/src/wp-includes/class-wp-comment-query.php index 7f53c74f882ce..ec382b03b8949 100644 --- a/src/wp-includes/class-wp-comment-query.php +++ b/src/wp-includes/class-wp-comment-query.php @@ -124,7 +124,7 @@ class WP_Comment_Query { * * Resolved once per parsed query so the cache keys and the SQL use the same set. * - * @since 7.2.0 + * @since 7.3.0 * @var string[]|null */ protected $ping_comment_types = null; @@ -159,7 +159,7 @@ public function __call( $name, $arguments ) { * @since 4.9.0 Introduced the `$paged` argument. * @since 5.1.0 Introduced the `$meta_compare_key` argument. * @since 5.3.0 Introduced the `$meta_type_key` argument. - * @since 7.2.0 A `$type` of 'pings' expands to every comment type registered with `is_ping`. + * @since 7.3.0 A `$type` of 'pings' expands to every comment type registered with `is_ping`. * * @param string|array $query { * Optional. Array or query string of comment query parameters. Default empty. @@ -1027,7 +1027,7 @@ protected function get_comment_ids() { * which do not affect query results. Adds the resolved ping types when 'pings' is * queried, since registering a ping type does not bump the comment last_changed value. * - * @since 7.2.0 + * @since 7.3.0 * * @return array Query vars to hash into a cache key. */ @@ -1053,7 +1053,7 @@ protected function get_cache_key_args(): array { * * Matches the 'pings' group built by separate_comments(). * - * @since 7.2.0 + * @since 7.3.0 * * @return string[] Comment type names. */ diff --git a/src/wp-includes/class-wp-comment-type.php b/src/wp-includes/class-wp-comment-type.php index f6d5deee04e8b..2a261a6bc4f5b 100644 --- a/src/wp-includes/class-wp-comment-type.php +++ b/src/wp-includes/class-wp-comment-type.php @@ -123,7 +123,7 @@ final class WP_Comment_Type { * set, unless the type has a `render_callback`. Validation, moderation, and * notifications still key on the 'pingback' and 'trackback' type names. Default false. * - * @since 7.2.0 + * @since 7.3.0 * @var bool */ public $is_ping = false; diff --git a/src/wp-includes/comment.php b/src/wp-includes/comment.php index 67f7d32997622..c747a02e99ce7 100644 --- a/src/wp-includes/comment.php +++ b/src/wp-includes/comment.php @@ -1390,7 +1390,7 @@ function wp_check_comment_flood( $is_flood, $ip, $email, $date, $avoid_die = fal * Separates an array of comments into an array keyed by comment_type. * * @since 2.7.0 - * @since 7.2.0 The 'pings' group includes every comment type registered with `is_ping`. + * @since 7.3.0 The 'pings' group includes every comment type registered with `is_ping`. * * @param WP_Comment[] $comments Array of comments. * @return array Array of comments keyed by comment type.