深入理解 `add_menu_page()` 函数的源码,它是如何向 WordPress 后台添加顶级菜单页面的?

各位观众老爷,晚上好!我是你们今晚的WordPress源码深度游导游,咱们今天的主题是:深扒add_menu_page()这个家伙,看看它到底是怎么把顶级菜单塞进WordPress后台的。

准备好了吗?Let’s dive in!

一、初识add_menu_page():长得好看但你得懂

首先,我们来看看add_menu_page()长什么样,别光看脸,还得看内在:

add_menu_page(
    string   $page_title,
    string   $menu_title,
    string   $capability,
    string   $menu_slug,
    callable $function = '',
    string   $icon_url = '',
    int      $position = null
);

是不是感觉有点头大?别怕,我来给它翻译翻译:

  • $page_title: 页面标题,显示在浏览器标签栏和编辑页面顶部。 可以理解为“这页纸的抬头”。
  • $menu_title: 菜单标题,显示在WordPress后台的菜单栏。 也就是左边竖着那一溜的文字。
  • $capability: 权限,决定谁能看到这个菜单。 比如'manage_options'只有管理员才能看到。
  • $menu_slug: 菜单别名,用于生成URL,也是唯一标识符。 相当于身份证号。
  • $function: 回调函数,点击菜单后执行的函数,显示页面内容。 就是菜单点开后,你要展示什么东西。
  • $icon_url: 菜单图标URL,可以是Dashicons或者自定义图标。 让你的菜单看起来更漂亮。
  • $position: 菜单位置,决定菜单在菜单栏的排序。 数字越小越靠前。

简单来说,就是告诉WordPress:

"喂,WordPress,我要加个菜单! 抬头是$page_title,名字是$menu_title, 只有$capability权限的人才能看, 链接是$menu_slug, 点开后执行$function, 图标用$icon_url, 放在第$position个位置。"

二、add_menu_page()背后的故事:源码解读

光知道add_menu_page()怎么用还不够,我们要深入源码,看看它是怎么运作的。 别担心,我不会直接丢给你一堆代码让你自己啃,我会抽丝剥茧,把重要的部分拎出来讲。

  1. 入口:add_menu_page()只是个皮包公司

    实际上,add_menu_page()自己啥也不干,它只是调用了add_submenu_page()函数,并且把$menu_slug设置成了null。 这就有点像个中介,把任务转手给了别人。

    源码片段:

    function add_menu_page( string $page_title, string $menu_title, string $capability, string $menu_slug, callable $function = '', string $icon_url = '', int $position = null ) {
        global $menu, $submenu;
    
        $menu_slug = plugin_basename( $menu_slug );
    
        add_submenu_page( $menu_slug, $page_title, $menu_title, $capability, $menu_slug, $function, $position );
    
        return true;
    }

    注意 add_submenu_page( $menu_slug, $page_title, $menu_title, $capability, $menu_slug, $function, $position ); 这里, $menu_slug 竟然又作为第一个参数传递了进去,这是为什么呢? 别急,我们接着往下看 add_submenu_page() 的源码。

  2. 核心:add_submenu_page()才是真大佬

    add_submenu_page()负责将菜单项添加到全局变量$menu$submenu中。 这两个全局变量存储了WordPress后台菜单的所有信息。

    $menu: 是一个二维数组,存储顶级菜单的信息。

    $submenu: 是一个三维数组,存储子菜单的信息。 第一层是父菜单的$menu_slug,第二层是子菜单的索引,第三层是子菜单的详细信息。

    源码片段(简化版):

    function add_submenu_page( string $parent_slug, string $page_title, string $menu_title, string $capability, string $menu_slug, callable $function = '', int $position = null ) {
        global $menu, $submenu, $_wp_real_parent_file;
    
        $menu_slug = plugin_basename( $menu_slug );
        $parent_slug = plugin_basename( $parent_slug );
    
        // ... 一堆参数检查和处理 ...
    
        // 如果 $parent_slug 为 null,则表示添加顶级菜单
        if ( null === $parent_slug ) {
            // 找到合适的位置插入菜单
            if ( null === $position ) {
                $menu[] = array( $menu_title, $capability, $menu_slug, $page_title, 'menu-top ' . $menu_slug, $hookname, $icon_url );
            } else {
                $menu[ $position ] = array( $menu_title, $capability, $menu_slug, $page_title, 'menu-top ' . $menu_slug, $hookname, $icon_url );
            }
        } else {
            // 添加子菜单
            $submenu[ $parent_slug ][] = array( $menu_title, $capability, $menu_slug, $page_title );
        }
    
        // ... 其他处理 ...
    
        return $hookname;
    }

    重点解析:

    • if ( null === $parent_slug ): 这是判断是否添加顶级菜单的关键。 因为add_menu_page()会将$menu_slug设置为null,所以会进入这个分支。

    • $menu[] = array(...): 将菜单信息添加到$menu数组中。 $menu数组的每个元素都是一个数组,包含了菜单的标题、权限、别名、页面标题、CSS类、钩子名称和图标URL。

    • $submenu[ $parent_slug ][] = array(...): 如果$parent_slug不为null,则表示添加子菜单。 子菜单的信息会被添加到$submenu数组中,以父菜单的$menu_slug作为键。

    • $hookname: 这个非常重要,它是一个钩子名称,用于在菜单页面加载时执行回调函数。 WordPress会根据这个钩子名称来调用我们传入的$function

  3. 钩子的力量:load-{$hookname}

    还记得add_submenu_page()返回的$hookname吗? 它可不是摆设,而是连接菜单和回调函数的关键。

    WordPress会使用add_action()函数,将回调函数绑定到load-{$hookname}这个钩子上。 当用户点击菜单,页面即将加载时,这个钩子会被触发,回调函数就会被执行。

    源码片段:

    add_action( "load-$hookname", $function );

    也就是说,当用户点击菜单时,WordPress会:

    1. 加载菜单对应的页面。
    2. 触发load-{$hookname}钩子。
    3. 执行我们提供的$function回调函数,显示页面内容。

