First of all you have to understand that when we update a post, wp_update_post
function is called. But due to a bit not optimal design of WP core, the actual saving is processed by wp_insert_post
function. See it in trac on line 3006.
Ok, next lets see what is inside of wp_insert_post
function. As you can see, on line 2950, save_post
action is called each time the function is called, whenever it has been called directly or by wp_update_post
function. It means that it doesn’t suit to determine if post has been inserted/published or updated.
To find better action, lets take a look at wp_transition_post_status
function which is called almost just before save_post
action, see it on line 2942. This function does three actions transition_post_status
, {$old_status}_to_{$new_status}
and {$new_status}_{$post->post_type}
, see it on line 3273. Here we have nice action transition_post_status
, which passes old and new post statuses. This is what we need. So if new status is publish
and old status is not publish
, then the post is published. And if new status is publish
and old status is new
, then the post has been inserted. And finally if new status equals to old status, then the post has been just updated.
Here is your snippet:
add_action( 'transition_post_status', 'wpse_transition_post_status', 10, 3 );
function wpse_transition_post_status( $new_status, $old_status, $post ) {
if ( $new_status == 'publish' && $old_status == 'new' ) {
// the post is inserted
} else if ( $new_status == 'publish' && $old_status != 'publish' ) {
// the post is published
} else {
// the post is updated
}
}
P.S.: read the code of the WordPress core, each time when you in doubts, it will help you a lot!