WordPress 模板加载大法:get_template_part()
函数解剖讲座
大家好!我是你们今天的导游,带大家深入探索 WordPress 模板世界中一个非常重要的函数:get_template_part()
。 别担心,今天我们不搞虚的,直接扒源码,看看这个家伙到底是怎么工作的,又是如何根据 slug
和 name
这两个小参数,变戏法似的加载出我们需要的模板文件的。准备好了吗? 让我们开始这场有趣的探险吧!
1. 欢迎来到 get_template_part()
的世界
首先,让我们明确一下目标:get_template_part()
的作用简单来说,就是加载一个模板文件。这个模板文件可以是主题中的任何 .php
文件,通常用于组织主题结构,比如页眉、页脚、侧边栏等等。
它的基本用法是这样的:
<?php get_template_part( string $slug, string|null $name = null ); ?>
$slug
是必需的,它指定了模板的基本文件名(不包含 .php
后缀)。$name
是可选的,它是一个后缀,可以用来区分同一类型的不同模板。
举个例子,如果我们想加载 template-parts/content-page.php
这个文件,我们可以这样写:
<?php get_template_part( 'template-parts/content', 'page' ); ?>
这里,$slug
是 'template-parts/content'
,$name
是 'page'
。get_template_part()
会尝试加载 template-parts/content-page.php
文件。
是不是感觉有点像侦探游戏?别急,精彩的还在后面。
2. 源码剖析:一层一层揭开它的神秘面纱
现在,让我们勇敢地跳进 get_template_part()
的源码,看看它到底是怎么实现的。为了更好地理解,我们将分步骤进行,并配以代码注释。
/**
* Loads a template part into a template.
*
* @since 3.0.0
*
* @param string $slug The slug name for the generic template.
* @param string|null $name The name of the specialised template.
* @param array $args Array of arguments to pass into the template.
*/
function get_template_part( string $slug, string|null $name = null, array $args = array() ) {
/**
* Fires before the specified template part file is loaded.
*
* The dynamic portion of the hook name, `$slug`, refers to the slug name
* for the generic template part.
*
* @since 3.0.0
*
* @param string $slug The slug name for the generic template.
* @param string|null $name The name of the specialised template.
* @param array $args Array of arguments to pass into the template.
*/
do_action( "get_template_part_{$slug}", $slug, $name, $args );
$templates = array();
$name = (string) $name;
if ( '' !== $name ) {
$templates[] = "{$slug}-{$name}.php";
}
$templates[] = "{$slug}.php";
/**
* Filters the list of template filenames to search for when calling get_template_part().
*
* The dynamic portion of the hook name, `$slug`, refers to the slug name
* for the generic template part.
*
* @since 3.0.0
*
* @param string[] $templates List of template filenames.
* @param string $slug Slug of the template.
* @param string|null $name Name of the template.
* @param array $args Array of arguments to pass into the template.
*/
$templates = apply_filters( "get_template_part_{$slug}", $templates, $slug, $name, $args );
locate_template( $templates, true, false, $args );
}
现在,让我们一步一步地拆解这段代码:
Step 1: 钩子 (Action) 的召唤
do_action( "get_template_part_{$slug}", $slug, $name, $args );
这行代码使用 do_action()
函数触发了一个 action hook。这个 hook 的名称是动态生成的,使用了 $slug
变量。这意味着我们可以通过这个 hook 在模板加载之前执行一些自定义操作,比如修改 $slug
或 $name
的值,甚至取消加载模板。这为我们提供了极大的灵活性。
Step 2: 模板文件名的构建
$templates = array();
$name = (string) $name;
if ( '' !== $name ) {
$templates[] = "{$slug}-{$name}.php";
}
$templates[] = "{$slug}.php";
这段代码是 get_template_part()
的核心部分。它根据 $slug
和 $name
构造了一组可能的模板文件名,并将它们存储在一个数组 $templates
中。
- 首先,如果
$name
不为空,它会构造一个形如"{$slug}-{$name}.php"
的文件名,并添加到$templates
数组中。 - 然后,它会构造一个形如
"{$slug}.php"
的文件名,也添加到$templates
数组中。
这意味着 get_template_part()
会优先查找带有 $name
后缀的模板文件,如果找不到,才会查找不带后缀的基本模板文件。
例如,如果 $slug
是 'template-parts/content'
,$name
是 'page'
,那么 $templates
数组将包含以下两个文件名:
template-parts/content-page.php
template-parts/content.php
Step 3: 过滤器 (Filter) 的妙用
$templates = apply_filters( "get_template_part_{$slug}", $templates, $slug, $name, $args );
这行代码使用 apply_filters()
函数应用了一个 filter hook。这个 hook 的名称同样是动态生成的,使用了 $slug
变量。这意味着我们可以通过这个 filter hook 修改 $templates
数组,从而改变 get_template_part()
查找模板文件的顺序,甚至添加或删除模板文件名。这为我们提供了更高级的自定义能力。
Step 4: 模板文件的定位与加载
locate_template( $templates, true, false, $args );
这行代码调用了 locate_template()
函数,这是另一个 WordPress 内置函数,它的作用是在主题目录中查找指定的模板文件,并加载它。
$templates
参数是包含了可能的模板文件名的数组。true
参数表示如果找到了模板文件,就直接加载它。false
参数表示不加载子主题中的模板文件(如果存在)。$args
参数是一个数组,包含了传递给模板文件的参数。
locate_template()
函数会按照 $templates
数组中的顺序,依次查找这些文件。一旦找到一个存在的文件,它就会立即加载该文件,并停止查找。如果没有找到任何文件,它将不会执行任何操作。
3. locate_template()
函数的深入解析
既然 locate_template()
在 get_template_part()
中扮演了如此重要的角色,那么我们不妨也来深入了解一下它的工作原理。
/**
* Retrieve the name of the highest priority template file that exists.
*
* Searches in the stylesheet directory before the template directory
* so that themes which inherit from a parent theme can just define
* the template files that they want to override.
*
* @since 1.5.0
*
* @param string|string[] $template_names Template file(s) to search for, in order.
* @param bool $load If true the template file will be loaded if it is found.
* @param bool $require_once Whether to require_once or require. Has no effect if $load is false.
* Default true.
* @param array $args Array of arguments to pass into the template.
* @return string The template filename if one is located.
*/
function locate_template( $template_names, $load = false, $require_once = true, $args = array() ) {
$located = '';
foreach ( (array) $template_names as $template_name ) {
if ( ! $template_name ) {
continue;
}
// Trim off any slashes from the template name.
$template_name = ltrim( $template_name, '/' );
// Check child theme first.
if ( file_exists( get_stylesheet_directory() . '/' . $template_name ) ) {
$located = get_stylesheet_directory() . '/' . $template_name;
break;
}
// Check parent theme next.
if ( file_exists( get_template_directory() . '/' . $template_name ) ) {
$located = get_template_directory() . '/' . $template_name;
break;
}
// Check in the plugin-templates/ directory
$located = apply_filters( 'plugin_template_hierarchy', $located, $template_name );
if ( $located ) {
break;
}
}
if ( $load && '' !== $located ) {
load_template( $located, $require_once, $args );
}
return $located;
}
Step 1: 遍历模板文件名
foreach ( (array) $template_names as $template_name ) {
if ( ! $template_name ) {
continue;
}
// Trim off any slashes from the template name.
$template_name = ltrim( $template_name, '/' );
// ... (后面的查找逻辑)
}
locate_template()
函数首先遍历传入的 $template_names
数组。对于每个模板文件名,它会去除前导斜杠,并执行后面的查找逻辑。
Step 2: 优先查找子主题
if ( file_exists( get_stylesheet_directory() . '/' . $template_name ) ) {
$located = get_stylesheet_directory() . '/' . $template_name;
break;
}
这段代码首先检查子主题的目录中是否存在该模板文件。get_stylesheet_directory()
函数返回当前主题的样式表目录,对于子主题来说,它会返回子主题的目录。如果找到了该文件,$located
变量会被设置为该文件的完整路径,并使用 break
语句跳出循环。
Step 3: 查找父主题
if ( file_exists( get_template_directory() . '/' . $template_name ) ) {
$located = get_template_directory() . '/' . $template_name;
break;
}
如果子主题中没有找到该模板文件,这段代码会检查父主题的目录中是否存在该文件。get_template_directory()
函数返回当前主题的模板目录,对于子主题来说,它会返回父主题的目录。如果找到了该文件,$located
变量会被设置为该文件的完整路径,并使用 break
语句跳出循环。
Step 4: 插件模板目录
// Check in the plugin-templates/ directory
$located = apply_filters( 'plugin_template_hierarchy', $located, $template_name );
if ( $located ) {
break;
}
该段代码允许插件通过 plugin_template_hierarchy
过滤器来扩展模板的查找位置,通常用于在插件的 plugin-templates/
目录下查找模板文件。
Step 5: 加载模板文件
if ( $load && '' !== $located ) {
load_template( $located, $require_once, $args );
}
如果在循环中找到了一个模板文件($located
不为空),并且 $load
参数为 true
,那么这段代码会调用 load_template()
函数加载该文件。$require_once
参数决定了使用 require_once
还是 require
来加载文件。$args
参数是一个数组,包含了传递给模板文件的参数。
Step 6: 返回模板文件路径
return $located;
最后,locate_template()
函数返回找到的模板文件的路径。如果没有找到任何文件,它将返回一个空字符串。
4. load_template()
函数的简单介绍
load_template()
函数负责加载指定的模板文件,并将 $args
数组中的变量传递给该文件。它的源码比较简单:
/**
* Require the template file with WordPress environment.
*
* @since 1.5.0
*
* @param string $template_path Path to the template file.
* @param bool $require_once Whether to require_once or require. Default true.
* @param array $args Array of arguments to pass into the template.
*/
function load_template( $template_path, $require_once = true, $args = array() ) {
global $posts, $post, $wp_did_header, $wp_query, $wp_rewrite, $wpdb, $wp_locale, $wp, $wp_version, $wp_theme_directories, $wp_current_filter;
if ( is_array( $wp_current_filter ) ) {
$wp_current_filter = array_unique( $wp_current_filter );
}
if ( is_array( $args ) ) {
extract( $args );
}
if ( $require_once ) {
require_once $template_path;
} else {
require $template_path;
}
}
这段代码主要做了两件事:
-
提取参数: 使用
extract()
函数将$args
数组中的键值对提取为变量。这意味着我们可以在模板文件中直接使用这些变量,而无需使用$args['key']
的形式访问。 -
加载模板文件: 使用
require_once
或require
语句加载指定的模板文件。
5. 总结:get_template_part()
的工作流程
现在,让我们把所有拼图碎片组合起来,总结一下 get_template_part()
的工作流程:
-
触发 action hook:
get_template_part()
首先触发一个 action hook,允许我们在模板加载之前执行一些自定义操作。 -
构建模板文件名: 它根据
$slug
和$name
构造一组可能的模板文件名,并将它们存储在一个数组中。 -
应用 filter hook: 它应用一个 filter hook,允许我们修改模板文件名数组。
-
定位和加载模板文件: 它调用
locate_template()
函数,按照数组中的顺序查找模板文件。locate_template()
函数会优先查找子主题中的文件,然后查找父主题中的文件,最后根据plugin_template_hierarchy
过滤器查找插件模板目录。 -
加载模板文件: 如果
locate_template()
找到了一个模板文件,它会调用load_template()
函数加载该文件,并将$args
数组中的变量传递给该文件。
6. 案例分析:实战演练
为了更好地理解 get_template_part()
的用法,让我们来看几个实际的案例。
案例 1:加载页眉和页脚
这是 get_template_part()
最常见的用法之一。我们可以在主题的 header.php
和 footer.php
文件中使用 get_template_part()
加载页眉和页脚的模板文件。
// header.php
<?php get_template_part( 'template-parts/header' ); ?>
// footer.php
<?php get_template_part( 'template-parts/footer' ); ?>
在这个例子中,get_template_part()
会分别加载 template-parts/header.php
和 template-parts/footer.php
文件。
案例 2:加载不同类型的文章内容
我们可以使用 get_template_part()
根据文章的类型加载不同的内容模板。
<?php
$post_type = get_post_type();
get_template_part( 'template-parts/content', $post_type );
?>
在这个例子中,get_post_type()
函数返回当前文章的类型。然后,get_template_part()
会根据文章类型加载相应的模板文件。例如,如果文章类型是 'post'
,它会加载 template-parts/content-post.php
文件;如果文章类型是 'page'
,它会加载 template-parts/content-page.php
文件。
案例 3:传递参数给模板文件
我们可以使用 $args
参数将变量传递给模板文件。
<?php
$author_id = get_the_author_meta( 'ID' );
$args = array(
'author_id' => $author_id,
'author_name' => get_the_author(),
);
get_template_part( 'template-parts/author', null, $args );
?>
在这个例子中,我们创建了一个 $args
数组,包含了作者的 ID 和姓名。然后,我们将这个数组传递给 get_template_part()
函数。在 template-parts/author.php
文件中,我们可以直接使用 $author_id
和 $author_name
变量。
7. 注意事项:一些小贴士
- 文件名必须是
.php
:get_template_part()
只能加载.php
文件。 slug
和name
的命名规范: 建议使用小写字母和连字符来命名slug
和name
。- 使用 action 和 filter hook: 充分利用 action 和 filter hook,可以实现更高级的自定义功能。
- 避免过度使用: 虽然
get_template_part()
非常方便,但过度使用可能会导致代码难以维护。
8. 总结:get_template_part()
的价值
get_template_part()
是 WordPress 主题开发中一个非常重要的函数。它提供了一种灵活而强大的方式来组织主题结构,并加载不同的模板文件。通过深入理解它的工作原理,我们可以更好地利用它来构建高质量的 WordPress 主题。
今天的讲座就到这里。希望通过今天的学习,大家对 get_template_part()
函数有了更深入的了解。 感谢大家的参与!下次再见!