剖析 `get_template()` 和 `get_header()` 函数的源码,它们是如何加载模板文件的?

模板引擎的秘密:get_template()get_header() 源码剖析

各位观众,欢迎来到今天的“模板引擎的秘密”讲座!今天,咱们不讲那些花里胡哨的概念,直接扒开 WordPress 源码的裤衩,看看 get_template()get_header() 这两个老伙计到底是怎么加载模板文件的。

准备好了吗?咱们开始!

模板加载的江湖规矩:模板层次结构

在深入源码之前,我们需要先了解 WordPress 模板加载的江湖规矩——模板层次结构。WordPress 会按照一定的顺序查找模板文件,找到第一个就用它。就像古代皇帝选妃,先看家世,再看容貌,一层层筛选。

这套规矩决定了你的主题文件应该如何命名和放置,也决定了 WordPress 会优先使用哪个模板。

举个例子,当请求一个单篇文章页面时,WordPress 会按照以下顺序查找模板文件:

  1. single-{post_type}-{slug}.php(例如:single-book-the-lord-of-the-rings.php
  2. single-{post_type}.php(例如:single-book.php
  3. single.php
  4. singular.php
  5. index.php

理解了模板层次结构,才能更好地理解 get_template()get_header() 的工作原理。

get_template():万能的模板加载器

get_template() 是一个非常重要的函数,几乎所有的模板文件加载都依赖它。它的主要功能是:

  1. 根据给定的模板名称,查找匹配的模板文件。
  2. 如果找到,就加载该模板文件。
  3. 如果没找到,就使用默认的模板文件。

咱们先看看 get_template() 的简化版源码(为了方便讲解,我做了一些简化,省略了一些不常用的参数和条件判断):

function get_template( $template_name, $require_once = true, $args = array() ) {
    $template_names = (array) $template_name; // 确保模板名称是数组

    $located = locate_template( $template_names, $require_once, false ); // 找到模板文件

    if ( $located ) {
        load_template( $located, $require_once, $args ); // 加载模板文件
    } else {
        // 如果没找到模板,可以执行一些默认操作,例如输出错误信息
        // 或者加载一个默认的模板
    }
}

这个函数的核心在于两行:locate_template()load_template()

  • locate_template():寻找模板文件

    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;
            }
    
            // 1. 在子主题中查找
            if ( file_exists( STYLESHEETPATH . '/' . $template_name ) ) {
                $located = STYLESHEETPATH . '/' . $template_name;
                break;
            }
    
            // 2. 在父主题中查找
            if ( file_exists( TEMPLATEPATH . '/' . $template_name ) ) {
                $located = TEMPLATEPATH . '/' . $template_name;
                break;
            }
        }
    
        if ( $load && '' != $located ) {
            load_template( $located, $require_once );
        }
    
        return $located;
    }

    locate_template() 的查找顺序是:

    1. 子主题目录 (STYLESHEETPATH)
    2. 父主题目录 (TEMPLATEPATH)

    如果找到了模板文件,就返回该文件的路径;否则,返回 false

  • load_template():加载模板文件

    load_template() 函数负责加载找到的模板文件。它实际上就是使用 require_oncerequire 语句来包含模板文件。

    function load_template( $_template_file, $require_once = true, $args = array() ) {
        global $posts, $post, $wp_did_header, $wp_did_template_redirect, $wp_query, $wp, $wp_rewrite, $wp_the_query;
    
        if ( is_array( $args ) ) {
            extract( $args, EXTR_SKIP );
        }
    
        if ( $require_once ) {
            require_once( $_template_file );
        } else {
            require( $_template_file );
        }
    }

    注意 extract( $args, EXTR_SKIP ) 这行代码。它会将 $args 数组中的键值对提取为变量,这样就可以在模板文件中使用这些变量了。

get_template() 的使用场景

get_template() 可以用于加载任何类型的模板文件,例如:

  • get_template( 'sidebar.php' ):加载侧边栏模板。
  • get_template( 'content.php' ):加载文章内容模板。
  • get_template( array( 'content-page.php', 'content.php' ) ):尝试加载 content-page.php,如果找不到,则加载 content.php

get_header():加载头部模板

get_header() 函数专门用于加载头部模板。它的源码相对简单,但也有一些值得注意的地方。

function get_header( $name = null, $args = array() ) {
    /**
     * Fires before the header template file is loaded.
     *
     * @since 2.1.0
     *
     * @param string|null $name Name of the specific header file to use. Null for the default header.
     * @param array       $args Additional arguments passed to the header template.
     */
    do_action( 'get_header', $name, $args );

    $templates = array();
    $name = (string) $name;
    if ( '' !== $name ) {
        $templates[] = "header-{$name}.php";
    }

    $templates[] = 'header.php';

    /**
     * Filters the list of header template files to search for.
     *
     * @since 4.0.0
     *
     * @param string[] $templates An ordered array of header template files names to search for.
     * @param string   $name      Name of the template to load.
     * @param array    $args      Additional arguments passed to the header template.
     */
    $templates = apply_filters( 'header_templates', $templates, $name, $args );

    locate_template( $templates, true, $args );
}

