Scheduling Tasks in WordPress: a Plugin Developer’s Guide

Scheduling the execution of certain functions at a time in the futureis a key feature of the WordPress API. It was added to the developers’ API in WordPressversion 2.0. It’s also a topic that’s poorly understood by many developers,as it’s only briefly covered in WordPress’s documentation. Apart fromfacilitating maintenance tasks, scheduling code to be executed in the futureopens up a lot of interesting possibilities for plugin developers.Here are some real-world scenarios where the ability to scheduleexecution of code will be useful for developing plugins:
  • A website analytics plugin should be able to generate a weeklyreport on site usage statistics, and then send an email to the websiteadministrator.
  • An email newsletter plugin might need to deliver emails on serverswhere there are hourly sending limits. The plugin would have to queueoutgoing emails and send a given number each hour.
  • A chart image generator can gather data from a database, generatethe image, and save it to a cache instead of generating it on everyrequest—this will avoid unnecessary load on the database.
  • A backup plugin will need a function to be activated once everyweek, so that it can generate the database backup and send an email tothe website administrator.
  • A membership website plugin would require the ability to expiresubscriptions at the end of a month.
  • A Twitter sidebar widget plugin that makes a request to theTwitter service only once an hour to fetch and load your latesttweets.

In this article I’ll show you how to work with the WordPress API toschedule tasks to run at specific times or intervals. It’s assumed thatyou’re already familiar with the basics of building WordPress plugins;otherwise, have a read through the Writing a Plugin page onthe WordPress Codex for some starting pointers.

How WordPress Triggers Scheduled Tasks

Before we look at how to set up scheduled tasks in your WordPressplugin, you need to understand how WordPress handles scheduled tasksbehind the scenes.Every time someone visits a page of the site, WordPress checks tosee if any of the functions that have been scheduled need to be executed.This is based on the interval since the last execution or the time whenthe task was scheduled.WordPress uses the fsockopen PHP functionto make a request to the wp-cron.php file that’slocated in the WordPress installation directory. To make sure that thewebsite visitor is unaffected by this call, the timeout for the request isvery small (0.1 seconds). Having initiated the request, WordPress doesn’twait for a response, and continues serving the page to the user. The 0.1second timeout is enough to initiate execution of thewp-cron.php file.The first statement the wp-cron.php fileexecutes is:

ignore_user_abort(true);

This ensures that execution continues, even if the client whorequested the page closes the connection (this would otherwise terminateexecution). This script will check for any tasks that should be run, basedon the current time and the time they were last executed. For example,let’s say you have a task that’s scheduled to run hourly, and was last runat 4:13 p.m. When a user requests a page from your site at 5:11 p.m.,wp-cron.php will skip your task, as it was last runless than an hour prior. A few minutes later, at 5:15 p.m., when anotheruser views a page, wp-cron.php will run yourtask.

Types of Scheduled Tasks

In order to schedule a function to be run, either as a one-off taskor as a recurring task, you simply attach it to an action hook. Thisallows several functions to be assigned to the same scheduled hook. Incase you’re unfamiliar with WordPress action hooks, let’s do a quickreview before looking at how they’re applied to scheduling.If you’re already familiar with WordPress action hooks, feel free toskip this section and go straight to the section called “Scheduling Tasks”.

What Are WordPress Action Hooks?

An action hook is a named group of functions to be executed at agiven point. Any function attached to a given action hook will beexecuted when that action is performed. The WordPress core defines manyactions, which are in turn triggered at many places in the WordPresscore code.Let’s look at an example: WordPress’s core includes thewp_head function, which you’re probablyfamiliar with. Inside that function you’ll find this line ofcode:

do_action('wp_head');

The wp_head function is called within the<head> section of theheader.php file in a WordPress theme. When thatfunction is called, the above line of code will trigger the execution ofany functions that have been linked to the wp_headaction hook. It’s important to distinguish between thewp_head method and thewp_head action hook: they have the same name but aredistinct.If we want to add a style sheet to all pages without editing theheader.php file, we simply create a function of ourown that generates the desired <link/>tag, and then attach this function to the wp_headaction. The code below does this:

//This function generates the link tagfunction myplugin_generatestylesheet(){   //Form the URL to the css file stored in our plugin directory   $pluginURL = get_bloginfo('home').'/wp-content/plugins/myplugin/mycss.css';   //Write it to the browser   echo '<link href="'.$pluginURL.'" type="text/css" rel="stylesheet" />';}//Now we add this function to the wp_head action.add_action('wp_head','myplugin_generatestylesheet');