三、实战演练:手撸一个顶级菜单

理论讲了一堆,不如撸起袖子干一把。 我们来创建一个简单的顶级菜单,显示一个“Hello World”页面。

<?php
/**
 * Plugin Name: My Awesome Menu
 * Description: Adds a top-level menu to the WordPress admin.
 */

// 添加菜单
add_action( 'admin_menu', 'my_awesome_menu' );

function my_awesome_menu() {
    add_menu_page(
        'My Awesome Page Title', // 页面标题
        'My Awesome Menu',      // 菜单标题
        'manage_options',       // 权限
        'my-awesome-menu',      // 菜单别名
        'my_awesome_page_content', // 回调函数
        'dashicons-star-filled', // 图标
        20                      // 位置
    );
}

// 页面内容
function my_awesome_page_content() {
    echo '<h1>Hello World!</h1>';
    echo '<p>This is my awesome menu page!</p>';
}

代码解释:

  1. add_action( 'admin_menu', 'my_awesome_menu' );:admin_menu钩子上注册my_awesome_menu函数。 admin_menu钩子会在WordPress后台菜单加载时触发。

  2. my_awesome_menu(): 使用add_menu_page()添加菜单。

  3. my_awesome_page_content(): 回调函数,显示“Hello World”页面。

安装并激活这个插件,你就会在WordPress后台看到一个名为“My Awesome Menu”的顶级菜单,点击后会显示“Hello World!”页面。

四、更上一层楼:高级用法

add_menu_page()的功能远不止于此,我们还可以玩出更多花样。

  1. 自定义图标:

    除了Dashicons,你还可以使用自定义图标。 只需将$icon_url设置为图片的URL即可。

    add_menu_page(
        'My Awesome Page Title',
        'My Awesome Menu',
        'manage_options',
        'my-awesome-menu',
        'my_awesome_page_content',
        plugin_dir_url( __FILE__ ) . 'images/my-icon.png', // 自定义图标URL
        20
    );
  2. 动态菜单位置:

    你可以根据不同的条件,动态设置菜单的位置。

    $position = get_option( 'my_menu_position', 20 ); // 从数据库获取位置
    add_menu_page(
        'My Awesome Page Title',
        'My Awesome Menu',
        'manage_options',
        'my-awesome-menu',
        'my_awesome_page_content',
        'dashicons-star-filled',
        $position // 动态位置
    );
  3. 权限控制:

    更精细地控制菜单的访问权限。

    if ( current_user_can( 'edit_posts' ) ) { // 只有能编辑文章的用户才能看到
        add_menu_page(
            'My Awesome Page Title',
            'My Awesome Menu',
            'edit_posts', // 权限
            'my-awesome-menu',
            'my_awesome_page_content',
            'dashicons-star-filled',
            20
        );
    }

五、总结:add_menu_page()的精髓

我们来总结一下add_menu_page()的精髓:

  • 本质: 它是一个辅助函数,简化了添加顶级菜单的操作。
  • 核心: 它调用add_submenu_page()函数,将菜单信息添加到全局变量$menu中。
  • 关键: 它使用load-{$hookname}钩子,将回调函数绑定到菜单页面加载事件。

表格总结:

函数名 作用 核心变量 关键钩子
add_menu_page() 添加顶级菜单的辅助函数,实际调用add_submenu_page() $menu load-{$hookname}
add_submenu_page() 添加菜单(包括顶级菜单和子菜单)的核心函数,将菜单信息添加到$menu$submenu全局变量中。 $menu, $submenu load-{$hookname}
$menu 存储顶级菜单信息的全局变量,是一个二维数组。
$submenu 存储子菜单信息的全局变量,是一个三维数组。
load-{$hookname} 菜单页面加载时触发的钩子,用于执行回调函数,显示页面内容。

希望今天的讲解能够帮助你更深入地理解add_menu_page()函数。 记住,源码的世界充满了乐趣,只要你愿意探索,就能发现更多惊喜!

下次再见!

发表回复

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