WordPress自定义文章类型register_post_type函数注册逻辑源码解析

好的,我们开始。

WordPress 自定义文章类型 register_post_type 函数注册逻辑源码解析

大家好,今天我们来深入探讨 WordPress 中 register_post_type 函数的注册逻辑,这是一个非常重要的函数,允许开发者创建自定义的文章类型,从而扩展 WordPress 的内容管理能力。我们将从函数定义开始,一步步解析其内部实现,并结合代码示例进行说明。

1. register_post_type 函数的基本结构

register_post_type 函数位于 wp-includes/post.php 文件中。其基本语法如下:

/**
 * Registers a new post type.
 *
 * @since 2.9.0
 *
 * @global array $wp_post_types An array of registered post types.
 *
 * @param string $post_type Post type key. Must not exceed 20 characters and may only contain lowercase alphanumeric characters, dashes, and underscores.
 * @param array|string $args {
 *     An array of arguments for registering a post type.
 *
 *     @type string       $label           Name of the post type shown in the menu. Usually plural.
 *                                         If not provided, the value of `$labels['name']` is used.
 *     @type array        $labels          An array of labels for this post type. If not provided, post labels are used.
 *                                         See {@link get_post_type_labels()} for a list of supported labels.
 *     @type string       $description     A short descriptive summary of what the post type is.
 *     @type bool         $public          Whether posts of this type should be shown in the admin UI. Default `false`.
 *     @type bool         $hierarchical    Whether the post type is hierarchical (e.g. pages). Allows Parent to be specified. Default `false`.
 *     @type bool         $exclude_from_search Whether to exclude posts with this type from front end search results. Default is the value of `$public`.
 *     @type bool         $publicly_queryable Whether posts of this type should be publicly queryable. Default is the value of `$public`.
 *     @type bool         $show_ui         Whether to generate and handle a UI for this post type in the admin. Default is the value of `$public`.
 *     @type bool         $show_in_menu    Whether to show the post type in the admin menu. If true, the post type is shown in its own top level menu.
 *                                         If false, no menu is shown. If a string of an existing top level menu (e.g. 'tools.php'
 *                                         or 'edit.php?post_type=page'), the post type will be placed as a sub-menu of that.
 *                                         Default is the value of `$show_ui`.
 *     @type bool         $show_in_admin_bar Whether to make this post type available in the WordPress admin bar. Default is the value of `$show_in_menu`.
 *     @type int          $menu_position   The position in the menu order this post type should appear. Warning: if two post types
 *                                         register with the same position attribute, one menu item may overwrite the other so that
 *                                         only one of the post types may be accessed from the menu. Default null (at the bottom).
 *     @type string       $menu_icon       The URL to the icon to be used for this menu. Pass a base64-encoded SVG using a data URI
 *                                         (https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs) or the name of a
 *                                         Dashicons helper class to use a built-in dashicon (https://developer.wordpress.org/resource/dashicons/).
 *                                         Default null - defaults to the posts icon.
 *     @type string|array $capability_type The string to use to build the read, edit, and delete capabilities. May be passed as an array
 *                                         to use singular and plural forms, such as `array('story', 'stories')`. Default 'post'.
 *     @type array        $capabilities    Array of capabilities for this post type. By default, capabilities are built from the `capability_type` string.
 *                                         See {@link get_post_type_capabilities()} for a list of supported capabilities.
 *     @type bool         $map_meta_cap    Whether to use the internal default meta capability handling. Default `true`.
 *     @type array        $supports        An alias for calling add_post_type_support() directly. False hides all screens.
 *                                         Default is an array containing 'title' and 'editor'.
 *     @type callable     $register_meta_box_cb A function that registers meta boxes for the post type.
 *     @type array        $taxonomies      An array of registered taxonomies like category or post_tag that will be used with this post type.
 *     @type bool         $has_archive     Whether there should be post type archives, or if a string, the archive slug to use.
 *                                         Will generate the proper rewrite rules if `$rewrite` is enabled. Default `false`.
 *     @type bool|array   $rewrite         Triggers the handling of rewrites for this post type. To prevent rewrite generation, set to `false`.
 *                                         If set to `true`, rewrites will be handled using the post type's key as the slug. Pass an
 *                                         array to customize the rewrite rules with any of these parameters:
 *         @type string    $slug         Customize the permalink slug. Default is the post type key.
 *         @type bool      $with_front   Should the permalink structure include the front base. Default `true`.
 *         @type bool      $feeds        Should feed links be made available for this post type. Default is the value of `$has_archive`.
 *         @type bool      $pages        Should pagination be allowed. Default `true`.
 *         @type string    $ep_mask      Assign an endpoint mask. For use with {@see add_rewrite_endpoint()}.
 *     @type string|bool  $query_var       Sets the query_var key for this post type. If set to `true`, the post type key will be used.
 *                                         If set to `false`, a query var is not created at all. Default is the post type key.
 *     @type callable     $update_count_callback A function that will be called when the count of an associated term is updated.
 *     @type string       $show_in_rest    Whether to expose this post type in the REST API. Set to true to enable this. Default `false`.
 *     @type string       $rest_base       The base path for this post type's REST API endpoint.
 *     @type string       $rest_controller_class The controller class to use for managing this post type in the REST API.
 *     @type bool         $delete_with_user Whether to delete posts of this type when the author of it is deleted from the system.
 *                                         Default `null`.
 *     @type bool         $_builtin        Whether the post type is a built-in type. This is for internal use only. Default `false`.
 *     @type string       '_edit_link'     URL to filter the edit link of the post type. This is for internal use only. Default `admin.php?page=edit&post_type=%s'.
 * }
 * @return WP_Post_Type|WP_Error The registered post type object on success, WP_Error object on failure.
 */
