分析 `wp_post_revisions` 表的源码,它是如何实现文章修订功能的?

各位观众老爷们,大家好!今天咱们来聊聊 WordPress 里面那个默默奉献、记录你每一次修改的“小本本”—— wp_post_revisions 表。 别看它名字叫“修订”,其实它干的活儿可不简单,相当于你的文章时光机,让你随时可以回到过去,找回灵感迸发的瞬间,或者手滑删掉的重要段落。

咱们今天就来扒一扒这个 wp_post_revisions 表的源码,看看它到底是怎么实现文章修订功能的。Let’s dive in!

一、wp_post_revisions 表:数据结构和存储逻辑

首先,我们得搞清楚 wp_post_revisions 表里都存了些什么。打开你的数据库,瞧一眼它的结构,你会发现它跟 wp_posts 表长得很像,毕竟它就是 wp_posts 表的一个“影子”。

以下是一个简化的 wp_posts 表和 wp_post_revisions 表的对比,方便大家理解:

字段名 wp_posts 表 (主表) wp_post_revisions 表 (修订表) 备注
ID 文章唯一ID 修订版本唯一ID 都是自增主键,但修订版本的ID跟文章ID没有直接关系。
post_author 作者ID 作者ID 修订版本通常与文章作者相同。
post_date 发布日期 修订日期 修订日期记录的是这个版本创建的时间,通常跟文章的最后修改时间接近。
post_date_gmt 发布日期 (GMT) 修订日期 (GMT) 同上,只是用了 GMT 时区。
post_content 文章内容 修订版本的内容 存储了对应修订版本的文章内容,这是最重要的部分。
post_title 文章标题 修订版本的标题 存储了对应修订版本的文章标题。
post_excerpt 文章摘要 修订版本的摘要 存储了对应修订版本的文章摘要。
post_status 文章状态 inherit 修订版本的状态始终是 inherit,表示继承自父文章。
comment_status 评论状态 与父文章相同
ping_status Pingback 状态 与父文章相同
post_password 文章密码 与父文章相同
post_name 文章别名 修订版本的别名 修订版本的别名通常是文章别名加上一个修订版本号,例如 my-post-revision-v1
to_ping 要 Ping 的 URL 与父文章相同
pinged 已 Ping 的 URL 与父文章相同
post_modified 最后修改日期 修订版本的修改日期
post_modified_gmt 最后修改日期 (GMT) 修订版本的修改日期 (GMT)
post_content_filtered 过滤后的内容 修订版本过滤后的内容
post_parent 父文章ID 父文章ID 指向主文章的ID,将修订版本与主文章关联起来。
guid GUID (全局唯一标识符) 修订版本的GUID
menu_order 菜单顺序 与父文章相同
post_type 文章类型 revision 修订版本的文章类型始终是 revision
post_mime_type MIME 类型 与父文章相同
comment_count 评论数 与父文章相同

从上面的表格可以看出,wp_post_revisions 表存储了文章的各个修订版本,每个版本都包含文章的完整内容、标题、摘要等信息。 关键的一点是 post_parent 字段,它指向了主文章的 ID,通过这个字段,WordPress 就能把所有的修订版本跟对应的文章关联起来。

二、WordPress 如何创建和管理修订版本?

好,了解了数据结构,接下来我们就看看 WordPress 是怎么创建和管理这些修订版本的。

1. 触发修订版本创建的时机

一般来说,以下几种情况会触发 WordPress 创建新的修订版本:

  • 自动保存 (autosave): WordPress 会定期自动保存你的文章,防止你辛辛苦苦码的字因为意外情况丢失。 自动保存的版本也是一个修订版本,但它的 post_statusauto-draft,而且通常不会显示在修订版本列表中。
  • 手动保存/发布: 当你点击“保存草稿”、“发布”或者“更新”按钮时,WordPress 也会创建一个新的修订版本。

2. 核心函数:wp_insert_post()

