各位听众,晚上好!今天咱们来聊聊 WordPress 里一个“默默奉献”的函数:comments_template()
。 别看它名字平平无奇,但它可是负责在你的博客文章里“召唤”评论区的关键人物。 咱们要深入它的源码,看看它是如何一步步找到并加载评论模板的。 准备好了吗?Let’s dive in!
1. 故事的开端:comments_template()
的职责
comments_template()
函数的主要职责非常明确:它负责根据 WordPress 主题的结构,加载相应的评论模板文件,从而在文章或页面中显示评论表单和已有的评论列表。 简单来说,就是把评论功能“变”出来。
2. 源码剖析:一层层抽丝剥茧
咱们直接上代码,然后逐行讲解。 这是 comments_template()
函数的核心代码(简化版,去掉了部分兼容性处理和过滤):
function comments_template( $template = '', $separate_comments = false ) {
global $wp_query, $withcomments, $post, $wpdb, $id, $comment, $user_login, $user_ID, $comments, $comment_alt, $comment_depth, $comment_thread_alt;
if ( ! ( is_singular() && ( have_comments() || 'open' == $post->comment_status ) ) ) {
return;
}
$req = get_option( 'require_name_email' );
$id = get_the_ID();
if ( null === $id ) {
return;
}
$comments = $wp_query->comments;
$withcomments = true;
if ( empty( $template ) ) {
$template = '/comments.php';
}
$template = apply_filters( 'comments_template', $template );
$include = locate_template( $template, true, false );
return;
}
现在,我们来一行行解读:
-
function comments_template( $template = '', $separate_comments = false ) { ... }
: 函数定义,接受两个参数。$template
指定了评论模板的文件名(默认是comments.php
),$separate_comments
用于控制是否将评论和 trackbacks/pingbacks 分开显示(通常不用管它)。 -
global ...
: 声明了一堆全局变量。 WordPress 的全局变量那可是个大杂烩,这里用到的主要是$wp_query
(保存查询结果),$post
(当前文章对象),$id
(当前文章ID),$comments
(评论列表)等等。 这些全局变量为评论模板的渲染提供数据。 -
if ( ! ( is_singular() && ( have_comments() || 'open' == $post->comment_status ) ) ) { return; }
: 这是一个关键的条件判断。 只有当以下条件同时满足时,才会继续执行:is_singular()
: 当前页面是单篇文章或页面。 也就是说,评论模板只会在文章或页面上显示,不会在首页、分类页等地方显示。have_comments() || 'open' == $post->comment_status
: 要么文章已经有评论,要么文章允许评论。 这保证了只有在需要显示评论或允许发表评论的情况下,才会加载评论模板。
-
$req = get_option( 'require_name_email' );
: 获取是否要求评论者填写姓名和邮箱的设置。 -
$id = get_the_ID();
: 获取当前文章的 ID。 -
if ( null === $id ) { return; }
: 确保文章 ID 存在。 -
$comments = $wp_query->comments;
: 将评论列表从$wp_query
赋值给$comments
变量,方便在模板中使用。 -
$withcomments = true;
: 设置$withcomments
变量为true
,表示当前页面包含评论。 -
if ( empty( $template ) ) { $template = '/comments.php'; }
: 如果没有指定模板文件名,则使用默认的comments.php
。 -
$template = apply_filters( 'comments_template', $template );
: 这是一个非常重要的过滤器。 它允许你通过插件或主题,修改要加载的评论模板文件名。 比如,你可以根据不同的文章类型,加载不同的评论模板。 -
$include = locate_template( $template, true, false );
: 这就是“寻宝”的关键一步!locate_template()
函数会在主题目录及其父主题目录中查找指定的模板文件。 它的三个参数分别是:$template
: 要查找的模板文件名。true
: 找到模板后,立即包含(require_once
)它。false
: 是否加载子主题的语言文件。
locate_template()
函数会按照以下顺序查找模板文件:- 子主题目录(如果存在)。
- 父主题目录。
如果找到了模板文件,
locate_template()
函数会返回模板文件的完整路径,并立即包含它。 如果没有找到,则返回空字符串。 -
return;
: 函数结束。
3. locate_template()
函数:寻宝的罗盘
locate_template()
函数是寻找模板文件的核心。 为了更清楚地理解它的工作原理,咱们可以简单模拟一下它的实现:
function my_locate_template( $template_names, $load = false, $require_once = true ) {
$located = false;
foreach ( (array) $template_names as $template_name ) {
if ( ! $template_name ) {
continue;
}
// 1. 检查子主题目录
if ( file_exists( get_stylesheet_directory() . '/' . $template_name ) ) {
$located = get_stylesheet_directory() . '/' . $template_name;
break;
}
// 2. 检查父主题目录
if ( file_exists( get_template_directory() . '/' . $template_name ) ) {
$located = get_template_directory() . '/' . $template_name;
break;
}
}
if ( $load && '' != $located ) {
load_template( $located, $require_once );
}
return $located;
}
function load_template( $_template_file, $require_once = true ) {
global $posts, $post, $wp_did_header, $wp_did_template_redirect, $wp_query, $wp, $wp_locale, $wp_admin_bar, $content_width;
( $require_once ) ? require_once( $_template_file ) : require( $_template_file );
}
这个简化的 my_locate_template()
函数模拟了 locate_template()
的主要逻辑:
- 遍历模板文件名数组(虽然
comments_template()
只传递一个文件名,但locate_template()
可以接受一个文件名数组)。 - 首先检查子主题目录是否存在该文件。
- 如果子主题目录不存在,则检查父主题目录。
- 如果找到了文件,则返回文件的完整路径。
- 如果
$load
参数为true
,则使用load_template()
函数包含该文件。 load_template()
函数使用require_once
或者require
函数来包含模板文件。
4. 主题目录的结构:评论模板的“家”
WordPress 主题通常会将评论模板文件放在主题的根目录下,文件名通常是 comments.php
。 但是,你也可以根据需要,创建不同的评论模板文件,并使用 comments_template
过滤器来指定要加载的模板。
例如,你可以创建一个名为 comments-special.php
的评论模板,并在你的主题的 functions.php
文件中添加以下代码:
function my_custom_comments_template( $template ) {
if ( is_single( array( 1, 2, 3 ) ) ) { // 文章ID为 1,2,3 时使用特殊模板
return 'comments-special.php';
}
return $template;
}
add_filter( 'comments_template', 'my_custom_comments_template' );
这段代码会将文章 ID 为 1、2 或 3 的文章的评论模板替换为 comments-special.php
。
5. 评论模板文件的内容:评论区的“骨架”
评论模板文件(例如 comments.php
)通常包含以下内容:
- 评论列表: 使用
wp_list_comments()
函数显示已有的评论列表。 - 评论表单: 使用
comment_form()
函数显示评论表单,让用户可以发表评论。 - 评论导航: 如果评论数量很多,可以使用
paginate_comments_links()
函数显示评论分页导航。 - 一些判断逻辑: 例如,判断是否允许评论,判断是否需要密码才能查看评论等。
一个典型的 comments.php
文件的结构如下:
<?php
/**
* The template for displaying comments
*
* This is the template that displays the area of the page that contains both the current comments
* and the comment form.
*/
/*
* If the current post is protected by a password and
* the visitor has not yet entered the password we will
* return early without loading the comments.
*/
if ( post_password_required() ) {
return;
}
?>
<div id="comments" class="comments-area">
<?php
// You can start editing here -- including this comment!
if ( have_comments() ) :
?>
<h2 class="comments-title">
<?php
printf( // WPCS: XSS OK.
esc_html( _nx( 'One thought on “%2$s”', '%1$s thoughts on “%2$s”', get_comments_number(), 'comments title', 'your-theme' ) ),
number_format_i18n( get_comments_number() ),
'<span>' . get_the_title() . '</span>'
);
?>
</h2><!-- .comments-title -->
<?php if ( get_comment_pages_count() > 1 && get_option( 'page_comments' ) ) : // Are there comments to navigate through? ?>
<nav id="comment-nav-above" class="navigation comment-navigation" role="navigation">
<h2 class="screen-reader-text"><?php esc_html_e( 'Comment navigation', 'your-theme' ); ?></h2>
<div class="nav-links">
<div class="nav-previous"><?php previous_comments_link( esc_html__( 'Older Comments', 'your-theme' ) ); ?></div>
<div class="nav-next"><?php next_comments_link( esc_html__( 'Newer Comments', 'your-theme' ) ); ?></div>
</div><!-- .nav-links -->
</nav><!-- #comment-nav-above -->
<?php endif; // Check for comment navigation. ?>
<ol class="comment-list">
<?php
wp_list_comments( array(
'style' => 'ol',
'short_ping' => true,
) );
?>
</ol><!-- .comment-list -->
<?php if ( get_comment_pages_count() > 1 && get_option( 'page_comments' ) ) : // Are there comments to navigate through? ?>
<nav id="comment-nav-below" class="navigation comment-navigation" role="navigation">
<h2 class="screen-reader-text"><?php esc_html_e( 'Comment navigation', 'your-theme' ); ?></h2>
<div class="nav-links">
<div class="nav-previous"><?php previous_comments_link( esc_html__( 'Older Comments', 'your-theme' ) ); ?></div>
<div class="nav-next"><?php next_comments_link( esc_html__( 'Newer Comments', 'your-theme' ) ); ?></div>
</div><!-- .nav-links -->
</nav><!-- #comment-nav-below -->
<?php endif; // Check for comment navigation. ?>
<?php endif; // Check for have_comments(). ?>
<?php
// If comments are closed and there are comments, let's leave a little note, shall we?
if ( ! comments_open() && get_comments_number() && post_type_supports( get_post_type(), 'comments' ) ) :
?>
<p class="no-comments"><?php esc_html_e( 'Comments are closed.', 'your-theme' ); ?></p>
<?php endif; ?>
<?php comment_form(); ?>
</div><!-- #comments -->
6. 总结:comments_template()
的寻宝之旅
现在,咱们来总结一下 comments_template()
函数的工作流程:
- 检查当前页面是否是单篇文章或页面,并且是否允许评论。
- 获取要加载的评论模板文件名(默认是
comments.php
)。 - 使用
locate_template()
函数在主题目录及其父主题目录中查找模板文件。 - 如果找到了模板文件,则包含该文件。
- 评论模板文件负责显示评论列表和评论表单。
用一张表格来总结:
步骤 | 函数/变量 | 描述 |
---|---|---|
1 | comments_template() |
函数入口,接收模板文件名参数。 |
2 | is_singular() , have_comments() , $post->comment_status |
条件判断:确保当前页面是单篇文章/页面,并且允许评论。 |
3 | apply_filters( 'comments_template', $template ) |
使用过滤器,允许修改模板文件名。 |
4 | locate_template( $template, true, false ) |
在主题目录和父主题目录中查找模板文件,并加载。 |
5 | comments.php (或其他指定的模板文件) |
包含评论列表和评论表单的 HTML 和 PHP 代码。 |
7. 常见问题与解答
-
Q: 为什么我的评论区不显示?
- A: 首先,确认你的文章或页面允许评论(在编辑文章/页面时,检查“讨论”选项)。 其次,确认你的主题目录下存在
comments.php
文件。 最后,检查你的主题或插件是否禁用了评论功能。
- A: 首先,确认你的文章或页面允许评论(在编辑文章/页面时,检查“讨论”选项)。 其次,确认你的主题目录下存在
-
Q: 我想修改评论表单的样式,应该怎么做?
- A: 你可以复制
comments.php
文件到你的子主题目录,然后修改子主题目录中的comments.php
文件。 这样可以避免修改父主题的文件,防止主题更新时丢失修改。 另外,你也可以使用 CSS 来修改评论表单的样式。
- A: 你可以复制
-
Q: 我想为不同的文章类型使用不同的评论模板,可以实现吗?
- A: 当然可以! 可以使用
comments_template
过滤器来实现。 在你的主题的functions.php
文件中,添加一个函数,根据文章类型修改要加载的模板文件名。
- A: 当然可以! 可以使用
-
Q:
comments_template()
函数的$separate_comments
参数有什么用?- A:
$separate_comments
参数用于控制是否将评论和 trackbacks/pingbacks 分开显示。 在 WordPress 4.5 之后,这个参数已经不再使用了,因为 WordPress 已经默认将评论和 trackbacks/pingbacks 分开显示。
- A:
好了,今天的讲座就到这里。 希望通过今天的讲解,大家对 comments_template()
函数的工作原理有了更深入的了解。 记住,理解源码是成为 WordPress 大牛的第一步! 咱们下期再见!