function register_post_type( $post_type, $args = array() ) {
    global $wp_post_types;

    // 参数校验和规范化将在后续步骤中详细介绍
    // ...

    // 创建 WP_Post_Type 对象
    $post_type_object = new WP_Post_Type( $post_type, $args );

    // 注册到全局变量
    $wp_post_types[ $post_type ] = $post_type_object;

    // 触发 action
    do_action( 'registered_post_type', $post_type, $post_type_object );

    return $post_type_object;
}

主要参数:

  • $post_type (string): 自定义文章类型的名称,长度不能超过 20 个字符,只能包含小写字母、数字、短横线和下划线。
  • $args (array|string): 一个包含各种参数的数组,用于配置自定义文章类型的行为和外观。

2. 参数校验与规范化

register_post_type 函数首先进行一系列的参数校验和规范化,以确保传入的参数有效且符合 WordPress 的规范。

    // Post type must not be longer than 20 characters.
    if ( strlen( $post_type ) > 20 ) {
        /* translators: %s: Post type. */
        return new WP_Error( 'post_type_length_invalid', sprintf( __( 'Post type names must not exceed 20 characters in length. %s exceeds this limit.' ), $post_type ) );
    }

    // Post type must be all lowercase and letters and numbers.
    if ( ! preg_match( '/^[a-z0-9_]+$/', $post_type ) ) {
        return new WP_Error( 'post_type_name_invalid', __( 'Post type names must be all lowercase letters and numbers.' ) );
    }

    // Filter the post type arguments.
    $args = apply_filters( 'register_post_type_args', $args, $post_type );

    $defaults = array(
        'labels'                => array(),
        'description'           => '',
        'public'                => false,
        'hierarchical'          => false,
        'exclude_from_search'   => null, // Default to value of ! $public.
        'publicly_queryable'    => null, // Default to value of $public.
        'show_ui'               => null, // Default to value of $public.
        'show_in_menu'          => null, // Default to value of $show_ui.
        'show_in_admin_bar'     => null, // Default to value of $show_in_menu.
        'menu_position'         => null,
        'menu_icon'             => null,
        'capability_type'       => 'post',
        'capabilities'          => array(),
        'map_meta_cap'          => true,
        'supports'              => array( 'title', 'editor' ),
        'register_meta_box_cb'  => '',
        'taxonomies'            => array(),
        'has_archive'           => false,
        'rewrite'               => true,
        'query_var'             => $post_type,
        'can_export'            => true,
        'delete_with_user'      => null,
        'show_in_rest'          => false,
        'rest_base'             => $post_type,
        'rest_controller_class' => 'WP_REST_Posts_Controller',
        '_builtin'              => false,
        '_edit_link'            => 'admin.php?page=edit&post_type=%s',
    );

    $args = wp_parse_args( $args, $defaults );

    // 强制转换为布尔值,以确保一致性。
    $args['public']      = (bool) $args['public'];
    $args['hierarchical']  = (bool) $args['hierarchical'];
    $args['show_in_rest'] = (bool) $args['show_in_rest'];

    // 根据 public 属性设置默认值
    if ( null === $args['publicly_queryable'] ) {
        $args['publicly_queryable'] = $args['public'];
    }

    if ( null === $args['exclude_from_search'] ) {
        $args['exclude_from_search'] = ! $args['public'];
    }

    if ( null === $args['show_ui'] ) {
        $args['show_ui'] = $args['public'];
    }

    if ( null === $args['show_in_menu'] ) {
        $args['show_in_menu'] = $args['show_ui'];
    }

    if ( null === $args['show_in_admin_bar'] ) {
        $args['show_in_admin_bar'] = $args['show_in_menu'];
    }

    if ( true === $args['has_archive'] ) {
        $args['has_archive'] = $post_type;
    }

    if ( false !== $args['has_archive'] ) {
        $args['has_archive'] = sanitize_title( $args['has_archive'] );
    }

    if ( is_array( $args['rewrite'] ) ) {
        $args['rewrite'] = wp_parse_args( $args['rewrite'], array(
            'slug'         => $post_type,
            'with_front'   => true,
            'feeds'        => $args['has_archive'],
            'pages'        => true,
            'ep_mask'      => EP_PERMALINK,
        ) );
    } elseif ( $args['rewrite'] === true ) {
        $args['rewrite'] = array(
            'slug'         => $post_type,
            'with_front'   => true,
            'feeds'        => $args['has_archive'],
            'pages'        => true,
            'ep_mask'      => EP_PERMALINK,
        );
    }

    if ( is_string( $args['capability_type'] ) ) {
        $args['capability_type'] = array( $args['capability_type'], $args['capability_type'] . 's' );
    }

    // Filter the arguments after the defaults are applied.
    $args = apply_filters( 'register_post_type_args', $args, $post_type );

