大家好,欢迎来到今天的WordPress源码剖析讲座!今天我们要聊聊一个WordPress主题开发里非常重要的函数:locate_template()
。 别看它名字平平无奇,实际上它负责了WordPress主题文件查找的核心逻辑,是主题能够正确渲染页面的关键先生。
好,废话不多说,咱们直接扒它的源码,看看它到底是怎么运作的,又是如何按照优先级查找模板文件的。
一、locate_template()
函数的概览
首先,让我们先明确一下locate_template()
函数的作用:它接收一个或多个模板文件名作为参数,然后按照一定的优先级顺序,在主题目录及其父主题目录中查找这些文件,并返回找到的第一个文件的完整路径。如果找不到任何匹配的文件,则返回一个空字符串。
它的基本语法如下:
<?php
locate_template(
string|string[] $template_names,
bool $load = false,
bool $require_once = true,
array $args = []
) : string
?>
$template_names
(string|string[]): 要查找的模板文件名,可以是单个文件名字符串,也可以是包含多个文件名字符串的数组。这是必填参数。$load
(bool): 是否加载找到的模板文件。如果设置为true
,则在找到文件后立即使用require()
或require_once()
加载它。默认为false
,仅返回文件路径。$require_once
(bool): 如果$load
为true
,则决定使用require_once()
还是require()
加载文件。默认为true
,使用require_once()
,确保文件只被加载一次。$args
(array): 传递给模板文件的参数。 这些参数可以在模板文件中通过$args
变量访问。WordPress 5.5 引入。
二、源码剖析:深入locate_template()
的内部运作
我们来看一下locate_template()
函数的具体实现 (WordPress 6.4.3):
<?php
function locate_template( $template_names, $load = false, $require_once = true, $args = array() ) {
$located = '';
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 . '/template-loader.php' ) ) {
// Check in the WP core includes directory if needed.
// This is for theme developers who are including files in their themes that are also in the WP core includes directory.
// For example, if a theme includes wp-includes/template-loader.php, it will be loaded from the theme directory instead of the WP core includes directory.
// This ensures that the theme's version of the file is used.
if ( file_exists( ABSPATH . WPINC . '/' . $template_name ) ) {
$located = ABSPATH . WPINC . '/' . $template_name;
break;
}
}
}
if ( $load && '' !== $located ) {
load_template( $located, $require_once, $args );
}
return $located;
}
我们来逐行分析一下这段代码:
-
初始化
$located
变量:$located = '';
这个变量用于存储找到的模板文件的路径,初始值为空字符串。
-
循环遍历模板文件名:
foreach ( (array) $template_names as $template_name ) { if ( ! $template_name ) { continue; } // ... }
$template_names
参数会被强制转换为数组。然后,代码遍历这个数组,逐个检查每个模板文件名。如果$template_name
为空,则跳过当前循环。 -
查找模板文件(优先级顺序):
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 . '/template-loader.php' ) ) { // Check in the WP core includes directory if needed. // This is for theme developers who are including files in their themes that are also in the WP core includes directory. // For example, if a theme includes wp-includes/template-loader.php, it will be loaded from the theme directory instead of the WP core includes directory. // This ensures that the theme's version of the file is used. if ( file_exists( ABSPATH . WPINC . '/' . $template_name ) ) { $located = ABSPATH . WPINC . '/' . $template_name; break; } }
这是
locate_template()
函数的核心部分,它按照以下优先级顺序查找模板文件:-
第一优先级:子主题目录 (
STYLESHEETPATH
)STYLESHEETPATH
是一个常量,指向当前子主题的目录。 如果当前主题是子主题,那么它首先会在子主题目录中查找模板文件。 -
第二优先级:父主题目录 (
TEMPLATEPATH
)TEMPLATEPATH
是一个常量,指向当前主题的目录。如果当前主题不是子主题,或者在子主题目录中没有找到模板文件,那么它会在父主题目录中查找。 -
第三优先级: WordPress核心includes目录(
ABSPATH . WPINC
)这个是为了兼容主题开发者,他们在主题中包含了和WordPress核心目录里重名的文件。这个目录只有在父主题或者子主题目录里,已经包含了
ABSPATH . WPINC . '/template-loader.php'
文件之后,才会去查找,否则不会查找。
如果找到了匹配的模板文件,
$located
变量会被更新为文件的完整路径,并且break
语句会跳出循环。 -
-
加载模板文件(如果需要):
if ( $load && '' !== $located ) { load_template( $located, $require_once, $args ); }
如果
$load
参数为true
,并且找到了模板文件($located
不为空),那么load_template()
函数会被调用来加载模板文件。load_template()
函数本质上就是使用require()
或require_once()
来包含模板文件。$require_once
参数决定了使用require()
还是require_once()
。$args
参数会被传递给加载的模板文件。 -
返回模板文件路径:
return $located;
函数最终返回找到的模板文件的路径。如果没有找到任何匹配的模板文件,则返回空字符串。
三、深入理解优先级
locate_template()
函数的优先级查找机制是WordPress主题系统灵活性的关键。它允许子主题覆盖父主题的模板文件,从而实现定制化。
用表格来展示一下优先级:
优先级 | 目录 | 说明 |
---|---|---|
1 | STYLESHEETPATH (子主题目录) |
如果当前主题是子主题,那么首先在这里查找。子主题可以覆盖父主题的模板文件,实现定制化。 |
2 | TEMPLATEPATH (父主题目录) |
如果当前主题不是子主题,或者在子主题目录中没有找到模板文件,那么在这里查找。 |
3 | ABSPATH . WPINC (WordPress核心目录) |
只有在父主题或者子主题目录里,已经包含了ABSPATH . WPINC . '/template-loader.php' 文件之后,才会去查找,否则不会查找。这是为了兼容主题开发者,他们在主题中包含了和WordPress核心目录里重名的文件。 |
四、load_template()
函数
在locate_template()
中,如果 $load
参数设置为 true
,就会调用 load_template()
函数来加载找到的模板文件。 我们也来看看它的源码:
<?php
function load_template( $template_path, $require_once = true, $args = array() ) {
global $posts, $post, $wp_did_header, $wp_query, $wp_rewrite, $wpdb, $wp_locale, $wp_admin_bar, $wp_registered_sidebars;
/**
* Fires before the specified template is loaded.
*
* @since 1.5.0
*
* @param string $template_path The path to the template being loaded.
* @param bool $require_once Whether to require_once or require. Default true.
* @param array $args Additional arguments passed to the template.
*/
do_action( 'template_redirect', $template_path, $require_once, $args );
if ( is_array( $args ) ) {
extract( $args );
}
if ( $require_once ) {
require_once $template_path;
} else {
require $template_path;
}
}
这个函数做了以下几件事:
-
引入全局变量:
global $posts, $post, $wp_did_header, $wp_query, $wp_rewrite, $wpdb, $wp_locale, $wp_admin_bar, $wp_registered_sidebars;
将一些常用的全局变量引入到函数的作用域中,以便在模板文件中使用。
-
触发
template_redirect
动作钩子:do_action( 'template_redirect', $template_path, $require_once, $args );
在模板文件加载之前,触发
template_redirect
动作钩子。这允许插件或其他主题修改模板加载的行为。 -
提取参数:
if ( is_array( $args ) ) { extract( $args ); }
如果
$args
参数是一个数组,那么使用extract()
函数将数组的键值对提取为变量。这样,在模板文件中就可以直接使用这些变量。 -
加载模板文件:
if ( $require_once ) { require_once $template_path; } else { require $template_path; }
根据
$require_once
参数的值,使用require_once()
或require()
函数来加载模板文件。
五、实际应用场景
locate_template()
函数在WordPress主题开发中被广泛使用。以下是一些常见的应用场景:
-
加载特定模板文件:
<?php locate_template( 'my-template.php', true ); // 加载 my-template.php 文件 ?>
-
根据条件加载不同的模板文件:
<?php if ( is_page( 'about' ) ) { locate_template( array( 'page-about.php', 'page.php' ), true ); // 优先加载 page-about.php,如果找不到则加载 page.php } else { locate_template( 'page.php', true ); // 加载 page.php } ?>
-
在插件中使用主题模板:
<?php $template = locate_template( 'my-plugin-template.php' ); if ( ! empty( $template ) ) { include( $template ); // 在插件中使用主题的 my-plugin-template.php 文件 } ?>
-
传递参数给模板文件
<?php $args = array( 'title' => 'Hello World', 'content' => 'This is my custom content.' ); locate_template( 'my-template.php', true, $args ); ?>
在
my-template.php
中:<?php echo '<h1>' . esc_html( $title ) . '</h1>'; echo '<p>' . esc_html( $content ) . '</p>'; ?>
六、注意事项
- 文件路径:
locate_template()
函数返回的是文件的完整路径,而不是相对路径。 - 性能: 频繁调用
locate_template()
函数可能会影响性能,因为它需要进行文件查找。尽量避免在循环中调用它。 - 安全性: 在使用
include()
或require()
加载模板文件时,要确保模板文件的来源是可信的,以防止安全漏洞。 - 主题继承:
locate_template()
函数是实现主题继承的关键。子主题可以通过覆盖父主题的模板文件来定制网站的外观和功能。 get_template_part()
函数:get_template_part()
函数是locate_template()
函数的封装,它简化了模板文件的加载过程。get_template_part()
函数会自动加载模板文件,而无需手动使用include()
或require()
。
七、get_template_part()
函数
让我们简单看下get_template_part
函数,它用起来更方便一些:
<?php
function get_template_part( $slug, $name = null, $args = array() ) {
/**
* Fires before the specified template part 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 Name of the generic template.
* @param string|null $name Name of the specific template.
* @param array $args Additional arguments passed to the template.
*/
do_action( "get_template_part_{$slug}", $slug, $name, $args );
// Build a list of possible parts names that can be filenames.
$templates = array();
if ( isset( $name ) && '' !== $name ) {
$templates[] = "{$slug}-{$name}.php";
}
$templates[] = "{$slug}.php";
// Allow some of the template names to be filtered to ease theme developers job.
$templates = apply_filters( 'get_template_part_templates', $templates, $slug, $name );
/**
* Fires after the template for the part is determined.
*
* The dynamic portion of the hook name, `$slug`, refers to the slug
* name for the generic template part.
*
* @since 5.5.0
*
* @param string $slug Name of the generic template.
* @param string|null $name Name of the specific template.
* @param array $templates Array of template files to search for, in order.
* @param array $args Additional arguments passed to the template.
*/
do_action( "get_template_part_{$slug}_templates", $slug, $name, $templates, $args );
locate_template( $templates, true, false, $args );
}
这个函数的主要功能是:
- 构建模板文件名数组: 它根据
$slug
和$name
参数构建一个包含多个可能的模板文件名的数组。例如,如果$slug
为'content'
,$name
为'single'
,那么它会构建一个包含'content-single.php'
和'content.php'
的数组。 - 调用
locate_template()
函数: 它调用locate_template()
函数来查找并加载模板文件。locate_template()
函数会按照优先级顺序查找模板文件,并返回找到的第一个文件的路径。 - 简化模板加载:
get_template_part()
函数简化了模板文件的加载过程,因为它会自动构建模板文件名数组,并调用locate_template()
函数来查找和加载模板文件。
八、总结
locate_template()
函数是WordPress主题系统的重要组成部分,它负责模板文件的查找和加载。理解它的工作原理对于主题开发至关重要。通过掌握 locate_template()
函数的用法,你可以更好地控制主题的结构和外观,并实现高度定制化的WordPress网站。
希望今天的讲座对你有所帮助!下次再见!