好的,我们开始。
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中未显式指定。 - 类型转换: 将一些关键参数,如
public和hierarchical强制转换为布尔值。 - 依赖关系处理: 基于其他参数的值设置一些参数的默认值。例如,如果
public为true,则publicly_queryable默认为true。 has_archive处理: 如果has_archive为true,则将其设置为文章类型的名称。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 ): 添加对文章类型特性的支持,例如title、editor、thumbnail等。remove_supports( $feature ): 移除对文章类型特性的支持。set_props( $args ): 根据传入的参数设置文章类型的属性。get_labels(): 获取文章类型的标签。set_labels( $labels ): 设置文章类型的标签。get_capabilities(): 获取文章类型的能力。set_capabilities( $capabilities ): 设置文章类型的能力。
WP_Post_Type 类还定义了一些常量,用于表示文章类型的状态,例如 WP_POST_TYPE_PUBLIC 和 WP_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 |
一个包含文章类型支持的功能的数组,例如 title、editor、thumbnail 等。 |
array('title', 'editor') |
taxonomies |
一个包含与文章类型关联的分类法的数组,例如 category 和 post_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 的内容管理能力。