WordPress: How to use WP-Cron

June 2, 2012

* This post was created in conjunction with a presentation for the 2012 Orange County WordCamp. If the session is recorded I will post it here when I receive it.

Important resources

Presentation slides:

Full source code used in this tutorial:

The Video

My presentation at 2012 WordCamp Portland was recorded! Here is the video from that

What is WP-Cron and why use it?

Cron is the time-based task scheduling system that is available on UNIX systems. WP-Cron is how WordPress handles scheduling time-based tasks in WordPress.  Several WordPress core features, such as checking for updates and publishing scheduled post, utilize WP-Cron, though WP-Cron is still an largely under-documented and under-utilized feature in WordPress.

WP-Cron works by checking on every page load through a list of scheduled tasks to see if there are any tasks that need to be run. If there are any tasks scheduled at the time of page load they will be run. It must be noted here that WP-Cron does not run constantly as the system cron does. WP-Cron will only be triggered on page load, this means if you have a task scheduled for 2:00 PM but do not have any visitors until 5:00 PM your task will not run until then.

Why use WP-Cron? Well first off, the vast majority of hosting services are shared and their users do not have access to the system cron. This is a real bummer as there are a lot of great uses for a cron system, thus the beginnings of WP-Cron. Though it may not run at a specific time, WP-Cron will get your tasks done in a good manner. WordPress is all about simplicity, so rather than forcing your users to go outside of WordPress to setup a task it is much more simple to utilize the WordPress API.

How to test the wp-cron jobs

In this tutorial we will be creating a plugin that runs a task every 5 seconds and displays a message. In order to test this we will load the wp-cron.php file directly in our browser, otherwise we would have to perform some other action, maybe in the database, as the output is typically not shown on the site. So let’s run through the initial steps to get this setup quickly.

Create the plugin file

In my wp-content/plugins folder I create the folder ‘2012-WordCamp-Orange-County-WP-Cron’ and the file ‘2012-wordcamp-oc-wp-cron.php’. Obviously you can name it whatever you would like. My name is simply descriptive of my intended use.

Open the PHP file for editing and insert the following lines

?php
/*
Plugin Name: 2012 WordCamp Orange County WP-Cron
Plugin URI: https://github.com/blobaugh/2012-WordCamp-Orange-County-WP-Cron
Description: Code to go along with my WP-Cron presentation at the 2012 Orange County WordCamp conference. To most easily visualize this code install the plugin and visit http://yourWPSite/wp-cron.php
Version: 0.1
Author: Ben Lobaugh
Author URI: https://ben.lobaugh.net
*/

This text will setup the plugin for display and activation in your wp-admin Plugins menu.

BE SURE TO ENABLE THE PLUGIN. Yes, I have forgotten this step during live demonstrations before…very embarassing.

Testing the code

Open your browser and point it to YOUR_SITE_URL/wp-cron.php

View all currently scheduled tasks

WordPress has an undocumented function, _get_cron_array, that returns an array of all currently scheduled tasks. We are going to use a crude but effective method to dump out all the tasks using var_dump. For ease of use place the following code:

echo '<pre>'; print_r(_get_cron_array());

Into an easy to call function like:

function bl_print_tasks() {
echo '<pre>'; print_r(_get_cron_array());
}

Understanding WP-Cron intervals

Unlike a traditional system cron that schedules for specific times (I.E. Every hour at 5 minutes past the hour), WP-Cron uses intervals. The way this works is by giving WP-Cron a time to schedule the first task, and then an interval in seconds for it to repeat. So if you schedule the task for June 2nd, 2012 at 2:00 PM with an interval of 300 seconds the task will run for the first time on June 2nd, 2012 at 2:00 PM, then again June 2nd, 2012 at 2:05 PM, and again June 2nd, 2012 at 2:10 PM, etc at five minute intervals until unscheduled.

You do not give the seconds for the interval when scheduling an event, rather you give the name of an existing interval in the system. WordPress ships with a very limited number of built-in intervals. Lucky for us, in true WordPress fashion, there is a hook for adding more. The hook is a filter called ‘cron_schedules’. The built-in intervals are: hourly, twicedaily, daily. Obviously not much to work with, so let’s take a look at how to add our own custom intervals, but first a quick snippet of code to dump out existing intervals:

echo '<pre>'; print_r(wp_get_schedules()); echo'</pre>';

To create a custom interval we tap into the ‘cron_schedules’ filter and alter the schedules array that gets passed to our custom function. The function will take one parameter. That parameter is an array containing all of the currently existing intervals.

add_filter( 'cron_schedules', 'bl_add_cron_intervals' );

function bl_add_cron_intervals( $schedules ) {

   $schedules['5seconds'] = array( // Provide the programmatic name to be used in code
      'interval' => 5, // Intervals are listed in seconds
      'display' => __('Every 5 Seconds') // Easy to read display name
   );
   return $schedules; // Do not forget to give back the list of schedules!
}

Be sure to return the array containing the intervals or you will erase all the intervals on the system, and potentially cause bad behavior of WordPress.

Schedule a recurring task

In order to get your task to execute you must create your own custom hook and give that hook the name of a function to execute. This is a very important step and buggered me up for a while when creating my first plugin using WP-Cron.

