分析 WordPress `WP_Post_Type` 类的源码:如何通过 `$wp_post_types` 全局变量存储文章类型配置。

各位观众,晚上好!欢迎来到今天的“WordPress源码解剖”节目。我是你们的老朋友,今天咱们要聊点儿实在的,扒一扒WordPress里负责管理文章类型的“管家”——WP_Post_Type类,以及它背后的秘密武器:$wp_post_types全局变量。

准备好了吗?咱们这就开始“解剖”!

第一幕:WP_Post_Type类——文章类型的“身份证”

首先,咱们得认识一下主角WP_Post_Type类。这哥们儿就像文章类型的“身份证”,里面记录了各种关于文章类型的重要信息。

<?php
/**
 * Core class used to implement a post type object.
 *
 * @since 4.4.0
 *
 * @see register_post_type()
 */
class WP_Post_Type {

    /**
     * Post type key.
     *
     * @since 4.4.0
     * @var string
     */
    public $name;

    /**
     * Post type object properties.
     *
     * @since 4.4.0
     * @var object
     */
    public $labels;

    /**
     * Post type arguments.
     *
     * @since 4.4.0
     * @var array
     */
    public $args;

    /**
     * Constructor.
     *
     * @since 4.4.0
     *
     * @param string $post_type Post type key. Must not exceed 20 characters and may only contain lowercase alphanumeric characters, dashes, and underscores. See {@link register_post_type()}.
     * @param array|string $args  Optional. Array or string of arguments for registering a post type.
     */
    public function __construct( $post_type, $args = array() ) {
        $this->name = $post_type;

        $this->set_props( $args );
    }

    /**
     * Sets post type properties.
     *
     * @since 4.4.0
     *
     * @param array|string $args Array or string of arguments for registering a post type.
     */
    protected function set_props( $args ) {
        $args = wp_parse_args( $args );

        $this->args = (object) $args;

        $this->add_supports();

        $this->set_labels();
    }

    /**
     * Adds default support values.
     *
     * @since 4.7.0
     */
    protected function add_supports() {
        $supports = array();

        if ( isset( $this->args->supports ) ) {
            $supports = $this->args->supports;
            if ( ! is_array( $supports ) ) {
                $supports = array( $supports => true );
            }
        }

        $this->supports = $supports;

        unset( $this->args->supports ); // Prevent clashes with the property.
    }

    /**
     * Sets the labels property for the post type based on the arguments passed.
     *
     * @since 4.4.0
     */
    protected function set_labels() {
        $labels = array();

        if ( isset( $this->args->labels ) ) {
            $labels = $this->args->labels;
            unset( $this->args->labels ); // Prevent clashes with the property.
        }

        $this->labels = (object) _get_post_type_labels( (object) array( 'name' => $this->name ), $labels );
    }

    /**
     * Gets an object containing the names for the post type labels property.
     *
     * @since 4.4.0
     *
     * @return object Object containing the names for the post type labels property.
     */
    public function get_labels() {
        return $this->labels;
    }

    /**
     * Adds filter to allow taxonomy to be associated with the post type.
     *
     * @since 4.4.0
     */
    public function add_rewrite_rules() {
        add_filter( 'rewrite_rules_array', array( $this, 'rewrite_rules_filter' ) );
    }

    /**
     * Remove filter to disallow taxonomy to be associated with the post type.
     *
     * @since 4.4.0
     */
    public function remove_rewrite_rules() {
        remove_filter( 'rewrite_rules_array', array( $this, 'rewrite_rules_filter' ) );
    }

    /**
     * Filters the rewrite rules for the post type.
     *
     * @since 4.4.0
     *
     * @param array $rules Array of rewrite rules.
     * @return array Array of rewrite rules.
     */
    public function rewrite_rules_filter( $rules ) {
        $post_rewrite = $this->rewrite_base();

        if ( false === $post_rewrite ) {
            return $rules;
        }

        $slug = $post_rewrite['slug'];
        if ( ! isset( $post_rewrite['ep_mask'] ) ) {
            $ep_mask = EP_PERMALINK;
        } else {
            $ep_mask = $post_rewrite['ep_mask'];
        }

        $new_rules = array();
        if ( $this->query_var ) {
            $new_rules[ $slug . '/?$' ] = 'index.php?' . $this->query_var . '=$matches[1]';
            $new_rules[ $slug . '/([^/]+)/?$' ] = 'index.php?' . $this->query_var . '=$matches[1]';
        } else {
            $new_rules[ $slug . '/?$' ] = 'index.php?post_type=' . $this->name;
            $new_rules[ $slug . '/([^/]+)/?$' ] = 'index.php?post_type=' . $this->name . '&name=$matches[1]';
        }

        return $new_rules + $rules;
    }

