剖析 `get_current_screen()` 函数的源码，它在 WordPress 后台是如何确定当前页面的？

admin
Thu, 07 Aug 2025 10:07:13 +0000
PHP, current, get, 源码

好嘞，各位观众老爷们，欢迎来到“扒光 WordPress 后台小秘密”系列讲座。今天咱们要聊的是一个非常关键的函数：get_current_screen()，看看它到底是怎么摸清我们现在身处 WordPress 后台的哪个角落。 一、开场白：get_current_screen() 的重要性 在 WordPress 后台开发中，我们经常需要知道当前页面是文章编辑页、用户管理页、还是小工具设置页等等。get_current_screen() 函数就像一个老侦探，负责收集线索、分析证据，最终确定“案发现场”。 为什么要知道当前页面？原因很多： 加载特定页面的 CSS/JavaScript: 只有在特定页面才需要加载的脚本和样式，避免全局加载造成性能浪费。 修改特定页面的行为: 根据当前页面，调整表单验证规则、添加自定义按钮等等。 权限控制: 判断用户是否有权限访问当前页面，进行相应的权限检查。 追踪用户行为: 记录用户在特定页面的操作，用于分析和优化。 总之，get_current_screen() 是后台开发的基石，理解它的工作原理至关重要。 二、源码剖析：步步为营，抽丝剥茧 让我们深入 …

继续阅读“剖析 `get_current_screen()` 函数的源码，它在 WordPress 后台是如何确定当前页面的？”

The post 剖析 `get_current_screen()` 函数的源码，它在 WordPress 后台是如何确定当前页面的？ first appeared on 智猿学院-前后端，数据库，人工智能，云计算等领域前沿技术讲座.

好嘞，各位观众老爷们，欢迎来到“扒光 WordPress 后台小秘密”系列讲座。今天咱们要聊的是一个非常关键的函数：get_current_screen()，看看它到底是怎么摸清我们现在身处 WordPress 后台的哪个角落。

一、开场白：get_current_screen() 的重要性

在 WordPress 后台开发中，我们经常需要知道当前页面是文章编辑页、用户管理页、还是小工具设置页等等。get_current_screen() 函数就像一个老侦探，负责收集线索、分析证据，最终确定“案发现场”。

为什么要知道当前页面？原因很多：

加载特定页面的 CSS/JavaScript: 只有在特定页面才需要加载的脚本和样式，避免全局加载造成性能浪费。

修改特定页面的行为: 根据当前页面，调整表单验证规则、添加自定义按钮等等。

权限控制: 判断用户是否有权限访问当前页面，进行相应的权限检查。

追踪用户行为: 记录用户在特定页面的操作，用于分析和优化。

总之，get_current_screen() 是后台开发的基石，理解它的工作原理至关重要。

二、源码剖析：步步为营，抽丝剥茧

让我们深入 wp-includes/screen.php 文件，看看 get_current_screen() 的真面目。 为了方便讲解，我简化了一些代码，保留了核心逻辑。

<?php

/**
 * Retrieve the current screen object.
 *
 * @global WP_Screen $current_screen
 *
 * @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;
}

/**
 * Sets the current screen object.
 *
 * @global WP_Screen $current_screen
 * @global string    $pagenow
 * @global WP_Admin_Bar $wp_admin_bar
 *
 * @param WP_Screen|string|null $hook_name Optional. A hook name.
 */
function set_current_screen( $hook_name = '' ) {
    global $current_screen, $pagenow, $wp_admin_bar;

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

    // Populate admin menu variables.
    require_once ABSPATH . 'wp-admin/includes/admin.php';

    // Get the hook name.
    if ( empty( $hook_name ) ) {
        $hook_name = get_current_screen_hook();
    }

    $current_screen->set_hookname( $hook_name );
    $current_screen->populate_network_path();
    $current_screen->populate_base();
    $current_screen->populate_id();
    $current_screen->populate_key();
    $current_screen->populate_taxonomy();
    $current_screen->populate_post_type();

    // Add screen help tabs and help sidebar.
    do_action( 'load-' . $current_screen->id );
    do_action( 'current_screen', $current_screen );

    if ( is_admin_bar_showing() ) {
        require_once ABSPATH . 'wp-admin/includes/admin-bar.php';
        wp_admin_bar_init();
    }
}

