Timing is everything: scheduling in WordPress

Ticking away the moments that make up a dull day. You fritter and waste the hours in an off hand way

Pink Floyd – Time

WordPress 2.1 brought with it a number of new features, one of which is a pseudo-cron scheduling framework.  This has been is the pipes for some time now, Scott Merrill developed a plugin called wp-cron back in 2005. After the 2.0 release of WordPress, there was a discussion on the wp-hackers mailing list about getting a cron-type scheduling feature into the core of WordPress and the functionality that was released with 2.1 started to take shape.

One of the more important points to come out of this discussion was that this was not meant to replace the actual cron feature built into the Linux operating system, it is not meant to be anywhere near that precise

As for the precision, perhaps the minutely thing might be a little much (mostly cause it’s hard to guarantee a hit every single minute of the day.) but I would still leave that in with a warning to authors that a * rules on the minute option is not likely to work — it’d still be handy, though, for every hour, on the hour.

What has been produced is a system that allows plugin developers to schedule events to occur at certain times.  It does this by checking the current time against the list of scheduled tasks every time a page is loaded on the site.  If the set time has passed the task (a callback function) is done. 

Getting the scheduling to do what it should is going to require a working knowledege of how WordPress hooks, actions & filters work.  If you don’t here is some recommended reading:

I highly recommend you appraise yourself of this information before proceeding.  It’s ok, I’ll wait…

All back now? Alright, let’s continue.

How to schedule a task

Before we start it is important to note two things:

  1. When scheduling a callback function, you actually schedule a custom action hook which will then in turn call your function, see the ‘Defining the event you’re scheduling’ section below for details.
  2. Unlike actions and filters, scheduled tasks are persistent. add_action & add_filter calls need to be run on every page load, becuase they are not stored anywhere.  Schedules set using the functions described below are stored in the database, in the wp_options table.  If you call the schedule functions on every load, you will end up with a new schedule defined every time a page is loaded.  For this reason I recommend setting up your schedules in an activation callback function, hooked in using register_activation_hook.

The scheduling allows for scheduling two types of events:

  1. A single future event (e.g. setting a post to be published at a certain time in the future)
  2. Reoccurring events, setting an event for a time and having it happen at that time every day, week, whatever. (e.g. Loading & parsing the RSS feed for the Verse of the day plugin)

Scheduling a single event

To schedule a single event you use the function

wp_schedule_single_event( $timestamp, $hook, $args )

$timestamp is the timestamp of when you want the event to occur
$hook is the custom action hook that will call the function you want (more on this in a minute)
$args is an optional array of arguments that will be passed to your callback function.

Scheduling a reoccurring event

To do this you use the function

wp_schedule_event( $timestamp, $recurrence, $hook, $args )

The same arguments as the single event schedule, with the addition of
$recurrence which is a string key for the type of recurrence to schedule.  There are two reccurence types built in – hourly & daily. But what if you want others, say, weekly? Well fortunately you can add others.  The cron system gets the information on what ‘hourly’ and ‘daily’ mean from the wp_get_schedules() function.  Doing a print_r() on the output of this function gives you this:

Array
(
    [hourly] => Array
        (
            [interval] => 3600
            [display] => Once Hourly
        )

    [daily] => Array
        (
            [interval] => 86400
            [display] => Once Daily
        )
)

As you can see all it is doing is using an interval value (in seconds) to set how regularly the event should occur.  Looking at the source code for the wp_get_schedules() function you can see that it merges the output of the cron_schedule filter with the array containing the two built in ones before returning it.  This means that if you implement a filter function that hooks into the cron_schedules hook, you can add to this list.

To add weekly & fortnightly as possible reccurences, use the following code:

function more_reccurences() {
return array(
'weekly' => array('interval' => 604800, 'display' => 'Once Weekly'),
'fortnightly' => array('interval' => 1209600, 'display' => 'Once Fortnightly'),
);
}
add_filter('cron_schedules', 'more_reccurences');

 Doing a print_r() on wp_get_schedules()  now produces:

Array
(
    [weekly] => Array
        (
            [interval] => 604800
            [display] => Once Weekly
        )

    [fortnightly] => Array
        (
            [interval] => 1209600
            [display] => Once Fortnightly
        )

    [hourly] => Array
        (
            [interval] => 3600
            [display] => Once Hourly
        )

    [daily] => Array
        (
            [interval] => 86400
            [display] => Once Daily
        )
)

Defining the event you’re scheduling

