wp_insert_post_datafilter-hookWP 2.7.0

Filters post data immediately before it is inserted into or updated in the database by wp_insert_post(). All data is slashed!

At this stage, escape slashes (\) have not yet been removed from the data; see wp_unslash().

This filter is useful when post data must be changed before it is inserted or updated—for example, to replace strings in the content, validate a date, or change fields such as post_name and guid.

Usage

add_filter( 'wp_insert_post_data', 'wp_kama_insert_post_data_filter', 10, 4 );

/**
 * Function for `wp_insert_post_data` filter-hook.
 * 
 * @param array $data                An array of slashed, sanitized, and processed post data.
 * @param array $postarr             An array of sanitized (and slashed) but otherwise unmodified post data.
 * @param array $unsanitized_postarr An array of slashed yet *unsanitized* and unprocessed post data as originally passed to wp_insert_post().
 * @param bool  $update              Whether this is an existing post being updated.
 *
 * @return array
 */
function wp_kama_insert_post_data_filter( $data, $postarr, $unsanitized_postarr, $update ){

	// filter...
	return $data;
}
$data(array)

Post data. The data contains escape slashes (\)!

Array (
	[post_author] => 1
	[post_date] => 2019-05-27 17:11:32
	[post_date_gmt] => 2019-05-27 13:11:32
	[post_content] =>
	[post_content_filtered] =>
	[post_title] => w3hosK-d_yE
	[post_excerpt] =>
	[post_status] => inherit
	[post_type] => attachment
	[comment_status] => open
	[ping_status] => closed
	[post_password] =>
	[post_name] => w3hosk-d_ye
	[to_ping] =>
	[pinged] =>
	[post_modified] => 2019-05-27 17:11:32
	[post_modified_gmt] => 2019-05-27 13:11:32
	[post_parent] => 0
	[menu_order] => 0
	[post_mime_type] => image/jpeg
	[guid] => http://wp-test.ru/wp-content/uploads/2019/05/w3hosK-d_yE-2.jpg
)
$postarr(array)

Original post data passed to wp_insert_post().

Array (
	[post_author] => 1
	[post_content] =>
	[post_content_filtered] =>
	[post_title] => w3hosK-d_yE
	[post_excerpt] =>
	[post_status] => draft
	[post_type] => attachment
	[comment_status] =>
	[ping_status] =>
	[post_password] =>
	[to_ping] =>
	[pinged] =>
	[post_parent] => 0
	[menu_order] => 0
	[guid] => http://wp-test.ru/wp-content/uploads/2019/05/w3hosK-d_yE-2.jpg
	[import_id] => 0
	[context] =>
	[file] => F:\server\sites\wp-test.ru\wp-content\uploads\2019\05\w3hosK-d_yE-2.jpg
	[post_mime_type] => image/jpeg
	[ID] => 0
	[filter] => db
)
$unsanitized_postarr(array) (WP 5.4.1)
Unsanitized post data. The data array contains slashes.
$update(true/false) (WP 6.0)
true when an existing post is updated; false when a new post is added.

Examples

#1 Remove \r (carriage return) from post content

## Removes \r (carriage return) from post content
add_action( 'wp_insert_post_data', 'remove_r_carriage_return', 0 );
function remove_r_carriage_return( $data ){
	$data['post_content'] = str_replace( "\r", '', $data['post_content'] );
	return $data;
}

#2 Change post data before updating

Before filtering the data, make sure that the intended post type is being changed in the intended context.

This example shows how to always store the post permalink in the guid field:

add_action( 'wp_insert_post_data', 'who_book_save', 20, 2 );
function who_book_save( $data, $postarr ){

	if(
		// Make sure we are editing the required post type
		( empty( $_POST['post_type'] ) || 'book' !== $_POST['post_type'] )
		||
		// Skip autosaves
		( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE )
		||
		// Make sure we are on the required admin page
		( 'book' !== get_current_screen()->id )
		||
		// Make sure the user can edit the post
		! current_user_can( 'edit_post', $postarr['ID'] )
	)
		return $data;

	// Everything is OK
	$data['guid'] = get_permalink( $postarr['ID'] );

	return $data;
}

#3 What the variables passed to the function look like

The following fields are passed in $data by default:

Array
(
	[post_author] => 1
	[post_date] => 2015-07-15 02:44:40
	[post_date_gmt] => 2015-07-14 23:44:40
	[post_content] = content
	[post_content_filtered] =>
	[post_title] => supermy
	[post_excerpt] =>
	[post_status] => publish
	[post_type] => book
	[comment_status] => closed
	[ping_status] => closed
	[post_password] =>
	[post_name] => supermy__
	[to_ping] =>
	[pinged] =>
	[post_modified] => 2015-07-16 01:10:27
	[post_modified_gmt] => 2015-07-15 22:10:27
	[post_parent] => 0
	[menu_order] => 0
	[post_mime_type] =>
	[guid] => http://example.com/?post_type=book&p=19
)

