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: , , ,