剖析 WordPress `get_current_screen()` 函数的源码:如何获取当前后台页面的 `WP_Screen` 对象。

大家好!我是你们今天的WordPress源码小导游,让我们一起潜入get_current_screen()的世界,看看它是如何帮助我们获取当前后台页面的信息的。准备好了吗?让我们开始这场代码探险之旅!

第一站:get_current_screen() 的真面目

首先,我们要搞清楚 get_current_screen() 到底是什么。它是一个WordPress函数,位于wp-includes/screen.php文件中。它的作用是返回一个 WP_Screen 类的对象,这个对象包含了当前后台页面的各种信息,比如页面ID、基名、父页面等等。

简单来说,WP_Screen 对象就是当前后台页面的“身份证”,而 get_current_screen() 就是帮你拿到这张“身份证”的工具。

第二站:源码剖析

让我们来看看 get_current_screen() 的源码(WordPress 6.4.2):

/**
 * Gets the current screen object.
 *
 * @return WP_Screen|null WP_Screen object. Null if not set.
 */
function get_current_screen() {
    global $current_screen;

    if ( ! is_a( $current_screen, 'WP_Screen' ) ) {
        set_current_screen();
    }

    return $current_screen;
}

这段代码看起来很简单,但它包含了几个关键步骤:

  1. 全局变量 $current_screen global $current_screen; 这一行代码声明了 $current_screen 是一个全局变量。这意味着它可以在WordPress的任何地方访问。这个变量就是用来存储 WP_Screen 对象的。

  2. 检查 $current_screen 是否存在且是 WP_Screen 对象: if ( ! is_a( $current_screen, 'WP_Screen' ) ) { ... } 这一行代码检查 $current_screen 是否已经存在,并且是否是一个 WP_Screen 类的实例。is_a() 函数用于判断一个对象是否属于某个类或其子类。如果 $current_screen 不存在,或者不是 WP_Screen 对象,那么就执行 set_current_screen() 函数。

  3. set_current_screen() 函数: set_current_screen(); 这个函数负责创建并初始化 WP_Screen 对象,并将其赋值给全局变量 $current_screen。这是整个流程中最关键的一步,我们稍后会详细分析它。

  4. 返回 $current_screen return $current_screen; 最后,函数返回 $current_screen,也就是当前后台页面的 WP_Screen 对象。

第三站:深入 set_current_screen()

set_current_screen() 函数位于同一个文件 wp-includes/screen.php 中。让我们看看它的源码:

/**
 * Sets the current screen object.
 *
 * @global WP_Screen $current_screen
 *
 * @param WP_Screen|string|null $hook_name Optional. The hook name.
 *                                         Defaults to the global `$hook_suffix`.
 * @return WP_Screen WP_Screen object.
 */
function set_current_screen( $hook_name = '' ) {
    global $current_screen, $hook_suffix, $pagenow;

    if ( ! is_a( $current_screen, 'WP_Screen' ) ) {
        $current_screen = new WP_Screen();
    }

    if ( empty( $hook_name ) ) {
        $hook_name = $hook_suffix;
    }

    // Don't set up the screen object twice.
    if ( did_action( 'current_screen' ) ) {
        return $current_screen;
    }

    $current_screen->populate( $hook_name );

    /**
     * Fires after the current screen object is set.
     *
     * @since 3.0.0
     *
     * @param WP_Screen $current_screen Current screen object.
     */
    do_action( 'current_screen', $current_screen );

    return $current_screen;
}

这个函数稍微复杂一些,我们一步一步来分析:

  1. 全局变量: global $current_screen, $hook_suffix, $pagenow; 这里声明了三个全局变量:$current_screen (我们已经熟悉了),$hook_suffix$pagenow$hook_suffix 存储了当前页面的hook后缀,而 $pagenow 存储了当前页面的文件名 (例如 index.php, edit.php 等)。

  2. 创建 WP_Screen 对象: if ( ! is_a( $current_screen, 'WP_Screen' ) ) { $current_screen = new WP_Screen(); } 如果 $current_screen 还不是一个 WP_Screen 对象,就创建一个新的 WP_Screen 实例。

  3. 确定 $hook_name if ( empty( $hook_name ) ) { $hook_name = $hook_suffix; } 如果函数没有传入 $hook_name 参数,就使用全局变量 $hook_suffix 作为 $hook_name$hook_name 是一个重要的参数,它决定了 WP_Screen 对象会如何初始化。

  4. 避免重复设置: if ( did_action( 'current_screen' ) ) { return $current_screen; } 这个检查确保 WP_Screen 对象只会被初始化一次。did_action() 函数检查一个action hook是否已经被触发。如果 current_screen 这个action hook已经被触发,说明 WP_Screen 对象已经被初始化过了,直接返回 $current_screen

  5. 填充 WP_Screen 对象: $current_screen->populate( $hook_name ); 这是最关键的一步。populate() 方法是 WP_Screen 类的一个成员函数,它负责根据 $hook_name 来填充 WP_Screen 对象的各种属性。我们将会在下一站详细分析 populate() 方法。

  6. 触发 current_screen action hook: do_action( 'current_screen', $current_screen ); 这个action hook允许其他插件或主题在 WP_Screen 对象被初始化后进行一些自定义操作。

  7. 返回 $current_screen return $current_screen; 返回初始化后的 WP_Screen 对象。

第四站:WP_Screen::populate() 的奥秘

WP_Screen::populate() 方法是整个流程的核心。它位于 wp-includes/class-wp-screen.php 文件中。 让我们简单看一下 WP_Screen::populate() 的代码结构(只展示关键部分):

/**
 * Sets up the properties for the screen.
 *
 * @param string $hook_name Optional. The hook name.
 */
