解析 WordPress `get_footer()` 函数的源码:它如何加载 `footer.php` 文件并调用 `wp_footer()` 钩子。

嘿,各位代码界的探险家们,欢迎来到今天的WordPress源码解析讲座!今天,我们要像剥洋葱一样,一层一层地扒开 get_footer() 这个函数,看看它到底是怎么把网站的footer加载出来,并且顺便召唤出 wp_footer() 这个神奇的钩子。

准备好了吗?让我们开始吧!

一、get_footer() 函数:你的网站 Footer 的召唤师

首先,我们要找到这个“召唤师”在哪里。get_footer() 函数通常位于 WordPress 主题的各种模板文件中,比如 index.phpsingle.phppage.php 等等。它的作用很简单,就是告诉 WordPress:“嘿,是时候把 footer 显示出来了!”

让我们看看 get_footer() 函数的源码(位于 wp-includes/template.php 文件中):

function get_footer( $name = null, $args = array() ) {
    /**
     * Fires before the footer template is loaded.
     *
     * @since 2.1.0
     *
     * @param string|null $name Slug of the footer to load.
     */
    do_action( 'get_footer', $name );

    $templates = array();
    $name      = (string) $name;

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

    $templates[] = 'footer.php';

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

是不是感觉有点眼花缭乱?别担心,让我们一步一步地解读它:

  1. do_action( 'get_footer', $name );

    • 这是个钩子!WordPress 的钩子就像一个预留的“插槽”,允许其他插件或主题在某个特定时刻插入自己的代码。do_action() 函数的作用就是触发这个钩子。
    • 'get_footer' 是钩子的名称,意味着在 footer 模板加载之前,会触发这个钩子。
    • $name 是传递给钩子的参数,它允许你指定一个特定的 footer 名称(稍后会解释)。

    简单来说,这行代码的作用是:“在加载 footer 之前,先通知一下大家,看看有没有人想做点什么。”

  2. $templates = array();

    • 这行代码创建了一个名为 $templates 的数组,用于存储可能的 footer 模板文件名。
  3. $name = (string) $name;

    • 这行代码将 $name 变量强制转换为字符串类型,以确保后续操作的类型一致性。
  4. if ( '' !== $name ) { $templates[] = "footer-{$name}.php"; }

    • 这部分是关键。如果我们在调用 get_footer() 函数时传递了一个 $name 参数(例如,get_footer( 'special' )),那么这行代码就会创建一个名为 footer-special.php 的模板文件名,并将其添加到 $templates 数组中。
    • 这样做的好处是,我们可以根据不同的页面或条件加载不同的 footer 模板。
  5. $templates[] = 'footer.php';

    • 这行代码将 footer.php 添加到 $templates 数组中。footer.php 是默认的 footer 模板文件。
    • 即使我们指定了 $name 参数,footer.php 仍然会被添加到 $templates 数组的末尾,作为备选方案。
  6. locate_template( $templates, true, false, $args );

    • 这行代码是真正的“寻宝猎人”。它使用 locate_template() 函数在主题目录中查找 $templates 数组中列出的模板文件。
    • $templates:要查找的模板文件名数组。
    • true:如果找到模板文件,则立即加载它。
    • false:如果找到模板文件,则不返回文件路径。
    • $args:传递给被加载模板的参数数组(WordPress 5.5 新增)。

    locate_template() 函数会按照 $templates 数组中模板文件的顺序进行查找,直到找到第一个匹配的文件为止。如果找到了匹配的文件,它会立即加载该文件,并停止查找。如果 $templates 数组中的所有文件都没有找到,那么就不会加载任何 footer 模板。

总结一下:

get_footer() 函数的作用是:

  1. 触发 'get_footer' 钩子。
  2. 根据 $name 参数创建一个或多个可能的 footer 模板文件名。
  3. 使用 locate_template() 函数在主题目录中查找这些模板文件,并加载找到的第一个匹配文件。

二、locate_template() 函数:模板文件寻宝大师

locate_template() 函数在 WordPress 中扮演着非常重要的角色,它负责在主题目录中查找模板文件。让我们深入了解一下它的工作原理(位于 wp-includes/template.php 文件中):

function locate_template( $template_names, $load = false, $require_once = true, $args = array() ) {
    $located = false;
    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;
        }
    }

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

    return $located;
}

