函数文档

wp_list_comments()

💡 云策文档标注

概述

wp_list_comments() 是 WordPress 核心函数,用于在 comments.php 模板中显示评论列表。它支持多种参数自定义输出格式,如列表样式、分页、评论深度等,并可结合 Walker_Comment 类或自定义回调函数进行高级控制。

关键要点

  • 主要用于在模板中输出评论列表,通常与 get_comments() 或 WP_Query 配合使用。
  • 接受 $args 数组参数,包括 style(列表样式)、callback(自定义回调)、max_depth(评论深度)、type(评论类型)等。
  • 默认使用 Walker_Comment 类处理评论遍历,支持嵌套评论和分页。
  • 可通过 wp_list_comments_args 过滤器修改参数。
  • 返回类型取决于 echo 参数:若为 true 则直接输出,否则返回 HTML 字符串。

代码示例

// 基本用法,显示所有评论
wp_list_comments();

// 自定义参数示例
wp_list_comments(array(
    'style'             => 'ol',
    'callback'          => 'mytheme_comment',
    'max_depth'         => 5,
    'avatar_size'       => 60,
    'type'              => 'comment',
    'reverse_top_level' => false
));

注意事项

  • 需在 comments.php 模板文件中调用,确保评论数据已加载。
  • 使用 'div' 样式时可能存在额外标记的 bug,需注意 CSS 调整。
  • 分页和评论深度设置可受后台“讨论设置”影响,但主题可覆盖。
  • 自定义回调函数需在 functions.php 中定义,并处理评论 HTML 结构。

📄 原文内容

Displays a list of comments.

Description

Used in the comments.php template to list comments for a particular post.

See also

  • WP_Query::$comments

Parameters

$argsstring|arrayoptional
Formatting options.

  • walker object
    Instance of a Walker class to list comments. Default null.
  • max_depth int
    The maximum comments depth.
  • style string
    The style of list ordering. Accepts 'ul', 'ol', or 'div'.
    'div' will result in no additional list markup. Default 'ul'.
  • callback callable
    Callback function to use. Default null.
  • end-callback callable
    Callback function to use at the end. Default null.
  • type string
    Type of comments to list. Accepts 'all', 'comment', 'pingback', 'trackback', 'pings'. Default 'all'.
  • page int
    Page ID to list comments for.
  • per_page int
    Number of comments to list per page.
  • avatar_size int
    Height and width dimensions of the avatar size. Default 32.
  • reverse_top_level bool
    Ordering of the listed comments. If true, will display newest comments first. Default null.
  • reverse_children bool
    Whether to reverse child comments in the list. Default null.
  • format string
    How to format the comments list. Accepts 'html5', 'xhtml'.
    Default 'html5' if the theme supports it.
  • short_ping bool
    Whether to output short pings. Default false.
  • echo bool
    Whether to echo the output or return it. Default true.

Default:array()

$commentsWP_Comment[]optional
Array of WP_Comment objects.

Default:null

Return

void|string Void if 'echo' argument is true, or no comments to list.
Otherwise, HTML list of comments.

More Information

Default options for $args

$args = array(
'walker'            => null,
'max_depth'         => '',
'style'             => 'ul',
'callback'          => null,
'end-callback'      => null,
'type'              => 'all',
'page'              => '',
'per_page'          => '',
'avatar_size'       => 32,
'reverse_top_level' => null,
'reverse_children'  => '',
'format'            => 'html5', // or 'xhtml' if no 'HTML5' theme support
'short_ping'        => false,   // @since 3.6
'echo'              => true     // boolean, default is true
);

The ‘max_depth‘, ‘per_page‘, and ‘reverse_top_level‘ parameters can be more easily controlled through the Discussion Settings Administration Panel but a theme can override those settings.

Source

