分析 `register_post_type()` 函数的源码,它如何将一个新的文章类型添加到 WordPress 的核心数据结构中?

大家好,欢迎来到今天的“WordPress源码解剖”特别讲座!今天我们要扒的是WordPress里一个至关重要的函数:register_post_type()。这货可是WordPress定义文章类型的核心枢纽,理解它,你就理解了WordPress内容组织的底层逻辑。

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

第一部分:register_post_type() 的基本结构与参数

register_post_type(),顾名思义,就是注册文章类型的函数。 它接受两个主要参数:文章类型名称和一个参数数组。

register_post_type( string $post_type, array|string $args = array() ) : WP_Post_Type|WP_Error
  • $post_type (string, required): 文章类型的名称。 注意,这玩意儿必须是小写字母,可以包含数字和下划线,最多20个字符。 比如:'book', 'event', 'my_custom_post'.
  • $args (array|string, optional): 一个包含各种选项的数组,用于配置文章类型的行为和外观。 也可以传递一个字符串,通常是 true,用于启用默认行为。 默认值是一个空数组 array()

$args 数组里面可以包含的参数那是相当丰富,我们挑几个常用的说一下:

参数 类型 描述 默认值
labels array 一个关联数组,定义用于文章类型界面的各种标签。 自动生成
public bool 是否公开文章类型。 这决定了文章类型是否在前端可见,是否可以通过搜索找到,等等。 false
show_ui bool 是否在 WordPress 管理界面中显示文章类型的界面。 根据 public 的值决定
rewrite bool|array 控制文章类型的 URL 重写规则。 可以是一个布尔值,表示启用或禁用默认重写规则;也可以是一个数组,用于自定义重写规则。 true
supports array 一个数组,指定文章类型支持的功能,例如 title(标题), editor(编辑器), thumbnail(特色图像) 等等。 titleeditor
hierarchical bool 是否允许文章类型具有父子关系,类似页面。 false
taxonomies array 一个数组,指定文章类型关联的分类法。 空数组

第二部分:源码剖析——深入 register_post_type() 的内部世界

现在,让我们深入 wp-includes/post.php 文件,探究 register_post_type() 的源码,看看它到底做了些什么。

function register_post_type( string $post_type, array|string $args = array() ) : WP_Post_Type|WP_Error {
    global $wp_post_types;

    // Sanitize the post type name.
    $post_type = sanitize_key( $post_type );

    if ( empty( $post_type ) ) {
        return new WP_Error( 'post_type_length_invalid', __( 'Post type names must be between 1 and 20 characters in length.' ) );
    }

    if ( strlen( $post_type ) > 20 ) {
        return new WP_Error( 'post_type_length_invalid', __( 'Post type names must be between 1 and 20 characters in length.' ) );
    }

    if ( ! is_array( $args ) ) {
        $args = array( 'public' => (bool) $args );
    }

    $args = wp_parse_args( $args );

    if ( isset( $wp_post_types[ $post_type ] ) ) {
        /**
         * Fires before a post type is registered.
         *
         * @since 3.0.0
         *
         * @param string $post_type Post type key.
         * @param array  $args      Array of arguments for registering a post type.
         */
        do_action( 'unregister_post_type', $post_type, $args );

        return new WP_Error( 'post_type_exists', __( 'Post type already registered.' ) );
    }

    // Back compat with quirky plugins.
    $args['name'] = $post_type;

    $post_type_object = new WP_Post_Type( $post_type, $args );

    $wp_post_types[ $post_type ] = $post_type_object;

    add_post_type_support( $post_type );

    /**
     * Fires after a post type is registered.
     *
     * @since 3.0.0
     *
     * @param string       $post_type        Post type key.
     * @param WP_Post_Type $post_type_object The registered post type object.
     */
    do_action( 'registered_post_type', $post_type, $post_type_object );

    return $post_type_object;
}

