分析 `WP_Post_Revisions` 类的源码,它是如何管理文章修订版本的?

大家好,欢迎来到今天的“WordPress源码解剖大会”。今天咱们要聊的是一个大家伙,但又有点默默无闻的角色:WP_Post_Revisions 类。 它就像一个负责任的图书管理员,默默记录着你文章的每一次修改,确保你永远不会丢失任何灵感火花。

今天我们不搞虚的,直接深入到WP_Post_Revisions 的源码腹地,看看它是如何管理WordPress文章修订版本的。准备好了吗?发车!

开场白:修订版,你的时光机

在WordPress里,修订版就像文章的时光机,让你随时可以回到过去,找回曾经的灵感。它不仅能帮你找回误删的内容,还能让你看到文章的演变过程。但这一切的幕后功臣就是WP_Post_Revisions 这个类。

WP_Post_Revisions 类概览

虽然 WordPress 核心并没有一个名为 WP_Post_Revisions 的类,但是与修订版相关的操作都是通过 wp-includes/revision.php 文件中的一系列函数来实现的。为了方便讲解,我们就假设存在一个 WP_Post_Revisions 类,它包含了这些函数的逻辑。

/**
 * 虚拟的 WP_Post_Revisions 类
 * 用于演示修订版管理逻辑
 */
class WP_Post_Revisions {

    /**
     * 创建修订版
     *
     * @param int   $post_id Post ID.
     * @param bool  $autosave Optional. Whether this is an autosave. Default is false.
     * @return int|WP_Error The post ID of the revision, or a WP_Error on failure.
     */
    public static function create_revision( $post_id, $autosave = false ) {
        // ... (实际创建修订版的代码) ...
    }

    /**
     * 获取文章的修订版
     *
     * @param int    $post_id Post ID.
     * @param array|string $args Optional. Array or string of arguments. See WP_Query::parse_query() for information on accepted arguments.
     * @return WP_Query|false WP_Query instance, or false if no revisions exist.
     */
    public static function get_revisions( $post_id, $args = array() ) {
        // ... (实际获取修订版的代码) ...
    }

    /**
     * 恢复修订版
     *
     * @param int $revision_id Revision ID.
     * @return int|WP_Error The post ID of the restored post, or a WP_Error on failure.
     */
    public static function restore_revision( $revision_id ) {
        // ... (实际恢复修订版的代码) ...
    }

    /**
     * 删除修订版
     *
     * @param int $post_id Post ID.
     * @return void
     */
    public static function delete_revisions( $post_id ) {
        // ... (实际删除修订版的代码) ...
    }
}

1. 创建修订版:create_revision()

这是修订版管理的核心函数,它负责在文章被修改时创建新的修订版本。

  • 触发时机:

    • 文章保存/发布时(wp_insert_post() 钩子)
    • 自动保存时
  • 主要步骤:

    1. 检查是否需要创建修订版:

      • 判断是否启用了修订版功能 ( WP_POST_REVISIONS 常量,默认为 true 或一个整数,表示保留的最大修订版本数)。
      • 检查文章类型是否支持修订版 ( supports( $post_type, 'revisions' ) )。
      • 判断当前用户是否有权限修改文章。
      • 排除自动保存 ( $autosave 参数 )。
      • 比较当前文章内容与上一个修订版的内容,如果差异不大,则不创建新的修订版。 这可以避免频繁的保存操作产生大量冗余的修订版本。
    2. 创建修订版文章:

      • 使用 wp_insert_post() 函数创建一个新的文章,其 post_type 设置为 revision
      • 将原始文章的 post_title, post_content, post_excerpt, post_author 等关键字段复制到修订版文章中。
      • 设置修订版文章的 post_parent 为原始文章的 ID,建立父子关系。
      • 使用 _wp_put_post_revision_version() 函数将修订版的版本号(通常是时间戳)存储为文章元数据。
    3. 清理旧的修订版:

      • 如果启用了修订版数量限制,则删除超出限制的旧修订版。
      • 使用 wp_delete_post_revision() 函数删除旧的修订版。

代码示例 (简化版):

/**
 * 虚拟的 WP_Post_Revisions::create_revision()
 */
public static function create_revision( $post_id, $autosave = false ) {
    // 1. 获取文章对象
    $post = get_post( $post_id );

    // 2. 检查是否需要创建修订版 (省略了详细的检查)
    if ( $autosave || !post_type_supports( $post->post_type, 'revisions' ) ) {
        return false;
    }

    // 3. 创建修订版文章
    $revision = array(
        'post_title'     => $post->post_title,
        'post_content'   => $post->post_content,
        'post_excerpt'   => $post->post_excerpt,
        'post_author'    => $post->post_author,
        'post_status'    => 'inherit', // 修订版的状态为 inherit
        'post_type'      => 'revision',
        'post_parent'    => $post_id,
        'menu_order'     => time(), // 使用时间戳作为修订版的排序依据
        'comment_status' => 'closed',
        'ping_status'    => 'closed',
    );

    // 插入修订版文章
    $revision_id = wp_insert_post( $revision );

    if ( is_wp_error( $revision_id ) ) {
        return $revision_id;
    }

    // 将原始文章的所有自定义字段复制到修订版
    $custom_fields = get_post_custom( $post_id );
    foreach ( $custom_fields as $key => $values ) {
        foreach ( $values as $value ) {
            update_post_meta( $revision_id, $key, maybe_serialize( $value ) );
        }
    }

    // 4. 清理旧的修订版 (省略了详细的清理逻辑)

    return $revision_id;
}

2. 获取修订版:get_revisions()