/**
 * Get the current screen hook.
 *
 * @global string $pagenow
 * @global string $typenow
 * @global string $taxnow
 *
 * @return string|null The hook name of the current screen, or null if not set.
 */
function get_current_screen_hook() {
    global $pagenow, $typenow, $taxnow;

    $hook_name = false;

    if ( isset( $_GET['import'] ) ) {
        $hook_name = 'import';
    } elseif ( 'plugins.php' == $pagenow ) {
        $plugin = isset( $_REQUEST['plugin'] ) ? trim( sanitize_text_field( wp_unslash( $_REQUEST['plugin'] ) ) ) : '';
        if ( $plugin ) {
            $hook_name = 'plugin-install';
        } else {
            $hook_name = 'plugins';
        }
    } elseif ( 'themes.php' == $pagenow ) {
        $hook_name = 'themes';
    } elseif ( $typenow ) {
        $hook_name = get_plugin_page_hookname( 'edit.php?post_type=' . $typenow, '' );
    } elseif ( $taxnow ) {
        $hook_name = get_plugin_page_hookname( 'edit-tags.php?taxonomy=' . $taxnow, '' );
    } elseif ( isset( $_GET['page'] ) ) {
        $hook_name = get_plugin_page_hookname( $_GET['page'], '' );
    } elseif ( in_array( $pagenow, array( 'post.php', 'post-new.php' ) ) ) {
        $hook_name = 'post';
    } elseif ( in_array( $pagenow, array( 'media-upload.php', 'async-upload.php' ) ) ) {
        $hook_name = 'media-upload';
    } elseif ( in_array( $pagenow, array( 'index.php', 'wp-activate.php' ) ) ) {
        $hook_name = 'dashboard';
    } else {
        $hook_name = $pagenow;
    }

    return $hook_name;
}

class WP_Screen {
    public $id = '';
    public $taxonomy = '';
    public $post_type = '';
    public $base = '';
    public $action = '';
    public $parent_base = '';
    public $parent_file = '';
    public $screen_icon = '';
    public $help_tabs = array();
    public $help_sidebar = '';
    public $is_network = false;
    public $is_user = false;
    public $in_admin = true;
    private $key = null;
    private $hook_suffix;

    public function __construct( $hook_name = '', $parent_file = '' ) {
        if ( ! empty( $hook_name ) ) {
            $this->set_hookname( $hook_name );
        }

        if ( ! empty( $parent_file ) ) {
            $this->parent_file = $parent_file;
        }
    }

    public function set_hookname( $hook_name ) {
        $this->hook_suffix = preg_replace( '#W#', '', strtolower( $hook_name ) );
    }

    public function populate_network_path() {
        if ( ! function_exists( 'is_network_admin' ) ) {
            return;
        }

        $this->is_network = is_network_admin();
        $this->is_user    = is_user_admin();
    }

    public function populate_base() {
        $base = 'admin';

        if ( 'appearance_page_custom-header' === $this->id ) {
            $base = 'appearance_page_header';
        }

        if ( 'appearance_page_custom-background' === $this->id ) {
            $base = 'appearance_page_background';
        }

        $this->base = $base;
    }

    public function populate_id() {
        global $pagenow, $typenow, $taxnow;

        if ( $typenow ) {
            $this->id = "edit-{$typenow}";
        } elseif ( $taxnow ) {
            $this->id = "edit-{$taxnow}";
        } else {
            $this->id = $pagenow;
        }
    }

    public function populate_key() {
        $this->key = md5( serialize( array(
            'id'         => $this->id,
            'base'       => $this->base,
            'action'     => $this->action,
            'post_type'  => $this->post_type,
            'taxonomy'   => $this->taxonomy,
            'is_network' => $this->is_network,
            'is_user'    => $this->is_user,
        ) ) );
    }

    public function populate_taxonomy() {
        global $taxnow;

        if ( $taxnow ) {
            $this->taxonomy = $taxnow;
        }
    }

    public function populate_post_type() {
        global $typenow;

        if ( $typenow ) {
            $this->post_type = $typenow;
        }
    }

}

