wp_save_post_revision()WP 2.6.0

Creates a revision (copy) of the specified post. It also deletes excess revisions.

In addition to creating revisions, the function also monitors the number of stored revisions; if there are more than specified in the settings, it will delete the excess (from the beginning) after creating the next revision.

The number of revisions to be stored is specified in the constant WP_POST_REVISIONS. The value of the constant can be changed for a post type or a specific post through hooks:

A revision is a copy of the current post. The most recent revision contains the data of the current post. A revision has the post type revision, status inherit, and the post_parent field contains the ID of the post for which the revision was created.

The function is automatically triggered (a revision is created) each time a post is published/updated. It hooks into the post_updated:

add_action( 'post_updated', 'wp_save_post_revision', 10, 1 );

Which fields (data) will be included in the revision is specified in the function _wp_post_revision_fields(). Through the hook _wp_post_revision_fields, you can change the fields that should be saved in the revision.

By default, the following fields are saved in the revision:

post_title
post_content
post_excerpt

The following fields will never be included in the revision (even if specified through a hook) - they are forcibly excluded:

ID
post_name
post_parent
post_date
post_date_gmt
post_status
post_type
comment_count
post_author

The function does NOTHING:

  • If the last revision (its data) does not differ from the data of the specified post.
  • During auto-saving (DOING_AUTOSAVE).
  • When saving a post with the status auto-draft.
  • If the post type does not support revisions - see post_type_supports().
  • If revisions are disabled (either globally or for the post type). See wp_revisions_enabled().

Also see the wrapper for this function - the function wp_save_post_revision_on_insert(), which is triggered on the hook wp_after_insert_post.

add_action( 'wp_after_insert_post', 'wp_save_post_revision_on_insert', 9, 3 );

Returns

Int|WP_Error|null.

  • null (void) - if the revision should not be created. For example, when revisions are disabled.
  • WP_Error or 0 - in case of an error.
  • ID of the revision (ID of the added post) - in case of successful creation of the revision.

Usage

wp_save_post_revision( $post_id );
$post_id(int) (required)
The identifier of the post whose data needs to be saved in the revision.

Examples

0

#1 Create a revision of the post with id 13

wp_save_post_revision( 13 );

Changelog

Since 2.6.0 Introduced.

wp_save_post_revision() code WP 6.8.1

function wp_save_post_revision( $post_id ) {
	if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
		return;
	}

	// Prevent saving post revisions if revisions should be saved on wp_after_insert_post.
	if ( doing_action( 'post_updated' ) && has_action( 'wp_after_insert_post', 'wp_save_post_revision_on_insert' ) ) {
		return;
	}

	$post = get_post( $post_id );

	if ( ! $post ) {
		return;
	}

	if ( ! post_type_supports( $post->post_type, 'revisions' ) ) {
		return;
	}

	if ( 'auto-draft' === $post->post_status ) {
		return;
	}

	if ( ! wp_revisions_enabled( $post ) ) {
		return;
	}

	/*
	 * Compare the proposed update with the last stored revision verifying that
	 * they are different, unless a plugin tells us to always save regardless.
	 * If no previous revisions, save one.
	 */
	$revisions = wp_get_post_revisions( $post_id );
	if ( $revisions ) {
		// Grab the latest revision, but not an autosave.
		foreach ( $revisions as $revision ) {
			if ( str_contains( $revision->post_name, "{$revision->post_parent}-revision" ) ) {
				$latest_revision = $revision;
				break;
			}
		}

		/**
		 * Filters whether the post has changed since the latest revision.
		 *
		 * By default a revision is saved only if one of the revisioned fields has changed.
		 * This filter can override that so a revision is saved even if nothing has changed.
		 *
		 * @since 3.6.0
		 *
		 * @param bool    $check_for_changes Whether to check for changes before saving a new revision.
		 *                                   Default true.
		 * @param WP_Post $latest_revision   The latest revision post object.
		 * @param WP_Post $post              The post object.
		 */
		if ( isset( $latest_revision ) && apply_filters( 'wp_save_post_revision_check_for_changes', true, $latest_revision, $post ) ) {
			$post_has_changed = false;

			foreach ( array_keys( _wp_post_revision_fields( $post ) ) as $field ) {
				if ( normalize_whitespace( $post->$field ) !== normalize_whitespace( $latest_revision->$field ) ) {
					$post_has_changed = true;
					break;
				}
			}

			/**
			 * Filters whether a post has changed.
			 *
			 * By default a revision is saved only if one of the revisioned fields has changed.
			 * This filter allows for additional checks to determine if there were changes.
			 *
			 * @since 4.1.0
			 *
			 * @param bool    $post_has_changed Whether the post has changed.
			 * @param WP_Post $latest_revision  The latest revision post object.
			 * @param WP_Post $post             The post object.
			 */
			$post_has_changed = (bool) apply_filters( 'wp_save_post_revision_post_has_changed', $post_has_changed, $latest_revision, $post );

			// Don't save revision if post unchanged.
			if ( ! $post_has_changed ) {
				return;
			}
		}
	}

	$return = _wp_put_post_revision( $post );

	/*
	 * If a limit for the number of revisions to keep has been set,
	 * delete the oldest ones.
	 */
	$revisions_to_keep = wp_revisions_to_keep( $post );

	if ( $revisions_to_keep < 0 ) {
		return $return;
	}

	$revisions = wp_get_post_revisions( $post_id, array( 'order' => 'ASC' ) );

	/**
	 * Filters the revisions to be considered for deletion.
	 *
	 * @since 6.2.0
	 *
	 * @param WP_Post[] $revisions Array of revisions, or an empty array if none.
	 * @param int       $post_id   The ID of the post to save as a revision.
	 */
	$revisions = apply_filters(
		'wp_save_post_revision_revisions_before_deletion',
		$revisions,
		$post_id
	);

	$delete = count( $revisions ) - $revisions_to_keep;

	if ( $delete < 1 ) {
		return $return;
	}

	$revisions = array_slice( $revisions, 0, $delete );

	for ( $i = 0; isset( $revisions[ $i ] ); $i++ ) {
		if ( str_contains( $revisions[ $i ]->post_name, 'autosave' ) ) {
			continue;
		}

		wp_delete_post_revision( $revisions[ $i ]->ID );
	}

	return $return;
}