让我们逐行分析:

  1. $located = false;

    • 初始化一个名为 $located 的变量,用于存储找到的模板文件的路径。初始值为 false,表示尚未找到任何模板文件。
  2. foreach ( (array) $template_names as $template_name ) { ... }

    • 遍历 $template_names 数组,该数组包含要查找的模板文件名。
    • (array) $template_names$template_names 强制转换为数组类型,以确保它可以被 foreach 循环遍历。
  3. if ( ! $template_name ) { continue; }

    • 如果 $template_name 为空,则跳过当前循环迭代,继续处理下一个模板文件名。
  4. if ( file_exists( STYLESHEETPATH . '/' . $template_name ) ) { ... }

    • 检查当前模板文件名是否存在于当前主题的样式表目录(子主题目录)中。
    • STYLESHEETPATH 是一个常量,表示当前主题的样式表目录的路径。
    • file_exists() 函数检查指定的文件是否存在。
    • 如果文件存在,则将 $located 设置为该文件的完整路径,并使用 break 语句退出循环。
  5. elseif ( file_exists( TEMPLATEPATH . '/' . $template_name ) ) { ... }

    • 如果当前模板文件名不存在于子主题目录中,则检查它是否存在于父主题目录中。
    • TEMPLATEPATH 是一个常量,表示父主题目录的路径。
    • 如果文件存在,则将 $located 设置为该文件的完整路径,并使用 break 语句退出循环。
  6. if ( ( $load ) && '' !== $located ) { load_template( $located, $require_once, $args ); }

    • 如果 $load 参数为 true 并且 $located 变量不为空(表示找到了模板文件),则使用 load_template() 函数加载该模板文件。
    • $require_once 参数指定是否使用 require_once() 函数加载模板文件。如果为 true,则只加载一次该文件,以避免重复加载。
    • $args 参数是一个关联数组,包含要传递给被加载模板的变量。
  7. return $located;

    • 返回找到的模板文件的路径。如果没有找到任何模板文件,则返回 false

总结一下:

locate_template() 函数的作用是:

  1. 遍历模板文件名数组,按照以下顺序查找模板文件:
    • 当前主题的样式表目录(子主题目录)。
    • 父主题目录。
  2. 如果找到了匹配的模板文件,则将其路径存储在 $located 变量中,并退出循环。
  3. 如果 $load 参数为 true,则使用 load_template() 函数加载找到的模板文件。
  4. 返回找到的模板文件的路径。

三、load_template() 函数:模板文件加载器

现在,我们来看看 load_template() 函数,它是真正负责加载模板文件的函数(位于 wp-includes/template.php 文件中):

