大家好,欢迎来到今天的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网站。
希望今天的讲座对你有所帮助!下次再见!