深入理解 WordPress `comments_template()` 函数的源码:如何根据主题文件结构加载评论模板。

大家好!我是你们今天的WordPress源码探险向导,很高兴能和大家一起深入comments_template()这个既熟悉又有点神秘的函数。咱们今天的目标是,把它扒个精光,看看它到底是怎么找到评论模板,又怎么把它们加载出来的。准备好,我们要开始了!

一、初识comments_template():一个简单的开始

首先,我们先来回顾一下comments_template()最常见的用法。通常,我们会在WordPress主题的single.php或者page.php这样的模板文件中看到类似这样的代码:

<?php
if ( comments_open() || get_comments_number() ) {
    comments_template();
}
?>

这段代码的意思很简单:如果当前文章允许评论,或者已经有评论了,那就加载评论模板。comments_template()就是负责找到并加载这个评论模板的关键。

二、comments_template()源码剖析:抽丝剥茧

接下来,让我们打开WordPress的源码(通常在/wp-includes/comment-template.php),一起看看comments_template()的真面目。为了方便大家理解,我们把源码简化一下,去掉一些不常用的参数和判断,保留核心逻辑。

function comments_template( $template = '', $separate_comments = false ) {
    global $wp_query, $withcomments, $post, $wpdb, $id, $comment, $user_login, $user_ID, $overridden_cpage;

    if ( ! ( is_singular() && ( have_comments() || 'open' == $post->comment_status ) ) ) {
        return;
    }

    $req = get_option( 'require_name_email' );

    if ( in_array( $wp_query->query_vars['pagename'], array('feed', 'rdf', 'rss', 'rss2', 'atom') ) ) {
        return;
    }

    $template = apply_filters( 'comments_template', $template );

    if ( empty( $template ) ) {
        $template = locate_template( array( 'comments.php', 'wp-comments.php' ), true, false );
        return;
    }

    load_template( $template, true );
}

让我们一步一步解读这段代码:

  1. 条件判断:守门员

    if ( ! ( is_singular() && ( have_comments() || 'open' == $post->comment_status ) ) ) {
       return;
    }

    这部分代码是comments_template()的守门员。它会检查当前页面是否是文章或页面(is_singular()),以及该文章/页面是否允许评论('open' == $post->comment_status)或者已经有评论(have_comments())。如果不满足这些条件,函数会直接返回,什么也不做。这意味着,在非文章/页面或者评论关闭的情况下,comments_template()不会加载任何模板。

  2. Feed检测:特殊情况处理

    if ( in_array( $wp_query->query_vars['pagename'], array('feed', 'rdf', 'rss', 'rss2', 'atom') ) ) {
       return;
    }

    这部分代码检测当前页面是否是feed页面。如果是feed页面,则直接返回,不加载评论模板。因为feed页面通常不需要显示评论。

  3. comments_template过滤器:预处理

    $template = apply_filters( 'comments_template', $template );

    这是一个WordPress的钩子(hook)。它允许其他插件或主题通过add_filter()函数来修改$template变量的值。也就是说,在真正加载模板之前,你可以通过这个钩子来指定要加载的评论模板文件。这为定制评论模板提供了极大的灵活性。

  4. locate_template():寻找宝藏

    if ( empty( $template ) ) {
       $template = locate_template( array( 'comments.php', 'wp-comments.php' ), true, false );
       return;
    }

    这部分代码是comments_template()的核心逻辑。如果$template变量为空(也就是没有通过comments_template过滤器指定模板),那么locate_template()函数就会被调用。locate_template()函数负责在主题目录中查找评论模板文件。

    • locate_template( array( 'comments.php', 'wp-comments.php' ), true, false )

      • array( 'comments.php', 'wp-comments.php' ): 这是一个模板文件名的数组。locate_template()会按照数组中的顺序依次查找这些文件。也就是说,它会先查找comments.php,如果找不到,再查找wp-comments.php
      • true: 这个参数表示如果找到了模板文件,就立即加载它。
      • false: 这个参数表示不要加载父主题中的模板文件。
  5. load_template():加载模板

    load_template( $template, true );

    如果locate_template()找到了评论模板文件,或者通过comments_template过滤器指定了模板文件,那么load_template()函数就会被调用来加载这个模板文件。

    • load_template( $template, true )

      • $template: 这是要加载的模板文件的路径。
      • true: 这个参数表示在加载模板文件之前,不要加载WordPress的模板标签。

