Plugin Development: WordPress processes twice on post update. How to skip process on the first?

When you create a plugin and set up a process function, WordPress will run the plugin process function twice upon clicking the update button. I believe the first process is for the versioning (revision) post, and the second the actual post.

Now, I’ve got an INSERT function in my process, so it is now inserting the data twice.

Read More

What is the best way to ensure that processing doesn’t happen while creating the revision and and only on the actual post processing?

* Note: Targeting WordPress 3.0+ *

An Example (complete plugin):

<?php
/*
Plugin Name: Something Amazing
Plugin URI: http://www.somewhere.com/
Description: Displays something.
Author: Some guy
Version: 1.0
Author URI: http://www.somewhere.com/
*/
function call_something() {
 return new something();
}
if (is_admin()) add_action('load-post.php','call_something');

class something {

 public function __construct() {
  add_action('add_meta_boxes',array(&$this,'something_add_boxes'));
  add_action('save_post',array(&$this,'something_process'),1,1);
 }

 public function something_add_boxes() {
  add_meta_box('something','Some Thing',array(&$this,'something_form'),'post','side','default');
 }

 public function something_form($post,$args) {
  echo '<p><input type="text" name="somethingnew" /></p>';
 }

 public function something_process($id) {
  echo 'hey there! I'm going to cause a redirect warning, but you will see this line twice per submit!';
  //do_something_amazing();
 }
}
?>

Perhaps there is a better hook to use to process that doesn’t make it write twice?

Related posts

Leave a Reply

5 comments

  1. @Steve Functions hook to save_post are always called twice, WordPress call functions hooked to save_post twice because:

    • first time it saves the post revision.
    • second time it saves the actual post.

    NOTE: If you have disabled post revisions using define('WP_POST_REVISIONS', false); then the save_post hook will be fired only once when the actual post will be saved.

    I just tested the following code and it works as expected and the echo only runs once in the something_process function.

    NOTE: the way I have hooked to save_post

    <?php
    /*
    Plugin Name: Something Amazing
    Plugin URI: http://www.somewhere.com/
    Description: Displays something.
    Author: Some guy
    Version: 1.0
    Author URI: http://www.somewhere.com/
    */
    function call_something() {
     return new something();
    }
    if (is_admin()) add_action('load-post.php','call_something');
    
    class something {
    
     public function __construct() {
      add_action('add_meta_boxes',array(&$this,'something_add_boxes'));
      add_action('save_post',array(&$this,'something_process'),1,2);
     }
    
     public function something_add_boxes() {
      add_meta_box('something','Some Thing',array(&$this,'something_form'),'post','side','default');
     }
    
     public function something_form($post,$args) {
      echo '<p><input type="text" name="somethingnew" /></p>';
     }
    
     public function something_process($id, $post_object) {
        // don't run the echo if this is an auto save
        if ( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE )
            return;
    
        // don't run the echo if the function is called for saving revision.
        if ( $post_object->post_type == 'revision' )
            return;
    
      echo 'hey there! I'm going to cause a redirect warning, but you will see this line twice per submit!';
      //do_something_amazing();
     }
    }
    ?>
    
  2. You need make sure that your code is only being run when the post is saved and not when a revision or automatic save is being carried out.

    Check if the current post id is indeed an actual post an not a revision or autosaved post by doing this (the actual post will always return a FALSE value):

    if ( is_int( wp_is_post_revision( $id ) ) )
        return;
    
    if( is_int( wp_is_post_autosave( $id ) ) )
        return;
    
  3. you can avoid autosave using a simple check in your function, something like

    if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
            return '';
        }
    

    update

    Like i’ve posted, just add the above snippet to the very top of your something_process function before any thing else.

  4. In my plugins I do a verification of the status of post is a revision. You can do something like this in the “something process” function.

    public function something_process($id, $post) {
        if ($post->post_type == 'revision') return;
        echo 'hey there! I'm going to cause a redirect warning, but you will see this line twice per submit!';
        //do_something_amazing();
    }
    

    Adding the if ($post->post_type == 'revision') return; line, you “cancel” the first call of function on WP.

    You need to do a second change on the action hook too. Just change the line:

    add_action('save_post',array(&$this,'something_process'),1,1);
    

    to this:

    add_action('save_post',array(&$this,'something_process'),1,2);
    

    Like this you pass the second parameter on action “save_post” (the $post variable)

  5. I hear you’re looking for the “best” way… well that depends. Code needs to handle custom issues- just like you wouldn’t select a cadillac to go offroad, and you wouldn’t (probably) take a jeep to a night at the opera.

    I have a plugin that handles both processes run on save on purpose. I use a static var and conditionally select which code I want to run. I also test for post parent id using the wp_is_post_revision() function above, and and have greater flexibility in tailoring conditions to meet my needs exactly based on those results.

    function complex_meta_update_on_save($id){
    $ppid = wp_is_post_revision($id);
    (can also employ) if($post_parent_id === false) $ppid = $id;
    ...set environmentals...
    
    static $tc;
    if($tc < 1){
    execute conditions essential  for revisions, or preprocess for ppid 
    }
    //note no else-
    i can do stuff here that is ok to duplicate, processing, etc. 
    
    if($tc > 0) {
    execute conditions and statements essential for post parents
    update custom tables with custom queries, register newly processed taxonomies, et. al. 
    keep in mind, on Save hook, post data has been saved in db. My plugin uses special  
    metadata that needs to know whether I've got a revision, or the parent. 
    update_post_meta($id, $blah);now is ok to use $id in
    }
    //oh the staticness of it all
    $tc++;
    }//end function
    

    I don’t know that any of that is brilliant, or even the best idea, it’s just what works where I need- and I don’t have to manage many more hooks than edit and save… pretty simple in one regard.