public function populate( $hook_name = '' ) {
    $this->id      = $hook_name;
    $this->base    = $this->id;
    $this->post_type = isset( $_REQUEST['post_type'] ) ? $_REQUEST['post_type'] : '';
    $this->taxonomy  = isset( $_REQUEST['taxonomy'] ) ? $_REQUEST['taxonomy'] : '';

    // Hack for those who forget to set $screen->post_type in admin_menu().
    if ( ! $this->post_type && 'edit.php' === $GLOBALS['pagenow'] ) {
        $this->post_type = 'post';
    }

    if ( isset( $_REQUEST['page'] ) ) {
        $this->in_admin = true;
        $this->parent_base = 'admin.php';
        $this->parent_file = 'admin.php?page=' . $_REQUEST['page'];
    } elseif ( 'index.php' === $GLOBALS['pagenow'] ) {
        $this->base = 'dashboard';
    }

    // Set up the screen options.
    $this->add_option( 'default_hidden_columns', array() );

    // Add help tabs.
    add_action( 'load-' . $this->id, array( $this, 'add_help_tabs' ) );
    add_action( 'load-' . $this->id, array( $this, 'add_screen_options' ) );
}

这个方法做了很多事情,简单来说,它就是根据传入的 $hook_name 以及全局变量 $pagenow$_REQUEST 中的信息,来设置 WP_Screen 对象的各种属性。

以下是一些重要的属性:

  • $id 页面的ID,通常等于 $hook_name
  • $base 页面的基本名称,通常也等于 $hook_name,但在某些情况下会被修改(例如,dashboard页面的 $base 是 ‘dashboard’)。
  • $post_type 如果页面与某个文章类型相关,则存储文章类型名称(例如 ‘post’, ‘page’)。从 $_REQUEST['post_type'] 获取。
  • $taxonomy 如果页面与某个分类法相关,则存储分类法名称。从 $_REQUEST['taxonomy'] 获取。
  • $parent_base 父页面的基本名称。
  • $parent_file 父页面的文件名。
  • $in_admin 一个布尔值,表示当前页面是否在后台管理界面。

populate() 方法还会调用其他方法来添加帮助标签(help tabs)和屏幕选项(screen options)。

第五站:实战演练:获取当前页面的 post_type

现在,我们已经了解了 get_current_screen() 的工作原理,让我们来看一个实际的例子:如何获取当前后台页面的 post_type

假设你正在编写一个插件,需要在文章编辑页面上添加一些自定义功能。你需要知道当前编辑的文章类型是 ‘post’ 还是 ‘page’,或者其他自定义文章类型。

你可以使用以下代码来获取 post_type

<?php
function my_plugin_get_current_post_type() {
    $screen = get_current_screen();

    if ( $screen && isset( $screen->post_type ) ) {
        return $screen->post_type;
    }

    return null; // 或者返回一个默认值
}

// 在你的插件代码中使用这个函数
$post_type = my_plugin_get_current_post_type();

if ( $post_type === 'post' ) {
    // 当前是文章编辑页面
    echo '当前是文章编辑页面';
} elseif ( $post_type === 'page' ) {
    // 当前是页面编辑页面
    echo '当前是页面编辑页面';
} else {
    // 当前是其他文章类型的编辑页面,或者不是文章编辑页面
    echo '当前是' . $post_type . '编辑页面';
}
?>

这段代码首先调用 get_current_screen() 获取 WP_Screen 对象。然后,检查 $screen 是否存在,并且是否设置了 post_type 属性。如果存在,就返回 post_type 的值,否则返回 null

第六站:常见问题和注意事项

  • get_current_screen() 只能在后台使用: get_current_screen() 函数主要用于获取后台页面的信息。如果在前端使用,可能会返回 null,或者返回一个不完整的 WP_Screen 对象。

  • get_current_screen()admin_init 之后可用: WP_Screen 对象通常在 admin_init action hook 之后才会被初始化。因此,如果你需要在插件或主题的代码中使用 get_current_screen(),最好在 admin_init 之后执行。

  • 注意检查 $screen 是否存在: 在使用 $screen 对象之前,一定要检查它是否为 null。例如:

    $screen = get_current_screen();
    if ( $screen ) {
        // 安全地使用 $screen 对象
        echo $screen->id;
    } else {
        // 处理 $screen 为 null 的情况
        echo '无法获取当前页面信息';
    }
  • $hook_suffix 的值: $hook_suffix 的值取决于当前页面的类型。例如,对于文章编辑页面,它可能是 post.php;对于页面编辑页面,它可能是 page.php;对于自定义文章类型的编辑页面,它可能是 edit.php?post_type=your_post_type

第七站:更高级的用法

除了获取 post_type 之外,WP_Screen 对象还提供了许多其他有用的属性和方法。例如:

  • $screen->id 获取当前页面的ID。
  • $screen->base 获取当前页面的基本名称。
  • $screen->is_block_editor() 判断当前页面是否是古腾堡编辑器页面。
  • $screen->add_help_tab() 向当前页面添加帮助标签。
  • $screen->add_option() 向当前页面添加屏幕选项。

你可以使用这些属性和方法来定制你的插件或主题,使其更好地与WordPress后台集成。

总结

get_current_screen() 函数是WordPress中一个非常重要的工具,它可以帮助你获取当前后台页面的信息,并根据这些信息来定制你的插件或主题。虽然它的源码看起来很简单,但它背后却隐藏着许多细节和技巧。希望通过今天的讲解,你能够更深入地理解 get_current_screen() 的工作原理,并在你的实际项目中灵活运用它。

记住,代码的世界充满了乐趣,让我们一起探索,一起成长!祝大家编程愉快!

发表回复

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