三、locate_template()的寻宝之旅:深入挖掘

locate_template()函数是寻找评论模板的关键。让我们更深入地了解它。

function locate_template( $template_names, $load = false, $require_once = true ) {
    $located = false;
    foreach ( (array) $template_names as $template_name ) {
        if ( ! $template_name )
            continue;

        if ( file_exists( STYLESHEETPATH . '/' . $template_name) ) {
            $located = STYLESHEETPATH . '/' . $template_name;
            break;
        } elseif ( file_exists( TEMPLATEPATH . '/' . $template_name ) ) {
            $located = TEMPLATEPATH . '/' . $template_name;
            break;
        } elseif ( file_exists( ABSPATH . WPINC . '/theme-compat/' . $template_name ) ) {
          $located = ABSPATH . WPINC . '/theme-compat/' . $template_name;
          break;
        }
    }

    if ( ( true == $load ) && ( '' != $located ) )
        load_template( $located, $require_once );

    return $located;
}

这个函数的工作方式如下:

  1. 循环查找:遍历模板文件名数组

    foreach ( (array) $template_names as $template_name ) {
       if ( ! $template_name )
           continue;

    locate_template()会遍历$template_names数组中的每一个模板文件名。如果文件名为空,则跳过。

  2. 优先级查找:子主题 > 父主题 > 默认模板

    if ( file_exists( STYLESHEETPATH . '/' . $template_name) ) {
       $located = STYLESHEETPATH . '/' . $template_name;
       break;
    } elseif ( file_exists( TEMPLATEPATH . '/' . $template_name ) ) {
       $located = TEMPLATEPATH . '/' . $template_name;
       break;
    } elseif ( file_exists( ABSPATH . WPINC . '/theme-compat/' . $template_name ) ) {
       $located = ABSPATH . WPINC . '/theme-compat/' . $template_name;
       break;
    }

    locate_template()会按照以下顺序查找模板文件:

    • 子主题目录(STYLESHEETPATH: 如果当前使用的是子主题,locate_template()会首先在子主题目录中查找模板文件。
    • 父主题目录(TEMPLATEPATH: 如果在子主题目录中没有找到模板文件,locate_template()会在父主题目录中查找。
    • WordPress默认模板目录(ABSPATH . WPINC . '/theme-compat/': 如果父主题目录中也没有找到模板文件,locate_template()会在WordPress的默认模板目录中查找。这个目录包含一些默认的模板文件,用于保证WordPress在缺少主题的情况下也能正常运行。

    一旦找到模板文件,$located变量就会被设置为该文件的路径,并且循环会立即结束(break)。

  3. 加载模板(可选):根据$load参数决定

    if ( ( true == $load ) && ( '' != $located ) )
       load_template( $located, $require_once );

    如果$load参数为true,并且$located变量不为空(也就是找到了模板文件),那么load_template()函数就会被调用来加载该模板文件。$require_once参数决定是否使用require_once来加载模板文件。

  4. 返回模板路径:方便后续处理

    return $located;

    locate_template()函数会返回找到的模板文件的路径。如果没有找到模板文件,则返回false

四、主题文件结构:评论模板的安身之所

现在我们知道了comments_template()locate_template()是如何工作的,接下来让我们看看评论模板文件通常放在主题的什么位置。

一般来说,评论模板文件(comments.phpwp-comments.php)应该放在主题的根目录下。如果使用了子主题,那么最好将评论模板文件放在子主题的根目录下,这样可以覆盖父主题的评论模板。

以下是一个典型的WordPress主题文件结构:

my-theme/
├── style.css
├── index.php
├── single.php
├── page.php
├── comments.php  <-- 评论模板文件
├── footer.php
├── header.php
└── ...

五、实战演练:定制评论模板

现在,让我们来做一个实际的例子,演示如何定制评论模板。

  1. 创建comments.php文件: 首先,在你的主题(或者子主题)的根目录下创建一个名为comments.php的文件。

  2. 编写模板代码: 在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.
     *
     * @link https://developer.wordpress.org/themes/basics/template-hierarchy/
     *
     * @package WordPress
     * @subpackage Twenty_Nineteen
     * @since 1.0.0
     */
    
    /*
     * 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
                $comments_number = get_comments_number();
                if ( '1' === $comments_number ) {
                    /* translators: %s: Post title. */
                    printf( _x( 'One thought on &ldquo;%s&rdquo;', 'comments title', 'twentynineteen' ), get_the_title() );
                } else {
                    printf(
                        /* translators: 1: Number of comments, 2: Post title. */
                        _nx(
                            '%1$s thought on &ldquo;%2$s&rdquo;',
                            '%1$s thoughts on &ldquo;%2$s&rdquo;',
                            $comments_number,
                            'comments title',
                            'twentynineteen'
                        ),
                        number_format_i18n( $comments_number ),
                        get_the_title()
                    );
                }
                ?>
            </h2><!-- .comments-title -->
    
            <?php the_comments_navigation(); ?>
    
            <ol class="comment-list">
                <?php
                wp_list_comments(
                    array(
                        'style'       => 'ol',
                        'short_ping'  => true,
                        'avatar_size' => 42,
                    )
                );
                ?>
            </ol><!-- .comment-list -->
    
            <?php
            the_comments_navigation();
    
            // If comments are closed and there are comments, let's leave a little note, shall we?
            if ( ! comments_open() ) :
                ?>
                <p class="no-comments"><?php _e( 'Comments are closed.', 'twentynineteen' ); ?></p>
                <?php
            endif;
    
        endif; // Check for have_comments().
    
        comment_form();
        ?>
    
    </div><!-- #comments -->
  3. 修改single.phppage.php: 在你的single.phppage.php文件中,确保调用了comments_template()函数。

    <?php
    if ( comments_open() || get_comments_number() ) {
        comments_template();
    }
    ?>
  4. 测试: 访问你的文章或页面,看看你的自定义评论模板是否生效了。

六、高级技巧:使用comments_template过滤器

除了直接修改comments.php文件之外,你还可以使用comments_template过滤器来指定要加载的评论模板文件。这在某些情况下非常有用,例如,你想要根据文章的类型加载不同的评论模板。

function my_custom_comments_template( $template ) {
    global $post;

    if ( $post->post_type == 'movie' ) {
        $template = get_stylesheet_directory() . '/comments-movie.php';
    }

    return $template;
}
add_filter( 'comments_template', 'my_custom_comments_template' );

这段代码的意思是:如果当前文章的类型是movie,那么就加载comments-movie.php作为评论模板。否则,就使用默认的评论模板。

七、常见问题与解答

问题 解答
评论模板不生效怎么办? 1. 确保comments.php文件存在于你的主题或子主题的根目录下。 2. 检查single.phppage.php文件中是否正确调用了comments_template()函数。 3. 清空WordPress缓存和浏览器缓存。 4. 禁用所有插件,看看是否是插件冲突导致的。 5. 检查主题的functions.php文件,看看是否有代码阻止了评论模板的加载。
如何修改评论表单的样式? 1. 直接修改comments.php文件中的评论表单代码。 2. 使用CSS样式来修改评论表单的样式。 3. 使用comment_form_defaults过滤器来修改评论表单的默认参数。
如何根据文章类型加载不同的评论模板? 使用comments_template过滤器。参考上面的高级技巧部分。
如何禁用评论? 1. 在WordPress后台的文章编辑页面中,取消勾选“允许评论”选项。 2. 在WordPress后台的“设置” -> “讨论”页面中,取消勾选“允许所有人对新文章发表评论”选项。 3. 使用插件来禁用评论。

八、总结

comments_template()函数是WordPress评论系统的核心组成部分。通过深入了解它的源码,我们可以更好地理解WordPress主题的结构,定制评论模板,以及解决评论相关的问题。希望今天的讲解对大家有所帮助。记住,代码的世界就像一个巨大的迷宫,探索的乐趣无穷无尽。下次再见!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注