剖析 WordPress `get_template_part()` 函数源码:如何通过 `locate_template()` 实现模板文件的查找。

各位观众老爷们,大家好!今天咱们来聊聊 WordPress 模板里一个非常关键的函数:get_template_part()。 别看它名字平平无奇,但它却是 WordPress 主题模块化的基石。 咱们不光要用它,还要扒开它的衣服,看看它到底是怎么工作的。核心就在于它如何通过 locate_template() 找到咱们想要的模板文件。

一、get_template_part():主题模块化的利器

简单来说,get_template_part() 的作用就是加载一个模板文件,然后把这个模板文件的内容插入到当前正在执行的模板中。 想象一下,你在制作一个博客主题,文章列表、侧边栏、页脚,这些都是可以重复使用的模块。 如果每次都把这些模块的代码复制粘贴到不同的页面,那简直是噩梦。 get_template_part() 就解决了这个问题,它允许你把这些模块分别写在不同的文件里,然后在需要的地方调用它们。

1.1 基本用法

get_template_part() 的基本用法很简单:

<?php get_template_part( 'template-parts/content', 'page' ); ?>

这行代码的意思是:加载 template-parts/content-page.php 文件。

  • 第一个参数 'template-parts/content' 是模板文件的“slug”,也就是文件名的一部分,用来区分不同的模板部分。
  • 第二个参数 'page' 是可选的“name”,用来指定更具体的模板文件。 如果省略第二个参数,那么就只加载第一个参数对应的文件。

1.2 查找顺序

get_template_part() 会按照一定的顺序查找模板文件:

  1. template-parts/content-page.php
  2. template-parts/content.php

也就是说,如果第一个文件存在,就加载它;如果第一个文件不存在,就加载第二个文件。

二、locate_template():模板文件查找背后的功臣

get_template_part() 真正干活的是 locate_template() 函数。 locate_template() 负责根据给定的文件名,在主题目录和子主题目录中查找模板文件,并返回找到的文件的完整路径。 如果找不到,就返回空字符串。

2.1 locate_template() 的参数

locate_template() 接受以下参数:

  • $template_names (array|string) (Required) 一个包含模板文件名的数组,或者一个单独的模板文件名字符串。
  • $load (bool) (Optional) 是否加载模板文件。 默认为 false,只返回文件路径。
  • $require_once (bool) (Optional) 是否使用 require_once() 加载模板文件。 默认为 true,防止重复加载。

2.2 源码剖析

现在,让我们深入 locate_template() 的源码,看看它到底是怎么工作的:

function locate_template( $template_names, $load = false, $require_once = true ) {
    $located = '';
    $template_names = (array) $template_names;

    if ( empty( $template_names ) ) {
        return $located;
    }

    $theme_search_paths = apply_filters( 'theme_search_paths', array( 10 => get_stylesheet_directory(), 20 => get_template_directory() ) );
    ksort( $theme_search_paths );
    $theme_search_paths = array_unique( $theme_search_paths );

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

        foreach ( $theme_search_paths as $dir ) {
            if ( file_exists( $dir . '/' . $template_name ) ) {
                $located = $dir . '/' . $template_name;
                break 2;
            }
        }
    }

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

    return $located;
}

2.3 源码解读

  1. 参数处理:

    • 首先,将 $template_names 转换为数组,方便后续处理。
    • 如果 $template_names 为空,直接返回空字符串。
  2. 构建搜索路径:

    • theme_search_paths 是一个数组,包含了主题和子主题的目录。 默认情况下,它包含两个元素:
      • get_stylesheet_directory():子主题的目录(如果存在子主题)。
      • get_template_directory():父主题的目录。
    • apply_filters( 'theme_search_paths', ... ):允许开发者通过 filter 修改搜索路径。 这提供了极大的灵活性,可以自定义模板文件的查找位置。
    • ksort($theme_search_paths)array_unique($theme_search_paths) 确保搜索路径的顺序是确定的,并且没有重复的路径。 ksort 按照键值排序,确保优先级高的路径排在前面。 array_unique 删除重复的路径,避免重复搜索。
  3. 循环查找:

    • 外层循环遍历 $template_names 数组,逐个查找模板文件。
    • 内层循环遍历 $theme_search_paths 数组,逐个在不同的目录中查找。
    • file_exists( $dir . '/' . $template_name ):判断文件是否存在。 如果存在,将文件路径赋值给 $located,并跳出两层循环。
  4. 加载模板:

    • 如果 $load 为 true,并且找到了模板文件,就调用 load_template() 函数加载模板文件。
    • load_template( $located, $require_once ):加载模板文件。 require_once 参数控制是否使用 require_once() 加载,防止重复加载。
  5. 返回值:

    • 返回找到的模板文件的完整路径。 如果没有找到,返回空字符串。