我们来逐行解读一下:

  1. global $wp_post_types;: 声明全局变量 $wp_post_types。这个全局变量是 WordPress 存储所有已注册文章类型对象的地方。 就像一个巨大的文章类型登记簿。

  2. $post_type = sanitize_key( $post_type );: 对文章类型名称进行清理,确保它是一个有效的键名。 这能防止一些恶意代码注入,保证程序的安全性。

  3. if ( empty( $post_type ) ) { ... }if ( strlen( $post_type ) > 20 ) { ... }: 对文章类型名称的长度进行验证。 必须在1到20个字符之间。 否则,返回一个 WP_Error 对象。

  4. if ( ! is_array( $args ) ) { $args = array( 'public' => (bool) $args ); }: 如果传入的 $args 不是数组,则将其转换为一个数组,并将 $args 的值赋给 'public' 键。 这主要是为了兼容旧版本的代码。

  5. $args = wp_parse_args( $args );: 使用 wp_parse_args() 函数将传入的 $args 数组与默认参数合并。 这确保了所有必需的参数都被设置,即使它们没有在传入的 $args 数组中显式指定。

  6. if ( isset( $wp_post_types[ $post_type ] ) ) { ... }: 检查文章类型是否已经注册。 如果已经注册,则触发 unregister_post_type 动作,并返回一个 WP_Error 对象。 避免重复注册导致冲突。

  7. $args['name'] = $post_type;: 将文章类型名称赋给 $args['name']。 这主要是为了兼容一些古老的插件。

  8. $post_type_object = new WP_Post_Type( $post_type, $args );: 创建一个 WP_Post_Type 类的实例,并将文章类型名称和参数传递给构造函数。 WP_Post_Type 类定义了文章类型的属性和方法。

  9. $wp_post_types[ $post_type ] = $post_type_object;: 将新创建的 WP_Post_Type 对象存储到全局变量 $wp_post_types 中。 这就是文章类型被添加到 WordPress 核心数据结构的关键一步。

  10. add_post_type_support( $post_type );: 为文章类型添加默认的文章类型支持。

  11. do_action( 'registered_post_type', $post_type, $post_type_object );: 触发 registered_post_type 动作。 允许其他插件或主题在文章类型注册后执行一些自定义操作。

  12. return $post_type_object;: 返回新创建的 WP_Post_Type 对象。

第三部分:WP_Post_Type 类——文章类型的蓝图

WP_Post_Type 类是定义文章类型属性和行为的蓝图。 让我们看看它的重要成员:

属性 类型 描述
$name string 文章类型的名称。
$label string 文章类型的单数名称,用于用户界面。
$labels object 一个对象,包含用于文章类型界面的各种标签。
$description string 文章类型的描述。
$public bool 是否公开文章类型。
$show_ui bool 是否在 WordPress 管理界面中显示文章类型的界面。
$show_in_menu bool 是否在管理菜单中显示文章类型。
$show_in_rest bool 是否在 REST API 中显示文章类型。
$rest_base string REST API 的基本路由。
$rest_controller_class string REST API 控制器的类名。
$show_in_nav_menus bool 是否在导航菜单中显示文章类型。
$show_in_admin_bar bool 是否在管理栏中显示文章类型。
$menu_position int 文章类型在管理菜单中的位置。
$menu_icon string 文章类型在管理菜单中显示的图标。
$capability_type string|array 用于文章类型的权限类型。
$capabilities array 一个数组,包含文章类型的权限。
$map_meta_cap bool 是否将文章类型的权限映射到默认的 WordPress 权限。
$hierarchical bool 是否允许文章类型具有父子关系。
$supports array 一个数组,指定文章类型支持的功能。
$taxonomies array 一个数组,指定文章类型关联的分类法。
$has_archive bool|string 是否启用文章类型的存档页面。
$rewrite bool|array 控制文章类型的 URL 重写规则。
$query_var bool|string 控制文章类型的查询变量。
$can_export bool 是否允许导出文章类型。
$delete_with_user bool 是否在用户删除时删除文章类型。
$_builtin bool 是否是内置的文章类型。
$_edit_link string 编辑链接的格式。

第四部分: 实践案例——注册一个“电影”文章类型

让我们用一个例子来演示如何使用 register_post_type() 注册一个名为 "movie" 的文章类型。