function wp_list_comments( $args = array(), $comments = null ) {
	global $wp_query, $comment_alt, $comment_depth, $comment_thread_alt, $overridden_cpage, $in_comment_loop;

	$in_comment_loop = true;

	$comment_alt        = 0;
	$comment_thread_alt = 0;
	$comment_depth      = 1;

	$defaults = array(
		'walker'            => null,
		'max_depth'         => '',
		'style'             => 'ul',
		'callback'          => null,
		'end-callback'      => null,
		'type'              => 'all',
		'page'              => '',
		'per_page'          => '',
		'avatar_size'       => 32,
		'reverse_top_level' => null,
		'reverse_children'  => '',
		'format'            => current_theme_supports( 'html5', 'comment-list' ) ? 'html5' : 'xhtml',
		'short_ping'        => false,
		'echo'              => true,
	);

	$parsed_args = wp_parse_args( $args, $defaults );

	/**
	 * Filters the arguments used in retrieving the comment list.
	 *
	 * @since 4.0.0
	 *
	 * @see wp_list_comments()
	 *
	 * @param array $parsed_args An array of arguments for displaying comments.
	 */
	$parsed_args = apply_filters( 'wp_list_comments_args', $parsed_args );

	// Figure out what comments we'll be looping through ($_comments).
	if ( null !== $comments ) {
		$comments = (array) $comments;
		if ( empty( $comments ) ) {
			return;
		}
		if ( 'all' !== $parsed_args['type'] ) {
			$comments_by_type = separate_comments( $comments );
			if ( empty( $comments_by_type[ $parsed_args['type'] ] ) ) {
				return;
			}
			$_comments = $comments_by_type[ $parsed_args['type'] ];
		} else {
			$_comments = $comments;
		}
	} else {
		/*
		 * If 'page' or 'per_page' has been passed, and does not match what's in $wp_query,
		 * perform a separate comment query and allow Walker_Comment to paginate.
		 */
		if ( $parsed_args['page'] || $parsed_args['per_page'] ) {
			$current_cpage = (int) get_query_var( 'cpage' );
			if ( ! $current_cpage ) {
				$current_cpage = 'newest' === get_option( 'default_comments_page' ) ? 1 : $wp_query->max_num_comment_pages;
			}

			$current_per_page = (int) get_query_var( 'comments_per_page' );
			if ( (int) $parsed_args['page'] !== $current_cpage || (int) $parsed_args['per_page'] !== $current_per_page ) {
				$comment_args = array(
					'post_id' => get_the_ID(),
					'orderby' => 'comment_date_gmt',
					'order'   => 'ASC',
					'status'  => 'approve',
				);

				if ( is_user_logged_in() ) {
					$comment_args['include_unapproved'] = array( get_current_user_id() );
				} else {
					$unapproved_email = wp_get_unapproved_comment_author_email();

					if ( $unapproved_email ) {
						$comment_args['include_unapproved'] = array( $unapproved_email );
					}
				}

				$comments = get_comments( $comment_args );

				if ( 'all' !== $parsed_args['type'] ) {
					$comments_by_type = separate_comments( $comments );
					if ( empty( $comments_by_type[ $parsed_args['type'] ] ) ) {
						return;
					}

					$_comments = $comments_by_type[ $parsed_args['type'] ];
				} else {
					$_comments = $comments;
				}
			}

			// Otherwise, fall back on the comments from `$wp_query->comments`.
		} else {
			if ( empty( $wp_query->comments ) ) {
				return;
			}
			if ( 'all' !== $parsed_args['type'] ) {
				if ( empty( $wp_query->comments_by_type ) ) {
					$wp_query->comments_by_type = separate_comments( $wp_query->comments );
				}
				if ( empty( $wp_query->comments_by_type[ $parsed_args['type'] ] ) ) {
					return;
				}
				$_comments = $wp_query->comments_by_type[ $parsed_args['type'] ];
			} else {
				$_comments = $wp_query->comments;
			}

			if ( $wp_query->max_num_comment_pages ) {
				$default_comments_page = get_option( 'default_comments_page' );
				$cpage                 = (int) get_query_var( 'cpage' );

				if ( 'newest' === $default_comments_page ) {
					$parsed_args['cpage'] = $cpage;
				} elseif ( 1 === $cpage ) {
					/*
					 * When the first page shows the oldest comments,
					 * post permalink is the same as the comment permalink.
					 */
					$parsed_args['cpage'] = '';
				} else {
					$parsed_args['cpage'] = $cpage;
				}

				$parsed_args['page']     = 0;
				$parsed_args['per_page'] = 0;
			}
		}
	}

	if ( '' === $parsed_args['per_page'] && get_option( 'page_comments' ) ) {
		$parsed_args['per_page'] = get_query_var( 'comments_per_page' );
	}

	if ( empty( $parsed_args['per_page'] ) ) {
		$parsed_args['per_page'] = 0;
		$parsed_args['page']     = 0;
	}

	if ( '' === $parsed_args['max_depth'] ) {
		if ( get_option( 'thread_comments' ) ) {
			$parsed_args['max_depth'] = get_option( 'thread_comments_depth' );
		} else {
			$parsed_args['max_depth'] = -1;
		}
	}

	if ( '' === $parsed_args['page'] ) {
		if ( empty( $overridden_cpage ) ) {
			$parsed_args['page'] = get_query_var( 'cpage' );
		} else {
			$threaded            = ( -1 !== (int) $parsed_args['max_depth'] );
			$parsed_args['page'] = ( 'newest' === get_option( 'default_comments_page' ) ) ? get_comment_pages_count( $_comments, $parsed_args['per_page'], $threaded ) : 1;
			set_query_var( 'cpage', $parsed_args['page'] );
		}
	}

	// Validation check.
	$parsed_args['page']     = (int) $parsed_args['page'];
	$parsed_args['per_page'] = (int) $parsed_args['per_page'];
	if ( 0 === $parsed_args['page'] && 0 !== $parsed_args['per_page'] ) {
		$parsed_args['page'] = 1;
	}

	if ( null === $parsed_args['reverse_top_level'] ) {
		$parsed_args['reverse_top_level'] = ( 'desc' === get_option( 'comment_order' ) );
	}

	if ( empty( $parsed_args['walker'] ) ) {
		$walker = new Walker_Comment();
	} else {
		$walker = $parsed_args['walker'];
	}

	$output = $walker->paged_walk( $_comments, $parsed_args['max_depth'], $parsed_args['page'], $parsed_args['per_page'], $parsed_args );

	$in_comment_loop = false;

	if ( $parsed_args['echo'] ) {
		echo $output;
	} else {
		return $output;
	}
}