The add_action function is the key here:it attaches our myplugin_generatestylesheetfunction to the wp_head action hook. Now, when thewp_head method callsdo_action('wp-head'), our function will be called,and the link tag will be output.You can define your own actions and add as many functions to thataction as you’d like. This turns out to be the key to WordPress’sscheduling functionality, as we’ll see shortly.

Scheduling Tasks

In WordPress, we can schedule functions to be called in twodifferent ways:

  • One-off task—a task that is to be executed once at a specifiedtime
  • Recurring task—a task that’s executed at regularintervals

I’ll go over each of them in turn.

Scheduling a One-off Task

To schedule actions that are executed once at a specific time andnever again, we use thewp_schedule_single_event function.First, we define the function that we want to schedule:

function my_function(){  // Do something. Anything.}

Next, we add this function to our action hook. Wait, we need tocreate the action hook first, right? Wrong. It turns out we don’t. Justadding the function to an action with a new name creates the new action.So, in this step, we create both the my_actionaction, and add the my_function function tothat action:

add_action('my_action','my_function');

Finally, we schedule the action hook to run at the desiredtime:

wp_schedule_single_event($timestamp,'my_action');

In the above line of code, $timestamp is thetime we want the action to run, formatted as a Unix timestamp.

Scheduling Recurring Tasks

Functions that are to be called at regular intervals are scheduledusing the wp_schedule_event function. The stepsinvolved in defining a recurring task are otherwise very similar to thesteps for a one-off task. Again, we start by defining a functioncontaining the code we want to execute periodically:

function my_periodic_function(){  // Do something regularly.}

We add the function to the new action hook:

add_action('my_periodic_action','my_periodic_function');

Finally, we schedule the action to be executed with the desiredfrequency:

wp_schedule_event($timestamp, 'hourly', 'my_periodic_action');

This function takes a new parameter: the frequency('hourly' in the above example).$timestamp is the time when the action hook shouldfirst be run, after which it will be run at a frequency determined bythe second parameter. The final parameter, as withwp_schedule_single_event, is the action hook toschedule.WordPress defines three recurrence frequencies: hourly, daily, andtwicedaily. However, should you need a different frequency, never fear! Youcan define your own recurrence frequencies.

Creating Your Own Recurrence Frequency

What if you need a recurrence period that’s shorter, such as everyfive minutes, or longer, such as once every month? In these cases, we candefine our own recurrence frequency and export it to WordPress. This isdone by attaching a function to the cron_schedulesfilter:

add_filter('cron_schedules','my_cron_definer');    function my_cron_definer($schedules){  $schedules['monthly'] = array(      'interval'=> 2592000,      'display'=>  __('Once Every 30 Days')  );  return $schedules;}

With this done, we can now use the new frequency when callingwp_schedule_event:

wp_schedule_event(time(),'monthly','my_action');

This will call my_action once every 30 days,starting from the current time.

Clearing Scheduled Actions

Let’s say the administrator finds that their inbox keeps filling upwith backup emails when using our database backup plugin. Perhaps thedaily backup is overkill, considering the modest level of activity ontheir blog. So the admin logs into the WordPress dashboard and changes thebackup frequency to weekly instead of daily.In this scenario, the database plugin should change the frequency ofthe backup routine. Here we run into a bit of a roadblock: WordPress’sscheduling API doesn’t allow us to change the periodicity of a hook thathas already been scheduled. We need to first remove the daily schedule,and then attach a new weekly hook.

Unscheduling Recurring Events

To remove all future instances of a recurring action, thewp_clear_scheduled_hook function should beused. The syntax couldn’t be simpler:

wp_clear_scheduled_hook('name_of_hook');

Unscheduling One-off Events

One-off events are unscheduled by using thewp_unschedule_event function. Using thisfunction is less straightforward than the unscheduling of recurringevents.The syntax is as follows:

wp_unschedule_event($timestamp, $hook, $arguments);

Here, the $timestamp argument is the time whenthe event is scheduled to run. As you may be unaware of when the actionhas been scheduled to run, you can use thewp_next_scheduled function to fetch the time,and then pass that intowp_unschedule_event:

$whenNext = wp_next_scheduled('my_action');wp_unschedule_event($whenNext,'my_action');

Where should tasks be scheduled?

Now that you know how to schedule and unschedule tasks, you’re leftwith the question of whereabouts in your code to set up this schedule. Ifyour plugin should start the execution of the tasks immediately after theplugin is activated, the tasks must be registered within the activationhook. The activation hook is provided by WordPress, and is triggered whenthe plugin is first activated.When your plugin is deactivated, all tasks scheduled by your pluginshould be unscheduled. This makes sure that no duplicate tasks arescheduled by WordPress if your plugin is reactivated. This also ensuresthat any other plugin that makes use of the tasks scheduled by your plugindoesn’t produce undesired behavior.This very simple plugin demonstrates both the scheduling of taskswhen the plugin is activated, and their removal when the plugin isdeactivated:

<?php   /*   Plugin Name: My Plugin   Plugin URI: http://www.sitepoint.com/   Description: A plugin to learn crontasks.   Version: 2.9.2   Author: Raj Sekharan   Author URI: http://www.krusible.com   *//* The activation hook is executed when the plugin is activated. */register_activation_hook(__FILE__,'myplugin_activation');/* The deactivation hook is executed when the plugin is deactivated */register_deactivation_hook(__FILE__,'myplugin_deactivation');/* This function is executed when the user activates the plugin */function myplugin_activation(){  wp_schedule_event(time(), 'hourly', 'my_hook');}/* This function is executed when the user deactivates the plugin */function myplugin_deactivation(){  wp_clear_scheduled_hook('my_hook');}/* We add a function of our own to the my_hook action.add_action('my_hook','my_function');/* This is the function that is executed by the hourly recurring action my_hook */function my_function(){   //do something.}?>

Listing All Tasks Currently Scheduled

While developing plugins that use scheduled tasks, it’s oftenhelpful to be able to see if the task has indeed been scheduled, andwhether it has been assigned the correct frequency. This can be done usingthe ControlCore plugin.The Control Core plugin was created to aid plugin developers intheir work, by providing control of certain aspects of the WordPress core.Control Core adds a section to the administration panel that shows all thetasks that are currently scheduled.

Caveats

This method of scheduling tasks comes with a few warninglabels:

Be aware that it’s not 100% precise
For those of you accustomed to system-based cron jobs, oneobvious disadvantage of the WordPress scheduling API is that it’simpossible to ensure that the action you scheduled will run at anexact time. Although the scheduled functions will definitely beexecuted, they may run a little later than you’d like them to. Thisis because WordPress relies on website traffic to triggertasks.In cases where a website may lack adequate traffic, ascheduled task might not execute at the proper times. While anaction was scheduled to run at 12 a.m., it may actually run at 12:45a.m., because no one visited the website that late at night.
Use appropriate frequencies
Some time ago, I created a plugin that generated some contentpages for a blog by querying the Yahoo Search web service and theTwitter web service. The script was scheduled to run every fiveminutes. I wrote the plugin and configured it to run on myblog.The next day, my web hosting account was temporarily suspendedfor using excessive resources. It turned out that the scriptsometimes ran for far more than five minutes. This meant that a newtask would often start before the previous one had completed; aftercouple of hours there were 12 instances of the script running. Soonthere was no more memory left over for any other process, and thescript started to affect other website owners on the shared hostingserver. The lesson here is that you should ensure that you choosethe recurrence interval for your resource-consuming procedurescarefully.
Use flags when using shared resources
Scheduled tasks will often make use of the file system, orcertain database tables. Simultaneous access or operations on theseresources can corrupt or destroy the integrity of the data. This canhappen when your plugin has two or more tasks that rely on the samedata file or database table. Be sure to use read/write locks toprotect access to your resources.

Final Notes

Although it’s possible to create WordPress plugins that performperiodic tasks without the WordPress scheduling API, it’s disadvantageousto do it that way. The procedure to set up your plugin will become morecomplicated, as the users will have to set up the crons manually.Complicated procedures only reduce the number of bloggers who can benefitfrom using your plugin.The setup process for a plugin should be as simple as clickingActivate in the Plugins section of the WordPressadministration panel. By making use of WordPress’s built-in schedulingfunctionality, you can give your plugins sophisticated features withoutmaking them more complicated to use.

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • http://twitter.com/anthonyblears anthonyblears

    Just what I needed. Thanks for the clear and concise guide.

  • http://test.ical.ly Christian

    Probably the most insightful article on this matter on the web. Thanks!