add_action( 'init', 'create_movie_post_type' );
function create_movie_post_type() {
    $labels = array(
        'name'               => _x( 'Movies', 'post type general name', 'my-theme' ),
        'singular_name'      => _x( 'Movie', 'post type singular name', 'my-theme' ),
        'menu_name'          => _x( 'Movies', 'admin menu', 'my-theme' ),
        'name_admin_bar'     => _x( 'Movie', 'add new on admin bar', 'my-theme' ),
        'add_new'            => _x( 'Add New', 'movie', 'my-theme' ),
        'add_new_item'       => __( 'Add New Movie', 'my-theme' ),
        'new_item'           => __( 'New Movie', 'my-theme' ),
        'edit_item'          => __( 'Edit Movie', 'my-theme' ),
        'view_item'          => __( 'View Movie', 'my-theme' ),
        'all_items'          => __( 'All Movies', 'my-theme' ),
        'search_items'       => __( 'Search Movies', 'my-theme' ),
        'parent_item_colon'  => __( 'Parent Movies:', 'my-theme' ),
        'not_found'          => __( 'No movies found.', 'my-theme' ),
        'not_found_in_trash' => __( 'No movies found in Trash.', 'my-theme' )
    );

    $args = array(
        'labels'             => $labels,
        'public'             => true,
        'publicly_queryable' => true,
        'show_ui'            => true,
        'show_in_menu'       => true,
        'query_var'          => true,
        'rewrite'            => array( 'slug' => 'movie' ),
        'capability_type'    => 'post',
        'has_archive'        => true,
        'hierarchical'       => false,
        'menu_position'      => null,
        'supports'           => array( 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'comments' ),
        'taxonomies'          => array( 'category', 'post_tag' )
    );

    register_post_type( 'movie', $args );
}

这段代码做了以下几件事:

  1. add_action( 'init', 'create_movie_post_type' );: 将 create_movie_post_type() 函数挂载到 init 动作钩子上。 这意味着该函数将在 WordPress 初始化时被执行。

  2. $labels 数组: 定义了用于 "movie" 文章类型的各种标签。 这些标签将在 WordPress 管理界面中使用。

  3. $args 数组: 定义了 "movie" 文章类型的各种选项。 例如,'public' => true 表示该文章类型是公开的,'rewrite' => array( 'slug' => 'movie' ) 表示该文章类型的 URL slug 是 "movie",'supports' => array( 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'comments' ) 表示该文章类型支持标题、编辑器、作者、特色图像、摘要和评论等功能。

  4. register_post_type( 'movie', $args );: 使用 register_post_type() 函数注册 "movie" 文章类型。

第五部分:add_post_type_support() 函数

我们之前提到 register_post_type() 函数内部调用了 add_post_type_support() 函数。 这个函数的作用是为文章类型添加默认的文章类型支持。 让我们看看它的源码:

function add_post_type_support( string|array $post_types, string|array ...$features ) : void {
    global $_wp_post_type_features;

    if ( ! is_array( $post_types ) ) {
        $post_types = array( $post_types );
    }

    foreach ( $post_types as $post_type ) {
        if ( ! isset( $_wp_post_type_features[ $post_type ] ) ) {
            $_wp_post_type_features[ $post_type ] = array();
        }

        foreach ( $features as $feature ) {
            $_wp_post_type_features[ $post_type ][ $feature ] = true;
        }
    }
}
  1. global $_wp_post_type_features;: 声明全局变量 $_wp_post_type_features。 这个全局变量是 WordPress 存储所有文章类型支持的功能的地方。
  2. 类型检查和转换: 确保 $post_types 是一个数组。
  3. 循环添加特性: 遍历所有指定的文章类型,并为每个文章类型添加所有指定的功能。

第六部分:总结与注意事项

通过今天的讲解,我们深入了解了 register_post_type() 函数的内部机制,以及它如何将新的文章类型添加到 WordPress 的核心数据结构中。 掌握了这个函数,你就可以灵活地定制 WordPress 的内容组织方式,创建各种各样的自定义文章类型,满足你的各种需求。

最后,总结几个注意事项:

  • 文章类型名称必须是小写字母,可以包含数字和下划线,最多20个字符。
  • $args 数组中的参数可以控制文章类型的各种行为和外观。
  • WP_Post_Type 类是定义文章类型属性和行为的蓝图。
  • add_post_type_support() 函数用于为文章类型添加默认的文章类型支持。
  • 注册文章类型时,要考虑 SEO 优化,合理设置 URL 重写规则。

希望今天的讲座对你有所帮助! 谢谢大家!

发表回复

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