At this point, for the sake of example, lets assume we’re scheduling something that will occur daily & the function we want to call daily is called my_daily_function(). So we will have added this code to our plugin:

if (!wp_next_scheduled('my_daily_function_hook')) {
wp_schedule_event( time(), 'daily', 'my_daily_function_hook' );
}

As mentioned above, what we’ve done here is say that every day the ‘my_daily_function_hook’ will be called. Also it’s important to note again at this point that you only need to call this once, which is the reason for checking that wp_next_scheduled('my_daily_function_hook') is false. That function will return the next scheduled time for a hook, and false if it hasn’t been scheduled. By checking that this is false, we ensure that the event is only scheduled once.

We now need to define this action to call the my_daily_function(). To do this we need to add the custom action

add_action( 'my_daily_function_hook', 'my_daily_function' );

and the function itself

function my_daily_function() { print "I was just called. I'll be called at the same time tomorrow"; }

That’s it.  At the same time every day (the time when this plugin is first loaded) the next person who loads a page on the site will see that text printed on the page.  For a more complete example of the scheduling in use, have a look at the wp-votd plugin code.  In that you’ll see the schedule event call sitting inside _install() method so that it’s only called once.

As mentioned wp_schedule_event can also take an optional fourth parameter of an array of values to pass to the callback function.

Wrapping it all up

This scheduling functionality is a great new feature in WordPress for plugin developers, it’s a lot easier than what we had to do, that is keep a last updated time in the options table and then take the difference between that and the current time & compare it to a pre-defined interval.  Major kudos to the developers for including this in the core code, and also to Scott Merrill for developing the original cron plugin.

I hope this tutorial has been helpful to people.  Please post any corrections, questions and usage ideas in the comments, share the knowledge!

Technorati tags: , , ,