创建修订版本的核心函数是 wp_insert_post(), 这个函数负责插入或更新 wp_posts 表中的数据,包括文章和修订版本。 让我们看看 wp_insert_post() 里面跟修订版本相关的代码片段:

function wp_insert_post( $postarr = array(), $wp_error = false ) {
    // ... 一大堆代码 ...

    // 如果是更新文章,并且需要创建修订版本
    if ( ! empty( $post_ID ) ) {
        $post_before = get_post( $post_ID );

        // 检查是否需要创建修订版本 (例如,内容是否发生变化)
        if ( wp_revisions_enabled( $post_before ) && apply_filters( 'wp_save_post_revision_check_for_changes', did_action( 'edit_post' ), $post_before, $postarr ) ) {
            $revision_id = wp_save_post_revision( $post_ID, 'revision' ); // 创建修订版本
            if ( is_wp_error( $revision_id ) ) {
                return $revision_id;
            }
            $revisions[] = $revision_id;
        }
    }

    // ... 更多代码 ...

    return $post_ID;
}

这段代码的核心在于 wp_save_post_revision() 函数,它才是真正创建修订版本的“幕后黑手”。

3. 创建修订版本的关键:wp_save_post_revision()

wp_save_post_revision() 函数负责将当前的文章数据复制一份,然后插入到 wp_post_revisions 表中。 我们来深入了解一下这个函数的实现:

function wp_save_post_revision( $post_id, $revision = 'revision' ) {
    $post = get_post( $post_id );

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

    // 准备修订版本的数据
    $revision_data = 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', // 修订版本的类型总是 'revision'
        'post_name'      => $post->post_name, // 修订版本的别名
        'post_parent'    => $post->ID, // 指向父文章的ID
        'post_date'      => $post->post_date,
        'post_date_gmt'  => $post->post_date_gmt,
        'post_modified'  => current_time( 'mysql' ),
        'post_modified_gmt' => current_time( 'mysql', 1 ),
    );

    // 根据 $revision 参数,设置修订版本的后缀 (例如,'revision', 'autosave')
    if ( 'autosave' == $revision ) {
        $revision_data['post_name'] = $post->post_name . '-autosave';
    } else {
       $revision_data['post_name'] = $post->post_name . '-revision';
    }

    // 插入修订版本到数据库
    $revision_id = wp_insert_post( $revision_data );

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

    // 更新修订版本的 GUID
    $guid = get_permalink( $revision_id );
    wp_update_post( array( 'ID' => $revision_id, 'guid' => $guid ) );

    return $revision_id;
}

这个函数做了以下几件事:

  1. 获取文章数据: 通过 get_post() 函数获取当前文章的所有数据。
  2. 准备修订版本数据: 创建一个数组 $revision_data,包含文章的标题、内容、摘要、作者、状态、类型等信息。 注意,修订版本的 post_status 始终是 inheritpost_type 始终是 revisionpost_parent 指向父文章的 ID。
  3. 设置修订版本别名: 根据 $revision 参数,设置修订版本的别名,通常是在文章别名后面加上 -revision-autosave
  4. 插入修订版本到数据库: 调用 wp_insert_post() 函数,将 $revision_data 插入到 wp_post_revisions 表中,创建一个新的修订版本。
  5. 更新修订版本的 GUID: 更新修订版本的 GUID,确保每个修订版本都有一个唯一的 URL。

三、WordPress 如何显示和比较修订版本?

创建了修订版本之后,WordPress 还需要提供一个界面,让用户可以查看、比较和恢复这些版本。

1. 获取文章的修订版本:wp_get_post_revisions()

wp_get_post_revisions() 函数用于获取指定文章的所有修订版本。 它会查询 wp_post_revisions 表,并返回一个包含所有修订版本对象的数组。

function wp_get_post_revisions( $post_id = 0, $args = array() ) {
    $defaults = array(
        'posts_per_page' => -1, // 获取所有修订版本
        'fields'         => '', // 获取所有字段
        'orderby'        => 'post_modified', // 按照修改时间排序
        'order'          => 'DESC', // 倒序排列
        'post_status'    => 'inherit', // 只获取状态为 'inherit' 的修订版本
        'post_type'      => 'revision', // 只获取类型为 'revision' 的修订版本
    );

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

    $args['post_parent'] = $post_id; // 设置父文章ID,只获取指定文章的修订版本

    $revisions = get_children( $args ); // 调用 get_children() 函数获取修订版本

    return $revisions;
}