Default fields for the $postarr parameter:

'post_status'           - 'draft'.
'post_type'             - 'post'.
'post_author'           - ID of the current user or the user who created the post.
'ping_status'           - the 'default_ping_status' setting value.
'post_parent'           - 0
'menu_order'            - 0
'to_ping'               -
'pinged'                - empty string
'post_password'         - empty string
'guid'                  - unique ID
'post_content_filtered' -
'post_excerpt'          - text.

This is what $postarr looks like:

Array
(
	[post_status] => publish
	[post_type] => book
	[post_author] => 1
	[ping_status] => closed
	[post_parent] => 0
	[menu_order] => 0
	[to_ping] =>
	[pinged] =>
	[post_password] =>
	[guid] => http://example.com/?post_type=book&p=19
	[post_content_filtered] =>
	[post_excerpt] =>
	[import_id] => 0
	[post_content] = text
	[post_title] => supermy
	[context] =>
	[ID] => 19
	[post_date] => 2015-07-15 02:44:40
	[post_date_gmt] => 2015-07-14 23:44:40
	[comment_status] => closed
	[post_name] => supermy__
	[post_modified] => 2015-07-16 00:48:42
	[post_modified_gmt] => 2015-07-15 21:48:42
	[post_mime_type] =>
	[comment_count] => 0
	[ancestors] => Array
		(
		)

	[post_category] => Array
		(
		)

	[tags_input] => Array
		(
		)

	[_wpnonce] => e296535396
	[_wp_http_referer] => /wp-admin/post.php?post=19&action=edit
	[user_ID] => 1
	[action] => editpost
	[originalaction] => editpost
	[original_post_status] => publish
	[referredby] => http://example.com/wp-admin/edit.php?post_type=book
	[_wp_original_http_referer] => http://example.com/wp-admin/edit.php?post_type=book
	[post_ID] => 19
	[meta-box-order-nonce] => ea65cc23d4
	[closedpostboxesnonce] => ca65bc8597
	[samplepermalinknonce] => f19941a639
	[hidden_post_status] => publish
	[hidden_post_password] =>
	[hidden_post_visibility] => public
	[visibility] => public
	[mm] => 07
	[jj] => 15
	[aa] => 2015
	[hh] => 02
	[mn] => 44
	[ss] => 40
	[hidden_mm] => 07
	[cur_mm] => 07
	[hidden_jj] => 15
	[cur_jj] => 16
	[hidden_aa] => 2015
	[cur_aa] => 2015
	[hidden_hh] => 02
	[cur_hh] => 00
	[hidden_mn] => 44
	[cur_mn] => 19
	[original_publish] => Update
	[save] => Update
	[filter] => db
)

#4 Prevent saving a post with the publish status

Suppose you created a custom transaction post type and registered custom post statuses.

These posts must not have any status other than the custom statuses. This is easy to control programmatically, but when such a post is edited in the admin panel, WordPress automatically assigns it the publish status on save. The following code solves the problem:

add_filter( 'wp_insert_post_data', 'restoring_status_saved_admin_panel' );

/**
 * Restores the original transaction status when saving from the admin panel,
 * because WordPress attempts to assign the publish status.
 *
 * @param array $data
 *
 * @return array
 */
function restoring_status_saved_admin_panel( $data ) {
	if ( 'transaction' === $data['post_type'] && 'publish' === $data['post_status'] ) {
		$data['post_status'] = sanitize_key( filter_input( INPUT_POST, 'original_post_status' ) );
	}

	return $data;
}

Changelog

Since 2.7.0 Introduced.
Since 5.4.1 The $unsanitized_postarr parameter was added.
Since 6.0.0 The $update parameter was added.

Where the hook is called

wp_insert_post()
wp_insert_post_data
wp-includes/post.php 4978
$data = apply_filters( 'wp_insert_post_data', $data, $postarr, $unsanitized_postarr, $update );

Where the hook is used in WordPress

wp-admin/includes/ms-admin-filters.php 26
add_filter( 'wp_insert_post_data', 'avoid_blog_page_permalink_collision', 10, 2 );
wp-includes/class-wp-customize-manager.php 2962
add_filter( 'wp_insert_post_data', array( $this, 'preserve_insert_changeset_post_content' ), 5, 3 );
wp-includes/class-wp-customize-manager.php 2993
remove_filter( 'wp_insert_post_data', array( $this, 'preserve_insert_changeset_post_content' ), 5 );
wp-includes/default-filters.php 103
add_filter( 'wp_insert_post_data', '_wp_customize_changeset_filter_insert_post_data', 10, 2 );