探究 WordPress `get_header()` 函数的源码:它如何加载 `header.php` 文件并调用 `wp_head()` 钩子。

嘿,大家好!我是你们今天的WordPress探险向导。今天咱们不聊风花雪月,直接钻到WordPress的心脏里,扒一扒 get_header() 这个小家伙的底裤,看看它到底是怎么把咱们的 header.php 文件加载进来,顺便再扯扯 wp_head() 这个钩子是怎么被触发的。准备好了吗?发车咯!

一、get_header():一个简单的函数,一个不简单的任务

get_header(),顾名思义,就是“获取头部”的意思。在WordPress主题模板中,你几乎可以在每个页面看到它的身影。它负责加载主题目录下的 header.php 文件。简单来说,就是把网站的头部区域,比如 logo、导航菜单、一些元信息等等,都给显示出来。

那么,get_header() 到底做了什么呢?我们来看看它的源码(位于 wp-includes/general-template.php):

function get_header( $name = null, $args = array() ) {
    /**
     * Fires before the header template file is loaded.
     *
     * @since 2.1.0
     *
     * @param string|null $name Name of the specific header file to use. Null for the default header.
     * @param array       $args Additional arguments passed to the header.
     */
    do_action( 'get_header', $name, $args );

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

    $templates[] = 'header.php';

    /**
     * Filters the list of header template filenames.
     *
     * @since 2.1.0
     *
     * @param string[] $templates List of header template filenames.
     * @param string   $name      Name of the specific header file to use. Null for the default header.
     * @param array    $args      Additional arguments passed to the header.
     */
    $templates = apply_filters( 'header_template_hierarchy', $templates, $name, $args );

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

是不是有点眼花缭乱?别怕,咱们一步一步来拆解它。

  1. do_action( 'get_header', $name, $args );: 这是个钩子!在加载任何头部文件之前,WordPress会触发一个名为 get_header 的 action hook。 就像是跟其他插件或者主题说:“嘿,我准备加载头部文件了,你们有什么要做的吗?” $name 参数允许指定不同的头部文件(比如 header-custom.php),$args 允许传递一些额外的参数。

  2. $templates = array(); ...: 这里定义了一个 $templates 数组,用于存储可能的头部文件名。 WordPress 会尝试按照这个数组的顺序去查找对应的文件。

    • 如果 $name 不为空(比如 get_header('custom')),那么 $templates 数组会先包含 header-custom.php,然后再包含 header.php
    • 如果 $name 为空(默认情况),那么 $templates 数组只会包含 header.php
  3. $templates = apply_filters( 'header_template_hierarchy', $templates, $name, $args );: 又是一个钩子!这次是 filter hook。 允许开发者通过 header_template_hierarchy 这个过滤器来修改 $templates 数组,也就是可以改变 WordPress 查找头部文件的顺序。 这给了我们极大的灵活性,可以根据自己的需求来加载不同的头部文件。

  4. locate_template( $templates, true, false, $args );: 这是整个函数的关键部分。 locate_template() 函数会在主题目录下查找 $templates 数组中指定的文件。

    • $templates:要查找的文件名数组。
    • true:如果找到文件,就直接加载它。
    • false:不要加载子主题中的模板文件,只加载当前主题的。
    • $args:传递给模板文件的参数。

二、locate_template():寻宝游戏,找到 header.php

locate_template() 函数负责在主题目录及其父主题目录中查找指定的模板文件。我们来看看它的简化版源码(位于 wp-includes/template.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;
        }

        $template_name = ltrim( $template_name, '/' );

        if ( file_exists( get_stylesheet_directory() . '/' . $template_name ) ) {
            $located = get_stylesheet_directory() . '/' . $template_name;
            break;
        }

        if ( file_exists( get_template_directory() . '/' . $template_name ) ) {
            $located = get_template_directory() . '/' . $template_name;
            break;
        }
    }

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

    return $located;
}

简单解释一下:

  1. 遍历 $template_names 数组: 循环查找数组中的每个文件名。

  2. 检查子主题目录: 首先在当前主题(子主题)的目录下查找文件。如果找到了,就记录下文件路径,然后跳出循环。

  3. 检查父主题目录: 如果在子主题目录中没有找到文件,就在父主题目录下查找。如果找到了,同样记录下文件路径并跳出循环。

  4. load_template( $located, $require_once, $args );: 如果 $load 参数为 true(在 get_header() 中就是 true),并且找到了文件,就调用 load_template() 函数来加载这个文件。

三、load_template():加载模板文件,触发 wp_head()

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_current_template;

    do_action( 'template_redirect' );

    $_wp_current_template[] = $_template_file;

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

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

    array_pop( $_wp_current_template );
}

关键步骤:

  1. do_action( 'template_redirect' );: 虽然这个钩子和get_header()没有直接关系,但是它在模板加载之前触发,非常重要。插件和主题可以使用这个钩子来执行一些操作,比如重定向、权限检查等等。

  2. extract( $args, EXTR_SKIP );: 如果传递了 $args 参数,那么这个函数会将 $args 数组中的键值对提取为变量,方便在模板文件中使用。

  3. require_once( $_template_file ); 或者 require( $_template_file );: 这才是真正加载模板文件的代码。 require_once() 确保文件只会被加载一次,避免重复定义。require() 则允许文件被加载多次。