这个过程包括:

  • 名称长度验证: 检查 $post_type 的长度是否超过 20 个字符。
  • 名称格式验证: 检查 $post_type 是否只包含小写字母、数字和下划线。
  • 参数合并: 使用 wp_parse_args 函数将传入的 $args 数组与默认参数 $defaults 数组合并。这确保了所有必需的参数都已设置,即使在 $args 中未显式指定。
  • 类型转换: 将一些关键参数,如 publichierarchical 强制转换为布尔值。
  • 依赖关系处理: 基于其他参数的值设置一些参数的默认值。例如,如果 publictrue,则 publicly_queryable 默认为 true
  • has_archive 处理: 如果 has_archivetrue,则将其设置为文章类型的名称。
  • rewrite 处理:rewrite 参数进行规范化,使其始终是一个数组。
  • capability_type 处理:capability_type 参数规范化为一个包含单数和复数形式的数组。
  • 应用过滤器: 使用 apply_filters 钩子允许其他插件或主题修改参数。

3. 创建 WP_Post_Type 对象

在参数校验和规范化之后,register_post_type 函数创建一个 WP_Post_Type 类的实例。WP_Post_Type 类定义了自定义文章类型的属性和方法。

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

WP_Post_Type 类的构造函数会进一步处理传入的参数,设置文章类型的各种属性,并生成默认的标签和能力。

4. 注册到全局变量

创建 WP_Post_Type 对象后,register_post_type 函数将其注册到全局变量 $wp_post_types 中。这使得 WordPress 能够访问和管理自定义文章类型。

global $wp_post_types;
$wp_post_types[ $post_type ] = $post_type_object;

5. 触发 Action

最后,register_post_type 函数触发一个名为 registered_post_type 的 action。这允许其他插件或主题在自定义文章类型注册后执行一些操作。

do_action( 'registered_post_type', $post_type, $post_type_object );

6. 返回 WP_Post_Type 对象

函数返回新创建的 WP_Post_Type 对象,如果注册过程中发生错误,则返回 WP_Error 对象。

return $post_type_object;

7. WP_Post_Type 类的内部逻辑

WP_Post_Type 类负责存储和管理自定义文章类型的属性。它包含以下关键方法:

  • __construct( $post_type, $args ): 构造函数,用于初始化文章类型的属性。
  • add_supports( $feature ): 添加对文章类型特性的支持,例如 titleeditorthumbnail 等。
  • remove_supports( $feature ): 移除对文章类型特性的支持。
  • set_props( $args ): 根据传入的参数设置文章类型的属性。
  • get_labels(): 获取文章类型的标签。
  • set_labels( $labels ): 设置文章类型的标签。
  • get_capabilities(): 获取文章类型的能力。
  • set_capabilities( $capabilities ): 设置文章类型的能力。

WP_Post_Type 类还定义了一些常量,用于表示文章类型的状态,例如 WP_POST_TYPE_PUBLICWP_POST_TYPE_HIERARCHICAL

8. 代码示例

下面是一个使用 register_post_type 函数注册自定义文章类型的示例:

add_action( 'init', 'register_book_post_type' );
function register_book_post_type() {
    $labels = array(
        'name'               => _x( '书籍', 'post type general name', 'textdomain' ),
        'singular_name'      => _x( '书籍', 'post type singular name', 'textdomain' ),
        'menu_name'          => _x( '书籍', 'admin menu', 'textdomain' ),
        'name_admin_bar'     => _x( '书籍', 'add new on admin bar', 'textdomain' ),
        'add_new'            => _x( '添加新书', 'book', 'textdomain' ),
        'add_new_item'       => __( '添加新书', 'textdomain' ),
        'new_item'           => __( '新书', 'textdomain' ),
        'edit_item'          => __( '编辑书籍', 'textdomain' ),
        'view_item'          => __( '查看书籍', 'textdomain' ),
        'all_items'          => __( '所有书籍', 'textdomain' ),
        'search_items'       => __( '搜索书籍', 'textdomain' ),
        'parent_item_colon'  => __( '父级书籍:', 'textdomain' ),
        'not_found'          => __( '未找到书籍', 'textdomain' ),
        'not_found_in_trash' => __( '回收站中未找到书籍', 'textdomain' ),
    );

    $args = array(
        'labels'             => $labels,
        'description'        => __( '描述书籍', 'textdomain' ),
        'public'             => true,
        'publicly_queryable' => true,
        'show_ui'            => true,
        'show_in_menu'       => true,
        'query_var'          => true,
        'rewrite'            => array( 'slug' => 'book' ),
        'capability_type'    => 'post',
        'has_archive'        => true,
        'hierarchical'       => false,
        'menu_position'      => 5,
        'supports'           => array( 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'comments' ),
        'show_in_rest'       => true,
    );

    register_post_type( 'book', $args );
}

这个示例注册了一个名为 book 的自定义文章类型,并设置了各种参数,例如标签、描述、可见性、rewrite 规则和支持的功能。

9. 关键参数解释

参数 描述 默认值
label 文章类型的名称,通常是复数形式。 $labels['name'] 的值
labels 一个包含各种标签的数组,用于自定义文章类型在管理界面中的显示方式。 根据文章类型名称生成默认标签
description 文章类型的简短描述。 空字符串
public 是否公开文章类型。如果为 true,则文章类型将显示在管理界面中,并且可以通过 URL 访问。 false
hierarchical 是否为层级结构。如果为 true,则文章类型将具有父子关系,类似于页面。 false
exclude_from_search 是否从搜索结果中排除文章类型。 !$public
publicly_queryable 是否可以通过 URL 查询文章类型。 $public
show_ui 是否在管理界面中显示文章类型的 UI。 $public
show_in_menu 是否在管理菜单中显示文章类型。 $show_ui
show_in_admin_bar 是否在管理栏中显示文章类型。 $show_in_menu
menu_position 文章类型在管理菜单中的位置。 null (底部)
menu_icon 文章类型在管理菜单中显示的图标。可以使用 Dashicons 或自定义 URL。 null (默认文章图标)
capability_type 用于构建文章类型的 read、edit 和 delete 能力的字符串。可以传递单数和复数形式的数组,例如 array('book', 'books') 'post'
capabilities 一个包含文章类型能力的数组。 根据 capability_type 生成默认能力
map_meta_cap 是否使用 WordPress 的默认 meta capability 处理。 true
supports 一个包含文章类型支持的功能的数组,例如 titleeditorthumbnail 等。 array('title', 'editor')
taxonomies 一个包含与文章类型关联的分类法的数组,例如 categorypost_tag array()
has_archive 是否具有文章类型存档页面。可以设置为 true 或自定义存档 slug。 false
rewrite 一个用于控制文章类型 URL rewrite 规则的数组。 true (使用文章类型名称作为 slug)
query_var 用于查询文章类型的 query_var 键。 文章类型名称
show_in_rest 是否在 REST API 中公开文章类型。 false
rest_base 文章类型在 REST API 中的基本路径。 文章类型名称

10. 注意事项

  • 文章类型的名称必须是唯一的,并且不能与 WordPress 的内置文章类型冲突。
  • 在注册自定义文章类型后,需要刷新 rewrite 规则,以便 WordPress 能够正确处理新的 URL。可以通过访问“设置”>“固定链接”页面并重新保存来刷新 rewrite 规则。
  • 自定义文章类型的能力与用户的角色和权限相关。需要确保用户具有访问和管理自定义文章类型的适当权限。
  • 使用 register_post_type_args 过滤器可以修改文章类型的参数,这允许其他插件或主题自定义文章类型的行为。

通过深入了解 register_post_type 函数的注册逻辑,我们可以更好地利用自定义文章类型来扩展 WordPress 的功能,并创建更强大的内容管理系统。

注册流程的总结与理解

register_post_type 函数经过严格的参数验证和规范化,创建 WP_Post_Type 对象并注册到全局变量,最后触发一个 action,使自定义文章类型能够被 WordPress 正确识别和管理。理解其内部逻辑有助于开发者更有效地利用这一函数扩展 WordPress 的内容管理能力。

发表回复

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