这个函数负责获取指定文章的所有修订版本。

  • 参数:

    • $post_id:要获取修订版本的文章 ID。
    • $args:可选参数,用于自定义查询条件,例如排序方式、数量限制等。
  • 主要步骤:

    1. 构建查询参数:

      • 设置 post_typerevision
      • 设置 post_parent 为要查询的文章 ID。
      • 根据 $args 参数设置其他查询条件,例如排序方式 ( orderby )、排序方向 ( order )、每页显示的数量 ( posts_per_page ) 等。
    2. 执行查询:

      • 使用 WP_Query 类执行查询,获取符合条件的修订版文章。
    3. 返回结果:

      • 返回一个 WP_Query 对象,其中包含了查询到的修订版文章。

代码示例 (简化版):

/**
 * 虚拟的 WP_Post_Revisions::get_revisions()
 */
public static function get_revisions( $post_id, $args = array() ) {
    $defaults = array(
        'post_parent'    => $post_id,
        'post_type'      => 'revision',
        'posts_per_page' => -1, // 获取所有修订版
        'orderby'        => 'menu_order', // 按修订版创建时间排序
        'order'          => 'DESC',
        'fields'         => 'ids', // 只获取 ID
    );

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

    $revisions_query = new WP_Query( $args );

    return $revisions_query;
}

3. 恢复修订版:restore_revision()

这个函数负责将指定的修订版本恢复为当前文章的内容。

  • 参数:

    • $revision_id:要恢复的修订版文章 ID。
  • 主要步骤:

    1. 获取修订版文章:

      • 使用 get_post() 函数获取修订版文章对象。
    2. 获取原始文章:

      • 使用 get_post() 函数获取原始文章对象,通过修订版文章的 post_parent 属性可以找到原始文章的 ID。
    3. 更新原始文章内容:

      • 将修订版文章的 post_title, post_content, post_excerpt 等关键字段复制到原始文章中。
      • 更新原始文章的 post_modifiedpost_modified_gmt 字段,表示文章已被修改。
    4. 更新原始文章的自定义字段:

      • 删除原始文章的所有自定义字段。
      • 将修订版文章的所有自定义字段复制到原始文章中。
    5. 更新文章:

      • 使用 wp_update_post() 函数更新原始文章。

代码示例 (简化版):

/**
 * 虚拟的 WP_Post_Revisions::restore_revision()
 */
public static function restore_revision( $revision_id ) {
    // 1. 获取修订版文章
    $revision = get_post( $revision_id );

    // 2. 获取原始文章
    $post = get_post( $revision->post_parent );

    // 3. 更新原始文章内容
    $post->post_title   = $revision->post_title;
    $post->post_content = $revision->post_content;
    $post->post_excerpt = $revision->post_excerpt;

    // 4. 更新文章
    $result = wp_update_post( $post );

    // 5. 更新自定义字段 (省略了详细的自定义字段更新逻辑)

    return $result;
}

4. 删除修订版:delete_revisions()

这个函数负责删除指定文章的所有修订版本。

  • 参数:

    • $post_id:要删除修订版本的文章 ID。
  • 主要步骤:

    1. 获取所有修订版:

      • 使用 get_revisions() 函数获取指定文章的所有修订版本。
    2. 删除修订版:

      • 遍历所有修订版文章,使用 wp_delete_post() 函数删除每个修订版文章。

代码示例 (简化版):

/**
 * 虚拟的 WP_Post_Revisions::delete_revisions()
 */
public static function delete_revisions( $post_id ) {
    $revisions_query = self::get_revisions( $post_id );

    if ( $revisions_query->have_posts() ) {
        while ( $revisions_query->have_posts() ) {
            $revisions_query->the_post();
            $revision_id = get_the_ID();
            wp_delete_post( $revision_id, true ); // true 表示强制删除,不放入回收站
        }
        wp_reset_postdata();
    }
}

修订版的存储方式

修订版实际上也是一种 post 类型,只不过它的 post_typerevision,并且它的 post_statusinherit

字段 描述
post_type revision,表示这是一个修订版。
post_status inherit,表示继承父文章的状态。
post_parent 原始文章的 ID,表示这个修订版属于哪个文章。
menu_order 修订版的版本号,通常是时间戳,用于排序。

修订版与自动保存

自动保存 (autosave) 也是一种特殊的修订版。它们之间的区别在于:

  • 自动保存是临时的,通常在用户离开编辑页面时被删除。
  • 修订版是永久的,除非手动删除。

创建自动保存时,create_revision() 函数的 $autosave 参数会被设置为 true

如何控制修订版

  1. WP_POST_REVISIONS 常量: 可以在 wp-config.php 文件中定义 WP_POST_REVISIONS 常量来控制修订版的行为。

    • true (默认): 保存所有修订版。
    • false: 禁用修订版功能。
    • 整数: 限制每个文章保留的最大修订版本数。
  2. revisions 支持: 可以在注册文章类型时,使用 supports() 函数来声明文章类型是否支持修订版。

    add_action( 'init', 'my_custom_post_type' );
    function my_custom_post_type() {
        register_post_type( 'my_post_type',
            array(
                'labels' => array(
                    'name' => __( 'My Post Types' ),
                    'singular_name' => __( 'My Post Type' )
                ),
                'public' => true,
                'supports' => array( 'title', 'editor', 'revisions' ) // 声明支持修订版
            )
        );
    }

总结

WP_Post_Revisions 类(或者说相关的函数)是 WordPress 修订版管理的核心。它通过创建、获取、恢复和删除修订版文章,实现了文章历史版本的管理。 理解了这些核心函数的工作原理,你就能更好地掌握 WordPress 的文章管理机制,甚至可以根据自己的需求定制修订版功能。

希望今天的“源码解剖大会”能让你对 WordPress 的修订版管理有更深入的了解。记住,源码的世界充满了乐趣,勇敢地去探索吧! 下课!

发表回复

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