重点来了!wp_head() 钩子在哪里?

wp_head() 钩子并没有直接出现在 get_header()locate_template()load_template() 函数中。 但是,它通常会出现在 header.php 文件中!

打开你的 header.php 文件,你会看到类似这样的代码:

<!DOCTYPE html>
<html <?php language_attributes(); ?>>
<head>
    <meta charset="<?php bloginfo( 'charset' ); ?>">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="profile" href="http://gmpg.org/xfn/11">
    <?php wp_head(); ?>
</head>
<body <?php body_class(); ?>>
    <div id="page" class="site">
        <a class="skip-link screen-reader-text" href="#content"><?php esc_html_e( 'Skip to content', 'your-theme' ); ?></a>

        <header id="masthead" class="site-header" role="banner">
            </header><!-- #masthead -->

        <div id="content" class="site-content">

wp_head() 函数负责输出一些重要的元信息,比如:

  • 网站的标题 (<title>)
  • CSS 样式表 (<link>)
  • JavaScript 脚本 (<script>)
  • 各种 meta 标签
  • 等等

wp_head() 的作用:

wp_head() 是一个非常重要的钩子,它允许插件和主题向 <head> 标签中添加内容。 很多插件,比如 SEO 插件、社交分享插件、统计代码插件等等,都会使用 wp_head() 钩子来添加它们需要的代码。

总结一下 get_header() 的工作流程:

我们可以用一张表格来总结一下 get_header() 的工作流程:

步骤 描述 涉及的函数 钩子
1 触发 get_header 钩子 get_header() get_header
2 构建模板文件名数组 get_header()
3 允许修改模板文件名数组 get_header() header_template_hierarchy
4 查找模板文件 locate_template()
5 加载模板文件 load_template() template_redirect
6 执行 header.php 中的代码 header.php wp_head (通常在 header.php 中调用)

四、实战演练:自定义头部文件

现在,让我们来做一个小实验,演示如何使用 get_header() 函数加载不同的头部文件。

  1. 创建 header-custom.php 文件: 在你的主题目录下创建一个名为 header-custom.php 的文件。 在这个文件中,你可以添加任何你想要的内容,比如一个不同的 logo,或者一个特殊的导航菜单。

    <!DOCTYPE html>
    <html <?php language_attributes(); ?>>
    <head>
        <meta charset="<?php bloginfo( 'charset' ); ?>">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="profile" href="http://gmpg.org/xfn/11">
        <title>Custom Header</title>
        <?php wp_head(); ?>
    </head>
    <body <?php body_class(); ?>>
        <div id="page" class="site">
            <h1>This is a custom header!</h1>
  2. 在模板文件中调用 get_header('custom'): 在你的某个模板文件(比如 page.php)中,将 get_header() 修改为 get_header('custom')

    <?php
    get_header('custom'); // 加载 header-custom.php
    ?>
    
    <div id="content" class="site-content">
        <?php
        while ( have_posts() ) : the_post();
            the_content();
        endwhile; // End of the loop.
        ?>
    </div><!-- #content -->
    
    <?php
    get_footer();
    ?>
  3. 刷新页面: 刷新你的页面,你会看到 header-custom.php 中的内容被加载进来了。

五、更高级的玩法:使用 header_template_hierarchy 过滤器

如果你想更灵活地控制头部文件的加载,可以使用 header_template_hierarchy 过滤器。 比如,你可以根据用户的角色来加载不同的头部文件。

add_filter( 'header_template_hierarchy', 'my_custom_header_hierarchy', 10, 3 );

function my_custom_header_hierarchy( $templates, $name, $args ) {
    if ( is_user_logged_in() ) {
        // 如果用户已登录,则加载 header-logged-in.php
        array_unshift( $templates, 'header-logged-in.php' );
    } else {
        // 如果用户未登录,则加载 header-logged-out.php
        array_unshift( $templates, 'header-logged-out.php' );
    }
    return $templates;
}

这段代码做了什么?

  1. 注册过滤器: 使用 add_filter() 函数注册一个名为 my_custom_header_hierarchy 的过滤器,绑定到 header_template_hierarchy 钩子上。

  2. my_custom_header_hierarchy 函数: 这个函数接收三个参数:$templates(模板文件名数组)、$name(header name)、$args(参数)。

  3. 判断用户是否登录: 使用 is_user_logged_in() 函数判断当前用户是否已登录。

  4. 修改 $templates 数组: 根据用户的登录状态,使用 array_unshift() 函数将 header-logged-in.phpheader-logged-out.php 添加到 $templates 数组的开头。 这样,WordPress 就会优先查找这些文件。

总结

get_header() 函数虽然看起来简单,但它背后涉及了很多重要的机制,包括钩子、模板查找、模板加载等等。 理解 get_header() 的工作原理,可以帮助你更好地控制网站的头部区域,并根据自己的需求进行定制。 同时,它也展示了WordPress强大的扩展性和灵活性。

希望今天的探险之旅对你有所帮助! 下次再见!

发表回复

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