How to avoid infinite loop in save_post callback

I have been using this site alot for solving my problems, but this time I did not succeed in finding and answer for my problem.

I get and infinite loop when using wp_update_post inside a function that is called on save_post. I know that this is a common issue, but I can’t figure out how to avoid it.

Read More

I want to save the order of my posts (which is of the post-type ‘section’). So I have made a custom meta box which contain some sortable html-elements. In each element there is an hidden input tag with name=’sectionorder[]’. So when I click the standard WordPress ‘Update’ buttom, an array containing all the ID’s of the posts (in order) are send via POST. So here is the code where I retrieve the array, and wants to save the order:

    // Update section sort order
$sectionorder = $_POST['sectionorder'];
if (isset($sectionorder)) { // Avoid error if there is no sections added yet
    foreach( $sectionorder as $no => $sectionID ) {
        $post_update = array();
        $post_update['ID'] = $sectionID;
        $post_update['menu_order'] = $no;
        wp_update_post( $post_update );
    }
}

But the problem is that it starts an infinite loop. How do I avoid that? Maybe I can do it in a completely different way?

Appriciate your help!

Related posts

Leave a Reply

3 comments

  1. You can remove the callback from the save_post hook, update the post and then re-add the call back to the hook. The Codex gives an example.

    add_action('save_post', 'wpse51363_save_post');
    
    function wpse51363_save_post($post_id) {
    
        //Check it's not an auto save routine
         if ( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE ) 
              return;
    
        //Perform permission checks! For example:
        if ( !current_user_can('edit_post', $post_id) ) 
              return;
    
        //Check your nonce!
    
        //If calling wp_update_post, unhook this function so it doesn't loop infinitely
        remove_action('save_post', 'wpse51363_save_post');
    
        // call wp_update_post update, which calls save_post again. E.g:
        wp_update_post(array('ID' => $post_id, 'post_status' => 'private'));
    
        // re-hook this function
        add_action('save_post', 'wpse51363_save_post');
    }
    
  2. I haven’t quite got the reputation to comment so I’m adding an answer even though Stephen’s is excellent and correct. It just doesn’t handle instances when you want to set the priority of the action.

    If you set the priority when adding the action but don’t specify priority when you remove it you will still get an infinite loop.

    add_action('save_post', 'wpse51363_save_post', 25 );
    

    // The WRONG way to handle this – leads to infinite loop

    remove_action('save_post', 'wpse51363_save_post');
    wp_update_post(array('ID' => $post_id, 'post_status' => 'private'));
    add_action('save_post', 'wpse51363_save_post');
    

    // The RIGHT way to handle this – executes only once

    remove_action('save_post', 'wpse51363_save_post', 25 );
    wp_update_post(array('ID' => $post_id, 'post_status' => 'private'));
    add_action('save_post', 'wpse51363_save_post', 25 );
    
  3. This is a rather quick and dirty trick but one way is to create a global variable like $my_plugin_name_saving and just set it to true before calling wp_update_post and return if the variable is already set.

    
    <?php
    /* Plugin Name: My plugin name */
    
    $my_plugin_name_saving = false;
    
    function cc_publish_wpse_263985( $postid ) {
        global my_plugin_name_saving;
    
        if ( ! empty( $my_plugin_name_saving ) ) {
            return;
        }
    
        $my_plugin_name_saving = true;
        wp_update_post(array('ID' => $postid, 'post_status' => 'private'));
    
    }
    
    add_action('save_post', 'cc_publish_wpse_263985');