剖析 WordPress `add_meta_box()` 函数源码:自定义元数据框的注册与管理。

大家好,今天咱们来聊聊 WordPress 里一个神奇的函数 add_meta_box()。这货可是定制文章编辑界面,添加自定义元数据框的利器。想象一下,你想在写博客的时候,加个“心情指数”的选项,或者给旅游攻略文章加个“推荐指数”的评价,靠它就对了!

咱们先来热身一下,看看 add_meta_box() 的庐山真面目:

<?php
/**
 * 添加自定义元数据框
 *
 * @param string   $id         元数据框的 ID。必须是唯一的。
 * @param string   $title      元数据框的标题。
 * @param callable $callback   显示元数据框内容的函数。
 * @param string   $screen     (可选)在哪个屏幕上显示元数据框。可以是单个屏幕 ID、屏幕 ID 数组或屏幕对象。默认值为当前屏幕。
 * @param string   $context    (可选)元数据框的上下文。可以是 'normal'、'advanced' 或 'side'。默认值为 'advanced'。
 * @param string   $priority   (可选)元数据框的优先级。可以是 'high'、'core'、'default' 或 'low'。默认值为 'default'。
 * @param array    $callback_args (可选) 传递给回调函数的额外参数。
 */
function add_meta_box( string $id, string $title, callable $callback, string|array|WP_Screen|null $screen = null, string $context = 'advanced', string $priority = 'default', array $callback_args = array() ) {
    global $wp_meta_boxes;

    $screen = get_current_screen();
    if ( is_string( $screen ) ) {
        $screen = convert_to_screen( $screen );
    } elseif ( is_null( $screen ) ) {
        $screen = get_current_screen();
    }

    if ( ! is_object( $screen ) ) {
        return;
    }

    $page = $screen->id;

    // Normalize context to lower case.
    $context = strtolower( $context );

    if ( ! isset( $wp_meta_boxes ) ) {
        $wp_meta_boxes = array();
    }

    if ( ! isset( $wp_meta_boxes[ $page ] ) ) {
        $wp_meta_boxes[ $page ] = array();
    }

    if ( ! isset( $wp_meta_boxes[ $page ][ $context ] ) ) {
        $wp_meta_boxes[ $page ][ $context ] = array();
    }

    if ( ! isset( $wp_meta_boxes[ $page ][ $context ][ $priority ] ) ) {
        $wp_meta_boxes[ $page ][ $context ][ $priority ] = array();
    }

    $wp_meta_boxes[ $page ][ $context ][ $priority ][ $id ] = array(
        'id'            => $id,
        'title'         => $title,
        'callback'      => $callback,
        'args'          => $callback_args,
    );
}

看起来参数挺多,别怕!咱们一个个拆解:

参数 类型 描述 例子
$id string 元数据框的唯一 ID,就像你的身份证号,绝对不能重复! 'my_custom_meta_box'
$title string 元数据框的标题,就是显示在框框顶部的文字。 '我的自定义元数据框'
$callback callable 一个函数,用来生成元数据框的内容。这个函数里,你可以放各种 HTML 元素,比如输入框、下拉菜单等等。 'my_meta_box_callback'
$screen string|array|WP_Screen|null 指定在哪个页面显示元数据框。可以是文章类型(post、page)、自定义文章类型,或者一个 WP_Screen 对象。默认是当前页面。 'post''my_custom_post_type'
$context string 元数据框的位置。有三个选项:'normal'(正常位置,在编辑器下方),'advanced'(高级位置,和正常位置一样),'side'(侧边栏)。 'normal''side'
$priority string 元数据框的优先级。有四个选项:'high'(高优先级,靠上显示),'core'(核心优先级,一般不用),'default'(默认优先级),'low'(低优先级,靠下显示)。 'high''low'
$callback_args array 传递给 $callback 函数的额外参数。 array('key' => 'value')

咱们来举个栗子:

假设我们想给文章添加一个“作者心情”的元数据框,让作者可以选择“开心”、“一般”、“郁闷”三种心情。

首先,我们需要定义一个回调函数,用来生成元数据框的内容:

<?php
function my_meta_box_callback( $post ) {
    // 添加一个 nonce 字段,用于安全验证
    wp_nonce_field( basename(__FILE__), 'my_meta_box_nonce' );

    // 获取之前保存的作者心情
    $author_mood = get_post_meta( $post->ID, '_author_mood', true );

    // 如果没有保存过,就设置一个默认值
    if ( empty( $author_mood ) ) {
        $author_mood = '一般';
    }

    // 生成下拉菜单
    echo '<label for="author_mood">作者心情:</label>';
    echo '<select name="author_mood" id="author_mood">';
    echo '<option value="开心"' . selected( $author_mood, '开心', false ) . '>开心</option>';
    echo '<option value="一般"' . selected( $author_mood, '一般', false ) . '>一般</option>';
    echo '<option value="郁闷"' . selected( $author_mood, '郁闷', false ) . '>郁闷</option>';
    echo '</select>';
}