The following will create the hook. The first parameter is the name of the hook, and the second is the name of our function to call.

add_action( 'bl_cron_hook', 'bl_cron_exec' );

Now on to the actual scheduling of the task. Another important note is that WP-Cron is kind of naive when scheduling tasks. Tasks are driven by the hook provided for the task, however if you call wp_schedule_event() multiple times, even with the same hook name, the event will be scheduled multiple times. If your code adds the task on each page load this could result in the task being scheduled several thousand times. Probably not a great idea. WordPress provides a convenient function called wp_next_scheduled() to check if a particular hook is already scheduled.

wp_next_scheduled() takes one parameter, the name of the hook. It will return either a string containing the timestamp of the next execution or false, signifying the task is not scheduled. It is used like so:

wp_next_scheduled( 'bl_cron_hook' )

Scheduling a recurring task is accomplished with wp_schedule_event(). This function takes three required parameters, and one additional parameter that is an array that can be passed to the function executing the wp-cron task. We will focus on the first three parameters. The parameters are as follows:

  1. $timestamp – The UNIX timestamp of the first time this task shoud execute
  2. $recurrence – The name of the interval in which the task will recur in seconds
  3. $hook – The name of our custom hook to call

We will use the 5 second interval and the hook we created earlier like so:

wp_schedule_event( time(), '5seconds', 'bl_cron_hook' );

Remember, we need to first ensure the task is not already scheduled, the full code for that is the following:

if( !wp_next_scheduled( 'bl_cron_hook' ) ) {
wp_schedule_event( time(), '5seconds', 'bl_cron_hook' );
}

Test the plugin

To test the code, save the code in your plugin file, make sure the plugin is activated, and visit YOUR_SITE_URL/wp-cron.php in your browser. Keep hitting refresh and every five seconds you should see a message along with a cute little kitty.

For convenience here is the code completed so far:

add_filter( 'cron_schedules', 'bl_add_cron_intervals' );

function bl_add_cron_intervals( $schedules ) {

   $schedules['5seconds'] = array( // Provide the programmatic name to be used in code
      'interval' => 5, // Intervals are listed in seconds
      'display' => __('Every 5 Seconds') // Easy to read display name
   );
   return $schedules; // Do not forget to give back the list of schedules!
}

add_action( 'bl_cron_hook', 'bl_cron_exec' );

if( !wp_next_scheduled( 'bl_cron_hook' ) ) {
   wp_schedule_event( time(), '5seconds', 'bl_cron_hook' );
}

function bl_cron_exec() {
   echo "Oh Lookie! This is your scheduled cron, grinding out some hardcore tasks...And now a kitty!<br/><figure><img src='http://wpengine.com/wp-content/uploads/2012/04/lolcat-stealin-ur-heart.jpg'/><figcaption>Photo courtesy WCOC 2012, Stolen from WPEngine</figcaption></figure>";
}

Unscheduling tasks

When you no longer need a task scheduled you can unschedule tasks with wp_unschedule_event(). This function takes the following two parameters:

  1. $timestamp – Timestamp of the next occurrence of the task
  2. $hook – Name of the custom hook to be called

This function will not only unschedule the task indicated by the timestamp, it will also unschedule all future occurrences of the task. Since you probably will not know the timestamp for the next task there is function, wp_next_schedule() that will find it for you. wp_next_scheduled() takes one parameter (that we care about):

  1. $hook – The name of the hook that is called to execute the task

Put it all together and the code looks like:

$timestamp = wp_next_scheduled( 'bl_cron_hook' );
wp_unschedule_event($timestamp, 'bl_cron_hook' );

It is very important to unschedule tasks when you no longer need them as WordPress will continue to attempt to execute the tasks, even though they are no longer in use. An important place to remember to unschedule your tasks is upon plugin deactivation. Unfortunately there are many plugins in the repository that do not clean up after themselves. If you find one of these plugins please let the author know to update their code. WordPress provides a function called register_deactivation_hook() that allows developers to run a function when their plugin is deactivated. It is very simple to setup and looks like:

register_deactivation_hook( __FILE__, 'bl_deactivate' );

function bl_deactivate() {
   $timestamp = wp_next_scheduled( 'bl_cron_hook' );
   wp_unschedule_event($timestamp, 'bl_cron_hook' );
}

Hooking WP-Cron into the system task scheduler

As previously mentioned, WP-Cron does not run continuously, which can be an issue if there are critical tasks that must run on time. There is an easy solution for this. Simply setup your systems task scheduler to run on the intervals you desire (or at the specific time needed). The easiest solution is to use a tool to make a web request to the wp-cron.php file. wget is a great tool to accomplish this. In your task scheduler insert the line

wget YOUR_SITE_URL/wp-cron.php

There is one more step to complete. WordPress will continue to run WP-Cron on each page load. This is no longer necessary and will contribute to extra resource usage on your server. WP-Cron can be disabled in the wp-config.php file. Open the wp-config.php file for editing and add the following line:

define(‘DISABLE_WP_CRON’, true);

Congrats, you should now have all the knowledge you need to get your own WP-Cron tasks scheduled and running. Please leave comments below if you find a mistake or have further questions.

Resources