2.1 核心流程

1. get_current_screen(): 这是我们调用的函数。它首先检查全局变量 $current_screen 是否存在并且是一个 WP_Screen 对象。如果不是，它会调用 set_current_screen() 来初始化 $current_screen。最后，返回 $current_screen 对象。

2. set_current_screen(): 这个函数负责创建和初始化 WP_Screen 对象。

它首先创建一个新的 WP_Screen 对象（如果 $current_screen 还没有被创建）。

然后，它调用 get_current_screen_hook() 来获取当前页面的 "hook name"。这才是确定当前页面的关键步骤。

接下来，它使用 WP_Screen 对象的各种 populate_*() 方法，根据 hook name 以及全局变量（如 $pagenow, $typenow, $taxnow）来设置 WP_Screen 对象的各种属性，例如 id, base, post_type, taxonomy 等。

最后，它触发两个 action hook：load-$current_screen->id 和 current_screen，允许其他插件或主题在当前页面加载时执行自定义代码。

3. get_current_screen_hook(): 这个函数是整个流程中最复杂的部分，也是决定当前页面的核心逻辑所在。它通过检查各种全局变量和 $_GET 参数，来确定当前页面的 "hook name"。这个 hook name 最终会被用作 WP_Screen 对象的 id 属性的一部分。

2.2 get_current_screen_hook() 的逻辑

get_current_screen_hook() 函数使用一系列 if...elseif...else 语句，逐步缩小范围，确定当前页面。

条件 | 页面类型 | Hook Name
isset( $_GET['import'] ) | 导入页面 | 'import'
$pagenow == 'plugins.php' | 插件页面 | 如果有 $_REQUEST['plugin'] 参数，则是 'plugin-install'，否则是 'plugins'
$pagenow == 'themes.php' | 主题页面 | 'themes'
$typenow (当前文章类型) | 文章类型列表页面 | 'edit.php?post_type=' . $typenow 的钩子名称 (通过 get_plugin_page_hookname)
$taxnow (当前分类法) | 分类法列表页面 | 'edit-tags.php?taxonomy=' . $taxnow 的钩子名称 (通过 get_plugin_page_hookname)
isset( $_GET['page'] ) | 插件创建的页面 (通过 add_menu_page) | $_GET['page'] 的钩子名称 (通过 get_plugin_page_hookname)
$pagenow 是 'post.php' 或 'post-new.php' | 编辑或新建文章页面 | 'post'
$pagenow 是 'media-upload.php' 或 'async-upload.php' | 媒体上传页面 | 'media-upload'
$pagenow 是 'index.php' 或 'wp-activate.php' | 仪表盘或激活页面 | 'dashboard'
其他情况 | 其他页面 | $pagenow

2.3 WP_Screen 对象的属性

WP_Screen 类定义了许多属性，用于描述当前页面的各种信息。

属性 | 描述
id | 页面的唯一标识符。通常是 $pagenow、edit-$post_type 或 edit-$taxonomy。
taxonomy | 当前页面的分类法名称，如果适用。
post_type | 当前页面的文章类型名称，如果适用。
base | 页面的基本类型。通常是 'admin'。
action | 页面的动作，例如 'edit' 或 'add'。
parent_base | 父页面的基本类型。
parent_file | 父页面的文件名。
is_network | 是否是网络管理页面（在 Multisite 环境中）。
is_user | 是否是用户管理页面（在 Multisite 环境中）。

三、实战演练：代码示例，手把手教学

现在，让我们通过一些代码示例，看看如何使用 get_current_screen() 来实现一些常见的任务。

3.1 只在文章编辑页面加载 JavaScript

add_action( 'admin_enqueue_scripts', 'my_admin_enqueue_scripts' );

function my_admin_enqueue_scripts() {
    $screen = get_current_screen();

    if ( $screen && $screen->id == 'post' ) {
        wp_enqueue_script( 'my-custom-script', plugin_dir_url( __FILE__ ) . 'js/my-custom-script.js', array( 'jquery' ), '1.0', true );
    }
}

这段代码会在文章编辑页面加载 my-custom-script.js 文件。 admin_enqueue_scripts 是一个在后台加载脚本和样式时触发的 action hook。 我们首先获取当前屏幕对象，然后检查它的 id 属性是否等于 'post'。如果是，就加载我们的自定义脚本。