这个函数做了几件事:

  1. 添加 nonce 字段: 这是为了安全起见,防止跨站请求伪造(CSRF)攻击。
  2. 获取之前保存的作者心情: get_post_meta() 函数可以获取文章的元数据。我们使用 '_author_mood' 作为键名,存储作者的心情。
  3. 生成下拉菜单: 使用 HTML 的 <select> 元素生成一个下拉菜单,让作者选择心情。selected() 函数可以根据之前保存的值,自动选中对应的选项。

接下来,我们需要使用 add_meta_box() 函数,将这个元数据框添加到文章编辑界面:

<?php
function add_my_meta_box() {
    add_meta_box(
        'my_meta_box_id',          // ID,唯一标识符
        '作者心情',                // 标题
        'my_meta_box_callback',    // 回调函数
        'post',                   // 在文章类型为 post 的页面显示
        'side',                   // 位置:侧边栏
        'high'                    // 优先级:高
    );
}

add_action( 'add_meta_boxes', 'add_my_meta_box' );

这个函数做了两件事:

  1. 调用 add_meta_box() 函数: 传入各个参数,将元数据框添加到文章编辑界面。
  2. 使用 add_action() 函数:add_my_meta_box() 函数挂载到 add_meta_boxes 这个钩子上。WordPress 会在适当的时候调用这个函数,从而添加我们的元数据框。

最后,我们需要保存用户选择的心情:

<?php
function save_my_meta_box_data( $post_id ) {
    // 检查 nonce 字段
    if ( ! isset( $_POST['my_meta_box_nonce'] ) || ! wp_verify_nonce( $_POST['my_meta_box_nonce'], basename(__FILE__) ) ) {
        return;
    }

    // 检查用户是否有权限
    if ( ! current_user_can( 'edit_post', $post_id ) ) {
        return;
    }

    // 检查是否是自动保存
    if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
        return;
    }

    // 获取用户选择的心情
    if ( isset( $_POST['author_mood'] ) ) {
        $author_mood = sanitize_text_field( $_POST['author_mood'] );

        // 保存心情
        update_post_meta( $post_id, '_author_mood', $author_mood );
    }
}

add_action( 'save_post', 'save_my_meta_box_data' );

这个函数做了几件事:

  1. 检查 nonce 字段: 验证请求的合法性。
  2. 检查用户权限: 确保用户有权限编辑文章。
  3. 检查是否是自动保存: 如果是自动保存,就直接返回,不进行保存操作。
  4. 获取用户选择的心情: 使用 sanitize_text_field() 函数对用户输入进行安全过滤。
  5. 保存心情: 使用 update_post_meta() 函数将用户选择的心情保存到文章的元数据中。

把这些代码放到你的主题的 functions.php 文件中,或者放到一个自定义插件中,刷新一下文章编辑页面,你就能看到一个“作者心情”的元数据框了!

源码剖析:

现在,咱们深入 add_meta_box() 的源码,看看它到底做了些什么:

  1. 获取当前屏幕:

    <?php
    $screen = get_current_screen();
    if ( is_string( $screen ) ) {
        $screen = convert_to_screen( $screen );
    } elseif ( is_null( $screen ) ) {
        $screen = get_current_screen();
    }
    
    if ( ! is_object( $screen ) ) {
        return;
    }
    
    $page = $screen->id;

    这段代码首先获取当前屏幕的 WP_Screen 对象。WP_Screen 对象包含了当前页面的各种信息,比如页面 ID、文章类型等等。如果 $screen 是一个字符串(比如文章类型),就使用 convert_to_screen() 函数将其转换为 WP_Screen 对象。 如果获取不到 WP_Screen 对象,就直接返回,不进行任何操作。

  2. 规范化上下文:

    <?php
    $context = strtolower( $context );

    $context 参数转换为小写,确保后续的逻辑能够正确处理。

  3. 存储元数据框信息:

    <?php
    global $wp_meta_boxes;
    
    if ( ! isset( $wp_meta_boxes ) ) {
        $wp_meta_boxes = array();
    }
    
    if ( ! isset( $wp_meta_boxes[ $page ] ) ) {
        $wp_meta_boxes[ $page ] = array();
    }
    
    if ( ! isset( $wp_meta_boxes[ $page ][ $context ] ) ) {
        $wp_meta_boxes[ $page ][ $context ] = array();
    }
    
    if ( ! isset( $wp_meta_boxes[ $page ][ $context ][ $priority ] ) ) {
        $wp_meta_boxes[ $page ][ $context ][ $priority ] = array();
    }
    
    $wp_meta_boxes[ $page ][ $context ][ $priority ][ $id ] = array(
        'id'            => $id,
        'title'         => $title,
        'callback'      => $callback,
        'args'          => $callback_args,
    );

    这段代码是 add_meta_box() 函数的核心。它使用一个全局变量 $wp_meta_boxes,将元数据框的信息存储起来。$wp_meta_boxes 是一个多维数组,它的结构如下:

    $wp_meta_boxes[ 页面 ID ][ 上下文 ][ 优先级 ][ 元数据框 ID ] = 元数据框信息数组

    元数据框信息数组包含了元数据框的 ID、标题、回调函数和额外参数。

    add_meta_box() 函数只是将元数据框的信息存储起来,并没有真正地将元数据框显示在页面上。真正显示元数据框的是 do_meta_boxes() 函数。