三、load_template():最终的模板加载

load_template() 函数负责真正加载模板文件。 它的源码很简单:

function load_template( $_template_file, $require_once = true, $args = array() ) {
    global $posts, $post, $wp_did_header, $wp_query, $wp_rewrite, $wpdb, $wp_locale, $wp, $l10n, $did_action, $doing_it_wrong, $pagenow;

    if ( is_array( $args ) ) {
        extract( $args, EXTR_SKIP );
    }

    if ( $require_once ) {
        require_once( $_template_file );
    } else {
        require( $_template_file );
    }
}

3.1 源码解读

  1. 全局变量:

    • global ...:声明了一堆全局变量,使得模板文件中可以访问这些变量。 这些变量包含了 WordPress 的核心数据和功能,比如文章数据、查询对象、数据库连接等等。
  2. 提取参数:

    • extract( $args, EXTR_SKIP ):将 $args 数组中的键值对提取为变量。 EXTR_SKIP 参数表示如果变量名已经存在,则跳过提取。 这允许在调用 load_template() 时传递一些自定义的变量到模板文件中。
  3. 加载模板文件:

    • require_once( $_template_file )require( $_template_file ):使用 require_once()require() 加载模板文件。 require_once() 确保文件只被加载一次,防止重复加载。

四、get_template_part() 的完整流程

现在,让我们把 get_template_part()locate_template()load_template() 串起来,看看整个流程是怎么样的:

  1. 调用 get_template_part( $slug, $name )
  2. get_template_part() 根据 $slug$name 构建模板文件名数组。 比如,如果 $slug'template-parts/content'$name'page',那么模板文件名数组就是 ['template-parts/content-page.php', 'template-parts/content.php']
  3. get_template_part() 调用 locate_template( $template_names, true )
  4. locate_template() 在主题目录和子主题目录中查找模板文件。
  5. 如果 locate_template() 找到了模板文件,就返回文件的完整路径;否则,返回空字符串。
  6. 如果 locate_template() 返回了文件路径,get_template_part() 就什么也不做(因为 locate_template() 已经加载了模板文件)。 如果 locate_template() 返回了空字符串,get_template_part() 就什么也不做。

五、实例分析

假设你的主题目录是 /wp-content/themes/my-theme/,并且你的主题中有一个 template-parts/content-page.php 文件。

当你调用 get_template_part( 'template-parts/content', 'page' ) 时,会发生以下事情:

  1. get_template_part() 构建模板文件名数组:['template-parts/content-page.php', 'template-parts/content.php']
  2. get_template_part() 调用 locate_template( ['template-parts/content-page.php', 'template-parts/content.php'], true )
  3. locate_template() 首先在子主题目录中查找 template-parts/content-page.php。 如果没有子主题,就跳过这一步。
  4. locate_template() 在父主题目录 /wp-content/themes/my-theme/ 中查找 template-parts/content-page.php
  5. locate_template() 找到了文件,返回 /wp-content/themes/my-theme/template-parts/content-page.php
  6. locate_template() 因为 $load 参数是 true,所以调用 load_template( '/wp-content/themes/my-theme/template-parts/content-page.php' )
  7. load_template() 加载 /wp-content/themes/my-theme/template-parts/content-page.php 文件,并将文件内容插入到当前模板中。

六、get_template_part() 的应用场景

get_template_part() 在 WordPress 主题开发中应用非常广泛,主要用于以下场景:

  • 模块化主题结构: 将主题的不同部分(比如页眉、页脚、侧边栏、文章列表)分别放在不同的文件中,然后使用 get_template_part() 在需要的地方加载它们。
  • 创建可重用的模板片段: 将一些常用的 HTML 代码片段(比如按钮、表单、导航菜单)放在单独的文件中,然后使用 get_template_part() 在不同的页面中重复使用它们。
  • 构建灵活的页面布局: 根据不同的条件加载不同的模板片段,从而实现灵活的页面布局。 比如,可以根据文章的类型加载不同的文章模板。
  • 子主题定制: 子主题可以通过覆盖父主题的模板片段来实现定制化。 只需要在子主题中创建一个同名的模板文件,就可以覆盖父主题的模板片段。

七、总结

get_template_part()locate_template() 是 WordPress 主题开发中非常重要的函数。 它们实现了主题的模块化,提高了代码的可重用性和可维护性。 通过深入了解它们的源码和工作原理,你可以更好地理解 WordPress 主题的结构,并开发出更加灵活和强大的主题。 记住,灵活运用 theme_search_paths 过滤器,可以扩展模板查找的范围,让你的主题更加强大。 好了,今天的讲座就到这里,希望大家有所收获!下次有机会再见!

发表回复

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