function load_template( $_template_file, $require_once = true, $args = array() ) {
    global $posts, $post, $wp_did_template_redirect, $wp_query, $wp, $wp_object_cache, $wp_locale, $wp_admin_bar;

    /**
     * Fires before the specified template is loaded.
     *
     * @since 1.5.0
     *
     * @param string $_template_file The path to the template to be loaded.
     */
    do_action( 'template_redirect', $_template_file );

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

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

让我们逐行分析:

  1. global $posts, $post, $wp_did_template_redirect, $wp_query, $wp, $wp_object_cache, $wp_locale, $wp_admin_bar;

    • 这行代码声明了一些全局变量,以便在加载的模板文件中可以使用这些变量。
    • 这些变量包含了 WordPress 的核心数据和功能,例如文章、查询、全局 WordPress 对象等等。
  2. do_action( 'template_redirect', $_template_file );

    • 这是一个钩子!它在模板文件加载之前触发 'template_redirect' 钩子。
    • $_template_file 是传递给钩子的参数,它包含了要加载的模板文件的路径。
  3. if ( is_array( $args ) ) { extract( $args, EXTR_SKIP ); }

    • 如果 $args 参数是一个数组,则使用 extract() 函数将数组中的键值对提取为变量。
    • EXTR_SKIP 参数告诉 extract() 函数,如果变量名已经存在,则跳过提取。
    • 这样做的好处是,我们可以将一些变量传递给加载的模板文件,并在模板文件中直接使用这些变量。
  4. if ( $require_once ) { require_once( $_template_file ); } else { require( $_template_file ); }

    • 这行代码使用 require_once()require() 函数加载模板文件。
    • require_once() 函数只加载一次文件,以避免重复加载。
    • require() 函数每次都会加载文件,即使它已经被加载过。
    • $require_once 参数决定使用哪个函数。

总结一下:

load_template() 函数的作用是:

  1. 声明一些全局变量,以便在加载的模板文件中可以使用这些变量。
  2. 触发 'template_redirect' 钩子。
  3. 如果 $args 参数是一个数组,则将数组中的键值对提取为变量。
  4. 使用 require_once()require() 函数加载模板文件。

四、wp_footer() 函数:Footer 的收尾工作

现在,我们终于来到了 wp_footer() 函数。这个函数通常位于 footer.php 文件的末尾,它的作用是在 footer 之后执行一些额外的操作,例如加载 JavaScript 脚本、输出 WordPress 统计信息等等。

让我们看看 wp_footer() 函数的源码(位于 wp-includes/general-template.php 文件中):

function wp_footer() {
    /**
     * Prints scripts or data before the closing body tag.
     *
     * @since 0.9.0
     */
    do_action( 'wp_footer' );
}

是不是很简单?wp_footer() 函数只有一个作用:触发 'wp_footer' 钩子。

这个钩子允许插件和主题在 footer 之后插入自己的代码,例如 JavaScript 脚本、统计代码等等。

为什么要在 footer 之后加载 JavaScript 脚本?

将 JavaScript 脚本放在 footer 之后加载可以提高页面的加载速度。当浏览器解析 HTML 代码时,如果遇到 <script> 标签,它会暂停解析 HTML 代码,先下载并执行 JavaScript 脚本,然后再继续解析 HTML 代码。如果 JavaScript 脚本位于 <head> 标签中,那么浏览器需要等待所有 JavaScript 脚本加载并执行完毕后才能开始渲染页面,这会导致页面加载速度变慢。

将 JavaScript 脚本放在 footer 之后加载可以避免这个问题。浏览器可以先渲染页面,然后再加载 JavaScript 脚本。这样可以提高页面的加载速度,改善用户体验。

五、get_footer()wp_footer() 的协作

现在,让我们把 get_footer()wp_footer() 函数放在一起,看看它们是如何协作的:

  1. 在模板文件中调用 get_footer() 函数。
  2. get_footer() 函数触发 'get_footer' 钩子。
  3. get_footer() 函数根据 $name 参数创建一个或多个可能的 footer 模板文件名。
  4. get_footer() 函数使用 locate_template() 函数在主题目录中查找这些模板文件,并加载找到的第一个匹配文件。
  5. locate_template() 函数使用 load_template() 函数加载模板文件。
  6. load_template() 函数触发 'template_redirect' 钩子。
  7. footer.php 文件被加载。
  8. footer.php 文件的末尾调用 wp_footer() 函数。
  9. wp_footer() 函数触发 'wp_footer' 钩子。

总结:

get_footer() 函数负责加载 footer 模板文件,而 wp_footer() 函数负责在 footer 之后执行一些额外的操作。这两个函数通过钩子机制协作,允许插件和主题在 footer 的加载过程中插入自己的代码。

六、一个简单的例子

假设我们想在 footer 中添加一个自定义的 JavaScript 脚本。我们可以这样做:

  1. 创建一个名为 footer.php 的文件,并将其放置在主题目录中。

  2. footer.php 文件的末尾添加以下代码:

    <?php wp_footer(); ?>
    <script>
    console.log("Hello from footer!");
    </script>
    </body>
    </html>
  3. 在模板文件中调用 get_footer() 函数:

    <?php get_footer(); ?>

当 WordPress 加载页面时,它会加载 footer.php 文件,并执行 wp_footer() 函数。wp_footer() 函数会触发 'wp_footer' 钩子,然后我们的 JavaScript 脚本会被添加到页面中。

七、进阶技巧:使用 $name 参数加载不同的 Footer

get_footer() 函数的 $name 参数允许我们根据不同的页面或条件加载不同的 footer 模板。例如,我们可以创建一个名为 footer-special.php 的文件,并在特定页面上加载它:

  1. 创建一个名为 footer-special.php 的文件,并将其放置在主题目录中。
  2. 在需要加载 footer-special.php 的页面上,调用 get_footer( 'special' ) 函数:

    <?php get_footer( 'special' ); ?>

当 WordPress 加载该页面时,它会首先查找 footer-special.php 文件。如果找到了该文件,它会加载该文件。如果没有找到该文件,它会加载默认的 footer.php 文件。

八、最后的忠告:小心陷阱!

  • 确保你的 footer.php 文件存在! 这是最常见的错误。
  • 不要忘记调用 wp_footer() 函数! 否则,你的 JavaScript 脚本可能无法正常加载。
  • 注意子主题和父主题的优先级! 如果子主题中存在 footer.php 文件,那么父主题中的 footer.php 文件将被忽略。
  • 合理使用钩子! 不要过度使用钩子,否则可能会导致代码混乱。

好了,今天的讲座就到这里。希望通过这次源码解析,大家对 get_footer() 函数有了更深入的了解。记住,理解代码的最好方法就是阅读它、修改它、运行它!祝大家编程愉快!下次再见!

发表回复

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