do_meta_boxes() 函数:

do_meta_boxes() 函数负责在页面上显示元数据框。它的定义如下:

<?php
/**
 * 显示元数据框
 *
 * @param string|WP_Screen $screen  当前屏幕。
 * @param string           $context 元数据框的上下文。
 * @param WP_Post|null     $post    (可选)当前文章对象。
 */
function do_meta_boxes( $screen, $context, $post = null ) {
    global $wp_meta_boxes;

    if ( is_string( $screen ) || is_object( $screen ) ) {
        $screen = convert_to_screen( $screen );
    }

    $page = $screen->id;

    if ( empty( $wp_meta_boxes[ $page ][ $context ] ) ) {
        return;
    }

    $priority_order = array( 'high', 'core', 'default', 'low' );

    foreach ( $priority_order as $priority ) {
        if ( isset( $wp_meta_boxes[ $page ][ $context ][ $priority ] ) ) {
            foreach ( (array) $wp_meta_boxes[ $page ][ $context ][ $priority ] as $box ) {
                call_user_func( $box['callback'], $post, $box['args'] );
            }
        }
    }
}

这个函数做了几件事:

  1. 获取页面 ID:add_meta_box() 函数一样,首先获取当前页面的 ID。
  2. 检查是否有元数据框: 如果当前页面和上下文中没有元数据框,就直接返回。
  3. 遍历优先级: 按照 highcoredefaultlow 的顺序遍历优先级。
  4. 遍历元数据框: 遍历当前优先级下的所有元数据框。
  5. 调用回调函数: 调用元数据框的回调函数,生成元数据框的内容。

do_meta_boxes() 函数会在 WordPress 的模板文件中被调用,从而将元数据框显示在页面上。

总结:

add_meta_box() 函数和 do_meta_boxes() 函数是 WordPress 中自定义元数据框的核心。add_meta_box() 函数负责注册元数据框,将元数据框的信息存储起来。do_meta_boxes() 函数负责显示元数据框,调用元数据框的回调函数生成元数据框的内容。

高级用法:

除了基本的用法之外,add_meta_box() 函数还有一些高级用法:

  • 使用 WP_Screen 对象: 可以使用 WP_Screen 对象来指定在哪个页面显示元数据框。例如,可以使用 get_current_screen() 函数获取当前页面的 WP_Screen 对象,然后将其传递给 add_meta_box() 函数。
  • 传递额外参数: 可以使用 $callback_args 参数将额外参数传递给回调函数。例如,可以传递一个数组,包含一些配置信息。
  • 使用自定义文章类型: 可以将元数据框添加到自定义文章类型中。只需要将 $screen 参数设置为自定义文章类型的名称即可。
  • 使用不同的上下文: 可以使用不同的上下文来控制元数据框的位置。例如,可以使用 'side' 上下文将元数据框添加到侧边栏。

注意事项:

  • ID 必须唯一: 每个元数据框的 ID 必须是唯一的,否则会导致冲突。
  • 安全验证: 在保存元数据时,一定要进行安全验证,防止跨站请求伪造(CSRF)攻击。
  • 清理用户输入: 在保存用户输入之前,一定要对其进行清理,防止恶意代码注入。

希望今天的讲解能够帮助你更好地理解 WordPress 的 add_meta_box() 函数。掌握了它,你就可以自由地定制文章编辑界面,添加各种自定义元数据框,让你的网站更加个性化!

下次有机会,咱们再聊聊如何使用 JavaScript 和 CSS 来美化元数据框,让它们看起来更漂亮!

发表回复

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