这个函数的核心是 get_children() 函数,它用于获取指定父文章的所有子文章,也就是修订版本。

2. 显示修订版本列表

在文章编辑页面,你会看到一个“修订”面板,里面列出了文章的所有修订版本。 这个面板通常会显示修订版本的修改日期和作者,方便你选择要查看的版本。

WordPress 使用 wp_list_post_revisions() 函数来生成修订版本列表的 HTML 代码。

3. 比较修订版本:wp_get_revision_ui_diff()

WordPress 提供了一个强大的修订版本比较功能,可以让你清楚地看到不同版本之间的差异。 这个功能的核心是 wp_get_revision_ui_diff() 函数,它使用 PHP 的 wp_text_diff() 函数来生成差异对比的 HTML 代码。

function wp_get_revision_ui_diff( $left, $right, $args = array() ) {
    $defaults = array(
        'show_split_view' => true, // 是否显示分屏对比
    );

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

    $left_content  = $left->post_content;
    $right_content = $right->post_content;

    $diff = wp_text_diff( $left_content, $right_content, $args ); // 使用 wp_text_diff() 函数生成差异对比

    return $diff;
}

wp_text_diff() 函数会将两个版本的文章内容进行比较,并生成一个 HTML 表格,用不同的颜色标记出新增、删除和修改的部分。 这样你就可以一目了然地看到两个版本之间的差异。

4. 恢复修订版本

如果你想恢复到某个旧的修订版本,只需点击“恢复”按钮,WordPress 就会将该版本的内容复制到主文章中。 这个过程实际上就是将修订版本的 post_contentpost_title 等字段的值更新到主文章的对应字段中。

四、修订版本的优化和管理

虽然修订版本功能非常有用,但过多的修订版本也会占用大量的数据库空间,影响网站的性能。 因此,我们需要对修订版本进行优化和管理。

1. 限制修订版本的数量

WordPress 允许你限制每个文章保存的修订版本数量。 你可以在 wp-config.php 文件中添加以下代码来设置修订版本的最大数量:

define( 'WP_POST_REVISIONS', 3 ); // 最多保存 3 个修订版本

如果你想禁用修订版本功能,可以将 WP_POST_REVISIONS 设置为 false

define( 'WP_POST_REVISIONS', false ); // 禁用修订版本功能

2. 定期清理旧的修订版本

即使你限制了修订版本的数量,时间长了,数据库中仍然会积累大量的旧版本。 你可以使用一些插件或者手动编写 SQL 语句来清理这些旧版本。

以下是一个清理旧修订版本的 SQL 语句:

DELETE FROM wp_posts
WHERE post_type = 'revision'
AND post_date < DATE_SUB(CURDATE(), INTERVAL 30 DAY); -- 删除 30 天前的修订版本

3. 使用插件进行管理

有很多 WordPress 插件可以帮助你更好地管理修订版本,例如:

  • WP-Sweep: 可以清理未使用的修订版本、自动草稿、垃圾评论等,优化数据库。
  • Optimize Database after Deleting Revisions: 专门用于优化数据库,删除旧的修订版本。
  • Revision Control: 可以更精细地控制每个文章类型的修订版本数量。

五、总结

好了,各位观众老爷们,今天我们深入剖析了 WordPress 的 wp_post_revisions 表,了解了它是如何实现文章修订功能的。 从数据结构到创建、显示、比较和管理,我们都进行了详细的讲解。

希望通过今天的讲座,大家对 WordPress 的修订版本功能有了更深入的理解。 记住,wp_post_revisions 表是你的文章时光机,善用它,可以让你更好地管理你的内容,找回灵感,避免损失。

下次再见!

发表回复

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