57 thoughts on “Timing is everything: scheduling in WordPress

  1. Pingback: WordPress Wednesday: Custom Fields Contest and Lots of WordPress.com News at The Blog Herald

  2. I’m not a programmer so I have no idea what this means, but I love anyone who quotes Pink Floyd.

    Does this mean we don’t need Skippy’s Cron Jobs plugins anymore?

  3. Pingback: Scheduling in WordPress 2.1 » D’ Technology Weblog — Technology, Blogging, Gadgets, Fashion, Life Style.

  4. I’m not a programmer so I have no idea what this means, but I love anyone who quotes Pink Floyd.Does this mean we don’t need Skippy’s Cron Jobs plugins anymore?

  5. Pingback: pilkster.com » Blog Archive » Wordpress Scheduling: WP-cron Functionality in WP 2.1

  6. Pingback: Holistic Networking - Chron Jobs in WordPress 2.1

  7. Pingback: Wondering About Search And Wordpress Cron on iface thoughts

  8. Pingback: del.izi.oes am 13.03.07 at kleinski //

  9. Pingback: Red Sweater Blog - Future Posting With MarsEdit

  10. Thank you for that explanation! I have tried to write an small test plugin for to test the cron functionality.

    This plugin should write every minute the time in to a text file. But it works only one time after i activate this plugin.
    I use XAMPP on my computer (Win XP SP2) at home and WP 2.1.3. Can you please help me a little? Thank you very much!!

    array('interval' => 180, 'display' => 'timly'),
    'timly2' => array('interval' => 60, 'display' => 'timly2'),
    );
    }

    //if (function_exists('more_reccurences') == false){
    add_filter('cron_schedules', 'more_reccurences');
    //}

    function tims_sf2_function() {
    $handle = fopen("timtestfile_en.txt", "a");
    fputs($handle, date('j. F Y - H:i:s',time())."\n");
    fclose($handle);
    }

    if (!wp_next_scheduled('tim_sf2_function_hook')) {
    wp_schedule_event( time(), 'timly2', 'tim_sf2_function_hook' );
    }
    add_action( 'tim_sf2_function_hook', 'tims_sf2_function' );
    ?>


  11. array('interval' => 180, 'display' => 'timly'),
    'timly2' => array('interval' => 60, 'display' => 'timly2'),
    );
    }
    add_filter('cron_schedules', 'more_reccurences');

    function tims_sf2_function() {
    $handle = fopen("timtestfile_en.txt", "a");
    fputs($handle, date('j. F Y - H:i:s',time())."\n");
    fclose($handle);
    }

    if (!wp_next_scheduled('tim_sf2_function_hook')) {
    wp_schedule_event( time(), 'timly2', 'tim_sf2_function_hook' );
    }
    add_action( 'tim_sf2_function_hook', 'tims_sf2_function' );
    ?>

  12. Thank you for that explanation! I have tried to write an small test plugin for to test the cron functionality. This plugin should write every minute the time in to a text file. But it works only one time after i activate this plugin. I use XAMPP on my computer (Win XP SP2) at home and WP 2.1.3. Can you please help me a little? Thank you very much!! array('interval' => 180, 'display' => 'timly'), 'timly2' => array('interval' => 60, 'display' => 'timly2'), );}//if (function_exists('more_reccurences') == false){add_filter('cron_schedules', 'more_reccurences');//}function tims_sf2_function() { $handle = fopen("timtestfile_en.txt", "a"); fputs($handle, date('j. F Y - H:i:s',time())."n"); fclose($handle);}if (!wp_next_scheduled('tim_sf2_function_hook')) { wp_schedule_event( time(), 'timly2', 'tim_sf2_function_hook' );}add_action( 'tim_sf2_function_hook', 'tims_sf2_function' );?>

  13. array('interval' => 180, 'display' => 'timly'), 'timly2' => array('interval' => 60, 'display' => 'timly2'), );}add_filter('cron_schedules', 'more_reccurences');function tims_sf2_function() { $handle = fopen("timtestfile_en.txt", "a"); fputs($handle, date('j. F Y - H:i:s',time())."n"); fclose($handle);}if (!wp_next_scheduled('tim_sf2_function_hook')) { wp_schedule_event( time(), 'timly2', 'tim_sf2_function_hook' );}add_action( 'tim_sf2_function_hook', 'tims_sf2_function' );?>

  14. Now, I have tried my little plugin on a “real” server of my webhosting provider and it was succesfully. So it seems that my problems is caused by something with test system at home.

  15. Now, I have tried my little plugin on a “real” server of my webhosting provider and it was succesfully. So it seems that my problems is caused by something with test system at home.

  16. Pingback: links for 2007-05-01 | Mansoor Nathani's Blog

  17. Pingback: 我爱水煮鱼 » 在 WordPress 安排任务

  18. Pingback: BloggingPro China » 如何在 WordPress 安排任务

  19. Glenn

    This method works wonderfully. I had a question though. I would like to run my event at midnight everyday and not at the time I actually enable the script. In the first parameter of the wp_schedul_event I have tried


    date('U'), mktime(0,0,0,date('m'),date('d'),date('Y')))

    This did not do anything different that time() would have. Do you have any thoughts on how to make this run at a specified time?

    Again I have to say this is a very nice tutorial.

  20. GlennThis method works wonderfully. I had a question though. I would like to run my event at midnight everyday and not at the time I actually enable the script. In the first parameter of the wp_schedul_event I have tried date('U'), mktime(0,0,0,date('m'),date('d'),date('Y')))This did not do anything different that time() would have. Do you have any thoughts on how to make this run at a specified time?Again I have to say this is a very nice tutorial.

  21. Pingback: Programar tareas en Wordpress 2.1 o superiores | aNieto2K

  22. Pingback: Wordpress Documentation | Bochgoch Blog

  23. Pingback: www.ngtech.gr » The new WordPress scheduler

  24. If you want to write a plugin, which should work with other plugins which add intervals too at one time in a weblog then it could be better if your function more_reccurences looks like this:

    function more_reccurences($schedules) {
    $schedules['every1800sec'] = array(‘interval’ => 1800, ‘display’ => __(‘every 30 min’));
    return $schedules;
    }

    This will add and not replace other intervals.

  25. If you want to write a plugin, which should work with other plugins which add intervals too at one time in a weblog then it could be better if your function more_reccurences looks like this:function more_reccurences($schedules) { $schedules['every1800sec'] = array(‘interval’ => 1800, ‘display’ => __(‘every 30 min’)); return $schedules;}This will add and not replace other intervals.

  26. Pingback: WordPress Designpraxis » Blog Archive » Demo for WordPress Scheduling

  27. Pingback: Martin Cleaver, masterfully. » Blog Archive » Pseudo Cron Dashboard Display for Wordpress

  28. Pingback: links for 2007-12-27 | a minor technicality

  29. I’ve written a script that inserts a load of posts from an articles database into a wordpress database. The posts are given a timestamp that is staggered at a rate of approx 4 posts a day.

    The posts go in, but don’t publish when the timestamp arrives, they just sit there in limbo.
    I know why they don’t publish – THE CRON.

    I’m going up the wall trying to figure out what I have to code to tell the cron to deal with the post.

    Heres the script, I need to ‘add each post to the cron’.

    I’ve been struggling on this for weeks, does anyone know how to add each post to the cron queue?

    http://pickledegg.orchardhostings6.co.uk/articletransfer.txt

    Thanks

  30. I’ve written a script that inserts a load of posts from an articles database into a wordpress database. The posts are given a timestamp that is staggered at a rate of approx 4 posts a day.The posts go in, but don’t publish when the timestamp arrives, they just sit there in limbo.I know why they don’t publish – THE CRON.I’m going up the wall trying to figure out what I have to code to tell the cron to deal with the post.Heres the script, I need to ‘add each post to the cron’.I’ve been struggling on this for weeks, does anyone know how to add each post to the cron queue?http://pickledegg.orchardhostings6.co.uk/articl…Thanks

  31. This was very useful for me.

    I do not know why, but when I program post in the future, categories are not getting the correct count of posts. I end up with categories which report 1 post, when they have 40. This way, I designed a plugin that recalculate the number of posts of all the categories every day.

    So many thanks to you, and others who helped create this plugin. I hope I’ll release it soon and, of course, I’ll let you know.

  32. This was very useful for me. I do not know why, but when I program post in the future, categories are not getting the correct count of posts. I end up with categories which report 1 post, when they have 40. This way, I designed a plugin that recalculate the number of posts of all the categories every day.So many thanks to you, and others who helped create this plugin. I hope I’ll release it soon and, of course, I’ll let you know.

  33. I’m having a problem (also described here: http://wordpress.org/support/topic/167846) where wp_schedule_event doesn’t seem to work.

    I’m trying to schedule a function to run hourly, and it’s not working, so I decided to call wp_next_scheduled to see what it had for the next scheduled timestamp, but it returns the start timestamp instead (the first parameter of wp_schedule_event).

    So that means the next scheduled event is always some time in the past. But even when I set the start timestamp to some time in the future, it doesn’t seem to work.

    Can anybody help?

  34. I’m having a problem (also described here: http://wordpress.org/support/topic/167846) where wp_schedule_event doesn’t seem to work.I’m trying to schedule a function to run hourly, and it’s not working, so I decided to call wp_next_scheduled to see what it had for the next scheduled timestamp, but it returns the start timestamp instead (the first parameter of wp_schedule_event).So that means the next scheduled event is always some time in the past. But even when I set the start timestamp to some time in the future, it doesn’t seem to work.Can anybody help?

  35. Pingback: Texo Blog… » Blog Archive » Friday Photos Version 1.1

  36. Pingback: ?WP-Cron???WordPress ?? | ????

  37. Don’t we need to also _remove_ our new item from the schedule if the plugin is deactivated?

    How would we do that?

    Is there an inverse to wp_schedule_event() ?

  38. Don’t we need to also _remove_ our new item from the schedule if the plugin is deactivated?How would we do that?Is there an inverse to wp_schedule_event() ?

  39. To answer my own question: wp_unschedule_event() or wp_clear_scheduled_hook()

    Another point: to see what is scheduled, you can do a print_r on _get_cron_array()

    Nice article. I wish I could get it to work….:-\

  40. To answer my own question: wp_unschedule_event() or wp_clear_scheduled_hook()Another point: to see what is scheduled, you can do a print_r on _get_cron_array()Nice article. I wish I could get it to work….:-

  41. My WP-pseudo-cron jobs aren't firing, even though I have what I believe to be sufficient traffic (~1400 hits/day). What can I do to make sure that page loads are indeed forcing the wp-pseudo-cron system to do its thing?

  42. Pingback: HN.Net WordPress Plugins » Blog Archive » Scheduling with WordPress Cron Functions

  43. This is a very good point and is so important that it should be added to the main instructions as plugin developers should always keep other plugins in mind while developing.

  44. Pingback: Ensuring your Wordpress cron schedule does not conflict with other cron schedules : WP Content

  45. Pingback: Shashin 2.3 Beta is Here | Nothing But Words

  46. As mentioned above, what we’ve done here is say that every day the ‘my_daily_function_hook’ will be called. Also it’s important to note again at this point that you only need to call this once, which is the reason for checking that wp_next_scheduled('my_daily_function_hook') is false. That function will return the next scheduled time for a hook, and false if it hasn’t been scheduled. By checking that this is false, we ensure that the event is only scheduled once.

  47. Dennis Wurster, I am having the same problem. I wonder if it worked well if you schedule REAL cronjob to run wp-pseudo-cron (/blog/wp-cron.php as I assume) each minute/hour? So that cron performance doesn't depend on blog traffic.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>