Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
6a0dfab
Comments: Generalize ping handling with an `is_ping` comment type flag.
adamsilverstein Jun 25, 2026
4bbd85c
Comments: Add tests for `is_ping` comment type grouping.
adamsilverstein Jun 25, 2026
3a5a401
Comments: Cover both branches of is_ping rendering in Walker_Comment.
adamsilverstein Jun 25, 2026
7643e1c
Merge branch 'feature/comment-type-render-callback' into feature/comm…
adamsilverstein Jul 11, 2026
c803649
Comments: Document is_ping interactions and pin grouping edge cases.
adamsilverstein Jul 11, 2026
6206a98
Merge branch 'feature/comment-type-render-callback' into feature/comm…
adamsilverstein Aug 13, 2026
306bb48
Comments: Label a registered ping type with its own name in the compa…
adamsilverstein Aug 13, 2026
36dccee
Comments: Expand a 'pings' comment query to every registered ping type.
adamsilverstein Aug 13, 2026
9aea3a5
Docs: Describe what the `is_ping` flag does and does not reach.
adamsilverstein Aug 13, 2026
7729951
Merge branch 'feature/comment-type-render-callback' into feature/comm…
adamsilverstein Aug 23, 2026
a4a0be9
Merge branch 'feature/comment-type-render-callback' into feature/comm…
adamsilverstein Aug 23, 2026
d72ec04
Comments: Fold the resolved ping types into the descendant cache keys.
adamsilverstein Aug 23, 2026
32661c6
Comments: Keep 'Pingback:' for ping types registered without labels.
adamsilverstein Aug 23, 2026
c52070c
Comments: Stamp the is_ping work for 7.2.0 and settle its edges.
adamsilverstein Aug 23, 2026
41a4852
Merge branch 'feature/comment-type-render-callback' into feature/comm…
adamsilverstein Nov 8, 2026
c58b84c
Merge branch 'feature/comment-type-render-callback' into feature/comm…
adamsilverstein Nov 8, 2026
e8fa4d0
Comments: Tighten the `is_ping` documentation.
adamsilverstein Nov 8, 2026
2b2e704
Comments: Add native types to the ping grouping helpers.
adamsilverstein Nov 8, 2026
741e967
Comments: Test that excluding 'pings' excludes registered ping types.
adamsilverstein Nov 8, 2026
37f1620
Merge branch 'feature/comment-type-render-callback' into feature/comm…
adamsilverstein Nov 8, 2026
cd134e3
Docs: Mark the new comment type APIs as @since 7.3.0.
adamsilverstein Nov 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 27 additions & 3 deletions src/wp-includes/class-walker-comment.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.3.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()
Expand Down Expand Up @@ -201,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();
Expand Down Expand Up @@ -257,6 +260,7 @@ public function end_el( &$output, $data_object, $depth = 0, $args = array() ) {
* Outputs a pingback comment.
*
* @since 3.6.0
* @since 7.3.0 A registered ping type with its own singular name is labeled with it.
*
* @see wp_list_comments()
*
Expand All @@ -266,10 +270,30 @@ public function end_el( &$output, $data_object, $depth = 0, $args = array() ) {
*/
protected function ping( $comment, $depth, $args ) {
$tag = ( 'div' === $args['style'] ) ? 'div' : 'li';

/*
* 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();

if (
$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". */
$label = sprintf( _x( '%s:', 'comment type label' ), esc_html( $comment_type_object->labels->singular_name ) );
} else {
$label = __( 'Pingback:' );
}
?>
<<?php echo $tag; ?> id="comment-<?php comment_ID(); ?>" <?php comment_class( '', $comment ); ?>>
<div class="comment-body">
<?php _e( 'Pingback:' ); ?> <?php comment_author_link( $comment ); ?> <?php edit_comment_link( __( 'Edit' ), '<span class="edit-link">', '</span>' ); ?>
<?php echo $label; ?> <?php comment_author_link( $comment ); ?> <?php edit_comment_link( __( 'Edit' ), '<span class="edit-link">', '</span>' ); ?>
</div>
<?php
}
Expand Down
88 changes: 75 additions & 13 deletions src/wp-includes/class-wp-comment-query.php
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,16 @@ class WP_Comment_Query {
*/
public $max_num_pages = 0;

/**
* Comment types a 'pings' type token expands to, or null until resolved.
*
* Resolved once per parsed query so the cache keys and the SQL use the same set.
*
* @since 7.3.0
* @var string[]|null
*/
protected $ping_comment_types = null;

/**
* Make private/protected methods readable for backward compatibility.
*
Expand Down Expand Up @@ -149,6 +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.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.
Expand Down Expand Up @@ -251,8 +262,9 @@ 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
* 'trackback'), or any custom type string. Default empty.
* Accepts 'comment', 'pings' (every comment type registered
* 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.
Expand Down Expand Up @@ -343,6 +355,9 @@ public function parse_query( $query = '' ) {

$this->query_vars = wp_parse_args( $query, $this->query_var_defaults );

// The ping type registry may have changed since the last query.
$this->ping_comment_types = null;

/**
* Fires after the comment query vars have been parsed.
*
Expand Down Expand Up @@ -453,14 +468,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'] );

$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";
Expand Down Expand Up @@ -811,8 +819,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:
Expand Down Expand Up @@ -1011,6 +1020,59 @@ protected function get_comment_ids() {
}
}

/**
* Returns the query vars that get_comments() and fill_descendants() hash into cache keys.
*
* 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.3.0
*
* @return array<string, mixed> Query vars to hash into a cache key.
*/
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'] );

$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;
}

/**
* Returns the comment types a 'pings' type token expands to.
*
* Matches the 'pings' group built by separate_comments().
*
* @since 7.3.0
*
* @return string[] Comment type names.
*/
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.
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.
Expand Down Expand Up @@ -1053,7 +1115,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.
Expand Down
14 changes: 14 additions & 0 deletions src/wp-includes/class-wp-comment-type.php
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,19 @@ final class WP_Comment_Type {
*/
public $render_callback = null;

/**
* 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.3.0
* @var bool
*/
public $is_ping = false;

/**
* Whether the comment type is hierarchical.
*
Expand Down Expand Up @@ -197,6 +210,7 @@ public function set_props( $args ): void {
'public' => true,
'internal' => false,
'render_callback' => null,
'is_ping' => false,
'_builtin' => false,
);

Expand Down
6 changes: 4 additions & 2 deletions src/wp-includes/comment-template.php
Original file line number Diff line number Diff line change
Expand Up @@ -2249,8 +2249,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.
Expand Down
19 changes: 16 additions & 3 deletions src/wp-includes/comment.php
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,7 @@ function create_initial_comment_types(): void {
'singular_name' => __( 'Pingback' ),
),
'public' => true,
'is_ping' => true,
'_builtin' => true,
)
);
Expand All @@ -376,6 +377,7 @@ function create_initial_comment_types(): void {
'singular_name' => __( 'Trackback' ),
),
'public' => true,
'is_ping' => true,
'_builtin' => true,
)
);
Expand Down Expand Up @@ -448,6 +450,12 @@ function create_initial_comment_types(): void {
* types are meant to be excluded from comment queries and
* counts by default. Core does not currently act on this
* argument. 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 use compact
* ping markup with 'short_ping'. Validation, moderation, and
* notifications still key on 'pingback' and 'trackback'.
* Default false.
* @type callable $render_callback Callback used to render a comment of this type in
* wp_list_comments(). Receives the same arguments as, and is
* overridden by, the wp_list_comments() `callback` argument.
Expand Down Expand Up @@ -1382,6 +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.3.0 The 'pings' group includes every comment type registered with `is_ping`.
*
* @param WP_Comment[] $comments Array of comments.
* @return array<string, WP_Comment[]> Array of comments keyed by comment type.
Expand All @@ -1405,7 +1414,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 ];
}
}
Expand Down Expand Up @@ -1485,7 +1496,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
Expand Down Expand Up @@ -1585,7 +1597,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
Expand Down
Loading