Hooks

apply_filters( ‘wp_list_comments_args’, array $parsed_args )

Filters the arguments used in retrieving the comment list.

Changelog

Version Description
2.7.0 Introduced.

User Contributed Notes

  1. Skip to note 8 content

    Example migrated from Codex:

    Comments Only With A Custom Comment Display

    Displays just comments (no pingbacks or trackbacks) while using a custom callback function to control the look of the comment. You may want to add a max_depth=X parameter, if the reply links are not appearing.

    <ul class="commentlist">
    
    </ul>

    You will need to define your custom callback function in your theme’s functions.php file. Here is an example:

    function mytheme_comment($comment, $args, $depth) {
        if ( 'div' === $args['style'] ) {
            $tag       = 'div';
            $add_below = 'comment';
        } else {
            $tag       = 'li';
            $add_below = 'div-comment';
        }?>
        <  id="comment-">
            <div id="div-comment-<?php comment_ID() ?>" class="comment-body">
            <div class="comment-author vcard">%s</cite> <span class="says">says:</span>' ), get_comment_author_link() ); ?>
            </div>comment_approved == '0' ) { ?>
                <em class="comment-awaiting-moderation"></em><br/>
            <div class="comment-meta commentmetadata">
                <a href="<?php echo htmlspecialchars( get_comment_link( $comment->comment_ID ) ); ?>">
                </a>
            </div>
    
            
    
            <div class="reply"> $add_below, 
                                'depth'     => $depth, 
                                'max_depth' => $args['max_depth'] 
                            ) 
                        ) 
                    ); ?>
            </div>
            </div></pre>
    <p>Note the lack of a trailing <code></li></code>. In order to accommodate nested replies, WordPress will add the appropriate closing tag after listing any child elements.</p>
    				</div><!-- .comment-content -->
    
    					<section id='feedback-3912' class='wporg-has-embedded-code feedback hide-if-js' data-comment-count='0'>
    </section><!-- .feedback -->
    <footer class='feedback-links wporg-dot-link-list' >
    <a role="button" class="feedback-login" href="https://login.wordpress.org/?redirect_to=https%3A%2F%2Fdeveloper.wordpress.org%2Freference%2Ffunctions%2Fwp_list_comments%2F%3Freplytocom%3D3912%23feedback-editor-3912" rel="nofollow">Log in to add feedback</a></footer>
    </article><!-- .comment-body -->
    </li>
    			<li id="comment-2927" data-comment-id="2927" class="comment byuser comment-author-truser odd alt thread-even depth-1">
    			<article id="div-comment-2927" class="comment-body">
    
    							<a href="#comment-content-2927" class="screen-reader-text">Skip to note 9 content</a>
    				<header class="comment-meta">
    					<div class="comment-author vcard">
    						<span class="comment-author-attribution">
    						<a href="https://profiles.wordpress.org/truser/" rel="external nofollow" class="url">Mustafa KUCUK</a>						</span>
    						<a class="comment-date" href="https://developer.wordpress.org/reference/functions/wp_list_comments/#comment-2927">
    							<time datetime="2018-10-26T12:24:33+00:00">
    							7 years ago							</time>
    						</a>
    
    																													</div>
    					<div class="user-note-voting" data-nonce="a378ece171" data-can-vote="false"><a class="user-note-voting-up" title="You must log in to vote on the helpfulness of this note" data-id="2927" data-vote="up" href="https://login.wordpress.org?redirect_to=https%3A%2F%2Fdeveloper.wordpress.org%2Freference%2Ffunctions%2Fwp_list_comments%2F%23comment-2927"><span class="screen-reader-text">You must log in to vote on the helpfulness of this note</span></a><span class="user-note-voting-count " title="100% like this"><span class="screen-reader-text">Vote results for this note: </span>2</span><a class="user-note-voting-down" title="You must log in to vote on the helpfulness of this note" data-id="2927" data-vote="down" href="https://login.wordpress.org?redirect_to=https%3A%2F%2Fdeveloper.wordpress.org%2Freference%2Ffunctions%2Fwp_list_comments%2F%23comment-2927"><span class="screen-reader-text">You must log in to vote on the helpfulness of this note</span></a></div>				</header>
    				<!-- .comment-metadata -->
    			
    				<div class="wporg-has-embedded-code comment-content" id="comment-content-2927">
    				<p>If you are using WordPress 4.9.6 or higher and not showing “Comment is awaiting moderation” alert.</p>
    <p>You can follow the steps below.</p>
    <p>– ) Settings -> Discussion -> enable “Show comments cookies opt-in checkbox.”<br />
    – ) After activating checkbox will show on comments form like this. “Save my name, email, and website in this browser for the next time I comment.”</p>
    <p>That’s it.</p>
    <p>If you want to customize this checkbox field, you can use this code.</p>
    <pre class="wp-block-code"><code lang="php" class="language-php line-numbers">$comment_form = array(
        'fields' => array(
            'cookies' => '<p class="comment-form-cookies-consent"><input id="wp-comment-cookies-consent" name="wp-comment-cookies-consent" type="checkbox" value="yes"' . $consent . ' />' .
            '<label for="wp-comment-cookies-consent">' . __( 'Save my name, email, and website in this browser for the next time I comment.' ) . '</label></p>',
        ),
    );
    
    comment_form( $comment_form );

  2. Skip to note 11 content

    Example migrated from Codex:

    Outputs an ordered list of comments for a specific page or post. Things like threading or paging being enabled or disabled are controlled via the Settings Discussion SubPanel.

    <ol class="commentlist">
    	 XXX,
    			'status' => 'approve' //Change this to the type of comments to be displayed
    		));
    
    		//Display the list of comments
    		wp_list_comments(array(
    			'per_page' => 10, //Allow comment pagination
    			'reverse_top_level' => false //Show the oldest comments at the top of the list
    		), $comments);
    	?>
    </ol>

  3. Skip to note 13 content

    Example migrated from Codex:

    Default Usage

    Outputs an ordered list of the comments. Things like threading or paging being enabled or disabled are controlled via the Settings Discussion SubPanel.

    <ol class="commentlist">
    
    </ol>

  4. Skip to note 14 content

    $cpage = get_query_var( 'cpage' ) ? get_query_var( 'cpage' ) : 1;
    wp_list_comments(
        array(
            'avatar_size'       => 60,
            'short_ping'        => true,
            'type'              => 'comment',
            'callback'          => 'ic_comment_list',
            'per_page'          => get_option( 'comments_per_page' ),
            'page'              => $cpage,
            'reverse_top_level' => get_option( 'default_comments_page' ) === 'oldest' ? false : true,
        )
    );