3.2 在特定插件页面添加自定义 Meta Box

假设我们有一个名为 "my-awesome-plugin" 的插件，并且它创建了一个名为 "my-awesome-page" 的后台页面。 我们想在这个页面上添加一个自定义 Meta Box。

add_action( 'add_meta_boxes', 'my_add_meta_boxes' );

function my_add_meta_boxes() {
    $screen = get_current_screen();

    if ( $screen && $screen->id == 'my-awesome-page' ) {
        add_meta_box(
            'my_meta_box',
            'My Awesome Meta Box',
            'my_meta_box_callback',
            $screen->id,
            'normal',
            'default'
        );
    }
}

function my_meta_box_callback( $post ) {
    // Meta Box 的内容
echo ' This is my awesome meta box! ';
}

这段代码会在 "my-awesome-page" 页面上添加一个名为 "My Awesome Meta Box" 的 Meta Box。 add_meta_boxes 是一个在添加 Meta Box 时触发的 action hook。 我们首先获取当前屏幕对象，然后检查它的 id 属性是否等于 'my-awesome-page'。如果是，就添加我们的自定义 Meta Box。 my_meta_box_callback 函数负责渲染 Meta Box 的内容。

3.3 根据文章类型显示不同的帮助文本

add_action( 'current_screen', 'my_add_help_tab' );

function my_add_help_tab( $screen ) {
    if ( 'edit-book' == $screen->id ) {
        $screen->add_help_tab( array(
            'id'      => 'book_help_tab',
            'title'   => 'Book Help',
'content' => ' This is help content for the Book post type. ',
        ) );
    } elseif ( 'edit-movie' == $screen->id ) {
        $screen->add_help_tab( array(
            'id'      => 'movie_help_tab',
            'title'   => 'Movie Help',
'content' => ' This is help content for the Movie post type. ',
        ) );
    }
}

这段代码会根据当前文章类型，在帮助选项卡中显示不同的帮助文本。 current_screen 是一个在当前屏幕对象被设置后触发的 action hook。 我们首先检查屏幕对象的 id 属性是否等于 'edit-book' 或 'edit-movie'。如果是，就添加相应的帮助选项卡。

四、注意事项：避坑指南，安全第一

在正确的 action hook 中使用 get_current_screen(): get_current_screen() 的返回值依赖于 WordPress 的加载顺序。 通常，在 admin_enqueue_scripts、add_meta_boxes 或 current_screen 等 action hook 中使用它是安全的。 在更早的 action hook 中使用它可能会导致 $current_screen 对象尚未被初始化。

检查返回值是否为 WP_Screen 对象: 在访问 $screen 对象的属性之前，务必检查它是否是 WP_Screen 对象，以避免潜在的错误。 可以使用 is_a( $screen, 'WP_Screen' ) 来进行检查。

谨慎使用 $_GET 和 $_POST 参数: 虽然 get_current_screen_hook() 函数会检查 $_GET 参数来确定当前页面，但在你的代码中直接使用 $_GET 和 $_POST 参数时要小心，确保进行适当的验证和清理，以防止安全漏洞（例如 XSS 攻击）。

了解不同页面的 id 属性: 不同的页面有不同的 id 属性。 可以使用 var_dump( $screen ) 来查看当前页面的完整 WP_Screen 对象，从而了解它的 id 属性和其他属性。

插件冲突: 某些插件可能会修改 $current_screen 对象或者 get_current_screen_hook() 的行为，导致你的代码无法正常工作。 在出现问题时，尝试禁用其他插件来排查冲突。

五、总结：掌握核心，融会贯通

get_current_screen() 函数是 WordPress 后台开发中一个非常重要的工具。 通过深入了解它的工作原理，我们可以更好地控制后台页面的行为，为用户提供更好的体验。 记住，理解源码是提升技能的关键。 希望今天的讲座能帮助你更好地理解 get_current_screen()，并在实际开发中灵活运用。

好了，今天的讲座就到这里。 感谢各位的观看，我们下期再见！ 如果有任何问题，欢迎留言讨论。 祝大家编程愉快！