get_header() 的工作流程如下:

  1. 触发 get_header action hook。 这个钩子允许你在加载头部模板之前执行一些自定义操作。
  2. 构建模板名称数组。 如果指定了 $name 参数,则会尝试加载 header-{$name}.php;否则,加载 header.php
  3. 应用 header_templates filter hook。 这个钩子允许你修改模板名称数组,例如添加自定义的头部模板。
  4. 使用 locate_template() 加载模板文件。 这里调用了我们前面讲过的 locate_template() 函数,根据模板名称数组查找并加载模板文件。

get_header() 的使用场景

get_header() 主要用于加载头部模板,通常在主题的 index.phpsingle.phppage.php 等文件中使用。

  • get_header():加载默认的 header.php 模板。
  • get_header( 'home' ):加载 header-home.php 模板。 这可以用于为首页加载不同的头部。

实例分析:加载文章列表页面的头部

假设我们有一个主题,并且想要在文章列表页面(例如首页或分类页面)加载一个自定义的头部模板。

  1. 创建 header-blog.php 文件。 在你的主题目录下创建一个名为 header-blog.php 的文件,并添加你想要显示的 HTML 代码。

    <!-- header-blog.php -->
    <div class="blog-header">
        <h1>我的博客</h1>
        <p>欢迎来到我的博客,这里记录了我的学习和生活。</p>
    </div>
  2. index.php 文件中使用 get_header() 函数。 在你的 index.php 文件中,将 get_header() 函数修改为 get_header( 'blog' )

    <!DOCTYPE html>
    <html <?php language_attributes(); ?>>
    <head>
        <meta charset="<?php bloginfo( 'charset' ); ?>">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title><?php wp_title( '|', true, 'right' ); ?></title>
        <?php wp_head(); ?>
    </head>
    
    <body <?php body_class(); ?>>
        <?php get_header( 'blog' ); // 加载 header-blog.php ?>
    
        <div class="container">
            <!-- 你的文章列表内容 -->
        </div>
    
        <?php get_footer(); ?>
        <?php wp_footer(); ?>
    </body>
    </html>

    现在,当你访问文章列表页面时,WordPress 就会加载 header-blog.php 文件,而不是默认的 header.php 文件。

灵魂拷问:为什么要用函数?

你可能会问,为什么 WordPress 不直接使用 require_onceinclude 来加载模板文件,而是要封装成 get_template()get_header() 这样的函数呢?

原因有很多,但主要有以下几点:

  1. 代码复用: 避免在多个文件中重复编写相同的模板加载逻辑。
  2. 模板层次结构: get_template() 函数实现了模板层次结构,可以根据不同的条件加载不同的模板文件。
  3. Hook 机制: get_template()get_header() 函数都使用了 action 和 filter 钩子,允许开发者在模板加载过程中进行自定义操作。
  4. 可维护性: 将模板加载逻辑封装成函数,可以提高代码的可维护性,方便以后进行修改和扩展。

总结

今天,我们一起剖析了 get_template()get_header() 函数的源码,了解了它们是如何加载模板文件的。

  • get_template() 是一个通用的模板加载器,可以用于加载任何类型的模板文件。
  • get_header() 专门用于加载头部模板。
  • 这两个函数都利用了模板层次结构和 Hook 机制,提供了强大的灵活性和可定制性。

希望今天的讲座能够帮助你更好地理解 WordPress 模板引擎的工作原理。下次再见!

附录:常用模板函数速查表

函数名 功能 示例
get_header() 加载头部模板。 get_header(); get_header('home');
get_footer() 加载底部模板。 get_footer(); get_footer('alternative');
get_sidebar() 加载侧边栏模板。 get_sidebar(); get_sidebar('primary');
get_template() 加载任何类型的模板文件,支持模板层次结构。 get_template('content.php');
locate_template() 根据模板名称数组查找模板文件,但不加载。 $template = locate_template(array('content.php'));
load_template() 加载指定的模板文件。通常与 locate_template() 配合使用。 load_template($template);
get_stylesheet_directory() 获取当前主题的样式表目录(子主题目录)。 get_stylesheet_directory() . '/images/logo.png';
get_template_directory() 获取当前主题的模板目录(父主题目录)。 get_template_directory() . '/template-parts/content.php';

记住这些函数,你就能像一位真正的 WordPress 大师一样,轻松驾驭模板引擎,打造出令人惊艳的主题!

发表回复

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