Context: I'm using a plugin that allows me to add author boxes throughout my site (Simple Author Box), while providing an easy way for any user to update their social media links and profile pictures. I figured I could also leverage that plugin to dynamically show the team members in the site's about section.

However, I didn't want to have author archives for the team members that won't be publishing any posts in the future (designer, myself etc.), so I used the solution from this question as a starting point to disable specific users' author archives and from there I created a few additional functions in order to automate that functionality (author archives are now automatically being disabled/enabled according to the number of posts each user has published).

One of these functions is hooked to post_updated, which according to the docs: "Fires once an existing post has been updated." [emphasis added]

Here's the function's code (please forgive any lack of good practices, I'm new to PHP and not an experienced programmer):

* This function does the checks and the actual value update, if needed.
* It's called from inside the callback.
function maybe_update_author_archive_status($user_id, $published_posts, $author_archive_disabled) {
    if ($published_posts == 0 && $author_archive_disabled != 'on') {
        update_user_meta($user_id, '_author_archive_disabled', 'on');
    } elseif ($published_posts != 0 && $author_archive_disabled != 'off') {
        update_user_meta($user_id, '_author_archive_disabled', 'off');

* The callback itself.
function maybe_update_author_archive_status_on_post_update($post_id, $post_after, $post_before) {
    if($post_before->post_status != 'publish' && $post_after->post_status != 'publish') {
    $old_author = $post_before->post_author;
    $new_author = $post_after->post_author;
    $authors = array($old_author);
    /* If the post author has changed, I might need to update both author archive status */
    if($new_author != $old_author) {
        $authors[] = $new_author;

    foreach($authors as $author) {
        $user_id = intval($author, 10);
        $author_archive_disabled = get_user_meta($user_id, '_author_archive_disabled', true);
        $published_posts = count_user_posts($user_id);

        maybe_update_author_archive_status($user_id, $published_posts, $author_archive_disabled);
add_action('post_updated', 'maybe_update_author_archive_status_on_post_update', 10, 3);

However, to my surprise (and delight actually), it's also firing when I create and publish a new post. Can anyone explain me why? Under which circumstances this function wouldn't be fired? Even though this is the behavior I desired and everything is working as I wanted it to, this is not what I expected it would happen after reading the documentation.

1 Answer 1


also firing when I create and publish a new post

If you mean something like, you clicked the "Add New" button (which brings you to the post add new page at /wp-admin/post.php), and then you composed your post and finally hit the "Publish" button, then it's actually normal for the post_updated hook to get fired twice or more.

Why so or what happens is:

  • After you clicked the "Add New" button, WordPress will automagically create a post with the auto-draft status (post_status = auto-draft).

  • While composing your post, WordPress will auto-save the post so that you don't lose what you've composed thus far, and for each auto-save, the post_updated hook will be fired. And note that, during an auto-save, the post status is set to draft.

  • And after you hit the "Publish" button, WordPress updates the post (with the status changed from auto-draft or draft to publish), and then the post_updated hook will be fired once more.

So as you could see, the hook could get fired at least twice when you manually create and publish a post.

But if you do that programmatically, e.g. using wp_insert_post(), then the hook will not be fired. Note though, if you have plugins or custom code in your theme, the hook might be fired, e.g. a code that runs via save_post and modifies the post slug then call wp_update_post():

// In a default WordPress setup/installation:

// This will not fire the post_updated hook.
$id = wp_insert_post( array(
    'post_title'  => 'Just testing',
    'post_status' => 'publish',
) );

// And this will fire it, but just once.
wp_update_post( array(
    'ID'           => $id,
    'post_content' => 'Sample content',
) );

And I think you might want to execute your code only if the post is not being auto-saved, either as an auto-draft or revision.

So for that purpose, you can check if the constant DOING_AUTOSAVE is defined and that it's true-ish:

// In your function (maybe_update_author_archive_status_on_post_update()), at the top:
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
  • Thanks Sally CJ, that clarifies things a lot! I was thinking about this after posting the question and doing additional research, and came across the wp_insert_post() code. There, the $post_before parameter is set to null if it's not an update, what worried me because the way I wrote it, my function seems it would might try to access an object property of something that is not an object yet ($post_before). Wouldn't that return an error? Or that magically initially created auto-draft post is what actually gets stored in that variable? Also, thank you for the additional tips. Feb 3, 2021 at 15:34
  • If it's not an update, the function will not run the post_updated hook. :) But in my answer, I was telling you that the hook is indeed fired only if the post is being updated and not created, however, what makes the hook runs when you create and publish a post manually (via the admin UI) is the background actions as explained in my answer. So you can safely assume WordPress itself will not pass null as the $post_before value.
    – Sally CJ
    Feb 3, 2021 at 16:14
  • 1
    Awesome, thanks again! Feb 3, 2021 at 16:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.