    /**
     * Returns the base rewrite rule for the post type.
     *
     * @since 4.4.0
     *
     * @return array|false Array containing the rewrite base, or false if the post type should not be rewritten.
     */
    protected function rewrite_base() {
        if ( ! $this->public ) {
            return false;
        }

        if ( ! $this->rewrite ) {
            return false;
        }

        $rewrite = $this->rewrite;

        if ( ! is_array( $rewrite ) ) {
            return array( 'slug' => $this->name );
        }

        if ( ! isset( $rewrite['slug'] ) ) {
            $rewrite['slug'] = $this->name;
        }

        return $rewrite;
    }

    /**
     * Adds support for a feature to the post type.
     *
     * @since 4.4.0
     *
     * @param string $feature Post type support key.
     */
    public function add_support( $feature ) {
        $this->supports[ $feature ] = true;
    }

    /**
     * Removes support for a feature from the post type.
     *
     * @since 4.4.0
     *
     * @param string $feature Post type support key.
     */
    public function remove_support( $feature ) {
        unset( $this->supports[ $feature ] );
    }

    /**
     * Checks if the post type supports a given feature.
     *
     * @since 4.4.0
     *
     * @param string $feature Post type support key.
     * @return bool True if the post type supports the feature, false otherwise.
     */
    public function supports( $feature ) {
        if ( isset( $this->supports[ $feature ] ) ) {
            return $this->supports[ $feature ];
        }

        return false;
    }

}

简单来说,WP_Post_Type类主要负责:

  • 存储文章类型的信息: 例如,文章类型的名称(name),标签(labels),参数(args)等等。
  • 处理文章类型的相关操作: 例如,添加/移除对特定功能的支持(add_support, remove_support),过滤重写规则(rewrite_rules_filter)等等。

咱们来看个例子,假设我们要注册一个名为book的文章类型:

$args = array(
    'labels' => array(
        'name' => '书籍',
        'singular_name' => '书籍'
    ),
    'public' => true,
    'has_archive' => true,
    'supports' => array( 'title', 'editor', 'thumbnail' )
);

$book_post_type = new WP_Post_Type( 'book', $args );

这段代码创建了一个WP_Post_Type类的实例$book_post_type,并将book文章类型的相关信息存储在这个实例中。

第二幕:$wp_post_types——文章类型的“户口登记处”

光有“身份证”还不行,还得去“户口登记处”登记一下,这样WordPress才能知道有这么个文章类型存在。这个“户口登记处”就是全局变量$wp_post_types

$wp_post_types是一个全局数组,它的作用是将所有注册的文章类型的WP_Post_Type对象存储起来。 就像一个大仓库,专门存放文章类型的信息。

在WordPress的wp-includes/post.php文件中,你可以找到这个全局变量的定义:

global $wp_post_types;
$wp_post_types = array();

看到了吗?它就是一个简单的数组。

第三幕:register_post_type()——注册文章类型的“窗口”

那么,如何把文章类型的“身份证”放到$wp_post_types这个“户口登记处”呢?答案就是register_post_type()函数。

register_post_type()函数是WordPress提供的一个核心函数,用于注册文章类型。它的主要作用就是:

  1. 创建一个WP_Post_Type类的实例。
  2. 将这个实例存储到$wp_post_types全局变量中。

咱们来看一下register_post_type()函数的简化版代码:

function register_post_type( $post_type, $args = array() ) {
    global $wp_post_types;

    // 1. 创建 WP_Post_Type 类的实例
    $wp_post_types[ $post_type ] = new WP_Post_Type( $post_type, $args );

    // 2. 返回 WP_Post_Type 类的实例
    return $wp_post_types[ $post_type ];
}

可以看到,register_post_type()函数首先创建了一个WP_Post_Type类的实例,然后将这个实例以文章类型名称为键名,存储到$wp_post_types全局变量中。

也就是说,当我们调用register_post_type( 'book', $args )时,WordPress会创建一个WP_Post_Type对象,然后将其存储到$wp_post_types['book']中。

第四幕:get_post_type_object()——获取文章类型的“查询机”

既然有了“户口登记处”,肯定也得有“查询机”,方便我们查询文章类型的信息。这个“查询机”就是get_post_type_object()函数。

get_post_type_object()函数的作用是根据文章类型名称,从$wp_post_types全局变量中获取对应的WP_Post_Type对象。

咱们来看一下get_post_type_object()函数的简化版代码:

function get_post_type_object( $post_type ) {
    global $wp_post_types;

    if ( isset( $wp_post_types[ $post_type ] ) ) {
        return $wp_post_types[ $post_type ];
    }

    return null;
}

可以看到,get_post_type_object()函数首先检查$wp_post_types全局变量中是否存在指定文章类型的WP_Post_Type对象,如果存在,则直接返回该对象,否则返回null

例如,我们可以这样获取book文章类型的WP_Post_Type对象:

$book_post_type = get_post_type_object( 'book' );

if ( $book_post_type ) {
    echo '文章类型名称:' . $book_post_type->name . '<br>';
    echo '是否公开:' . ($book_post_type->public ? '是' : '否') . '<br>';
}

第五幕:get_post_types()——获取所有文章类型的“花名册”

有时候,我们需要获取所有已注册的文章类型,而不是单个文章类型的信息。这时候,就需要用到get_post_types()函数。

get_post_types()函数的作用是获取所有已注册的文章类型的名称。它会遍历$wp_post_types全局变量,然后返回一个包含所有文章类型名称的数组。

咱们来看一下get_post_types()函数的简化版代码:

function get_post_types( $args = array(), $output = 'names' ) {
    global $wp_post_types;

    $names = array_keys( $wp_post_types );

    return $names;
}

可以看到,get_post_types()函数直接使用了array_keys()函数,从$wp_post_types全局变量中提取所有键名,也就是文章类型的名称。

例如,我们可以这样获取所有已注册的文章类型:

$post_types = get_post_types();

echo '所有文章类型:<br>';
foreach ( $post_types as $post_type ) {
    echo $post_type . '<br>';
}

第六幕:unregister_post_type()——注销文章类型的“销户窗口”

如果有一天,我们不再需要某个文章类型了,就可以使用unregister_post_type()函数将其注销。

unregister_post_type()函数的作用是从$wp_post_types全局变量中移除指定文章类型的WP_Post_Type对象。

咱们来看一下unregister_post_type()函数的简化版代码:

function unregister_post_type( $post_type ) {
    global $wp_post_types;

    if ( isset( $wp_post_types[ $post_type ] ) ) {
        unset( $wp_post_types[ $post_type ] );
        return true;
    }

    return false;
}

可以看到,unregister_post_type()函数直接使用了unset()函数,从$wp_post_types全局变量中删除指定键名的元素,也就是文章类型的WP_Post_Type对象。

总结:WP_Post_Type$wp_post_types的“二人转”

现在,咱们来总结一下WP_Post_Type类和$wp_post_types全局变量之间的关系:

角色 职责
WP_Post_Type 存储文章类型的具体信息,例如名称、标签、参数等等。
$wp_post_types 存储所有已注册的文章类型的WP_Post_Type对象,相当于一个文章类型的“户口登记处”。
register_post_type() 注册文章类型,将WP_Post_Type对象存储到$wp_post_types中。
get_post_type_object() 获取指定文章类型的WP_Post_Type对象,从$wp_post_types中查询。
get_post_types() 获取所有已注册的文章类型名称,遍历$wp_post_types
unregister_post_type() 注销文章类型,从$wp_post_types中移除WP_Post_Type对象。

它们之间的关系就像一对“二人转”演员,WP_Post_Type负责提供“唱腔”(文章类型的信息),$wp_post_types负责搭建“舞台”(存储和管理文章类型)。

实战演练:修改文章类型的标签

最后,咱们来个实战演练,演示如何通过$wp_post_types全局变量修改文章类型的标签。

假设我们要修改post文章类型的“文章”标签为“新闻”。我们可以这样做:

global $wp_post_types;

if ( isset( $wp_post_types['post'] ) ) {
    $wp_post_types['post']->labels->name = '新闻';
    $wp_post_types['post']->labels->singular_name = '新闻';
}

这段代码首先获取post文章类型的WP_Post_Type对象,然后直接修改其labels属性的namesingular_name属性。

需要注意的是,这种方式修改文章类型的信息可能会导致一些问题,例如,一些插件或主题可能缓存了文章类型的信息,导致修改无效。因此,建议使用WordPress提供的register_post_type_args过滤器来修改文章类型的信息,这样可以确保修改的兼容性。

结语

好了,今天的“WordPress源码解剖”节目就到这里了。希望通过今天的讲解,大家对WP_Post_Type类和$wp_post_types全局变量有了更深入的了解。

记住,掌握了这些知识,你就可以更好地管理WordPress的文章类型,定制出更符合自己需求的网站。

感谢大家的收看,咱们下期再见! 别忘了点个赞,关注一下,下次咱们继续聊点儿更有意思的!

发表回复

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