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

15 thoughts on “WordPress: How to use WP-Cron

  1. All Slides from Orange County WordCamp 2012 | Web Training Wheels
  2. Ben Lobaugh Online » I am speaking at the 2012 Orange County WordCamp!
  3. Steve Daily (March 8, 2013)

    Thanks for a clear and thorough explanation of the WordPress cron. It is just what I needed.

  4. Butt (April 9, 2013)

    Is there any plugin of WordPress cron? I can’t find a plugin for this functionality. Your help appreciated.

  5. Frank Mcgrath (April 26, 2013)

    WOW, clear and elaborate. Thanks!

  6. Omprakash (June 24, 2013)

    What a great work “Ben” did here! I think it is no one can explain much easier that this! Good job “Ben”. I would like to learn lots of WordPress concepts from you.

  7. Eric Amundson (July 30, 2013)

    Thanks Ben! Great presentation at WordCamp Portland last year and terrific write-up of wp-cron!

  8. Annie (November 5, 2013)

    There is another way to do the cron job things in WordPress: http://wordpress.org/plugins/easycron

    1. Ben Lobaugh (blobaugh) (November 6, 2013)

      Future readers please note that while this service may in fact perform the cron jobs when you need them to it is an outside company that you have to register with. If you require cron jobs running at specific times and do not have access to the system cron this could be useful to you.

  9. Adam (November 7, 2013)

    I’m trying to basically do a “Hello World” test of how I would use this to affect some output on my WordPress site at a certain time or interval. As a test, I figured I’d try to add a body class every 5 seconds. (This isn’t what I’m ultimately trying to do, but I wanted something I could see pretty easily, for my own testing purposes.)

    To do that, I tried this code, based on yours:

    ———-

    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() {
    add_filter( ‘body_class’, ‘bl_cron_classes’ );
    }

    function bl_cron_classes($classes) {
    $classes[] = ‘wooitworked’;
    return $classes;
    }

    ———-

    It just completely fails. Nothing happens whatsoever.

    When I move the body_class filter outside of the bl_cron_exec() function, I get a body class of “wooitworked,” as I would expect. When I move it into the bl_cron_exec() function, I thought I would get a body class of “wooitworked” every 5 seconds. Instead, I get nothing.

    What am I missing here?

    1. Ben Lobaugh (blobaugh) (November 8, 2013)

      What you are missing is how wp-Cron works ;). WP-Cron does not run where the user can see it, but in the background. You will never see its direct output. Check out the Testing the Code section above on how you can see something like you are attempting.

  10. zazzzy (July 10, 2014)

    One question: If I disable wp-cron in the config, are there plugins that may or may not potentially rely on it that I may be disabling? That is, can I be more specific/granular about what I am disabing (e.g. mydomain/wp-cron.php?[somethinghere]?

    1. Ben Lobaugh (blobaugh) (July 10, 2014)

      I do not know of any way to make it more granular via the wp-config file. That would probably need to happen on a plugin by plugin basis. If you disable wp-cron through the config you will need to setup some way of hitting the http://yoursite.tld/wp-cron.php file in order to run wp-cron again. You can do that via the system service on the server or even through a service such as pingdom.

  11. kshirod (April 6, 2016)

    Hi Ben,
    Thanks for this great tutorial. I wanted to can we set some task like sending emails to users in every hour without loading wp-cron.php on the browser. I just want that when any visitor visits the site it will run the function. If user doesn’t visit the site still it will send the email in every hour. So how that can be done? I am trying to send emails and that function is inside my plugin. So how that can be achieved? Any help and suggestions will be really appreciable. Thanks

    1. Ben Lobaugh (blobaugh) (April 7, 2016)

      Something always has to trigger it. If you do not have any traffic and are not using a system cron it simply will not run. You can run a system cron in addition to the regular way wp-cron works. If that is not a possibility there are other options. You can use a ping service that check site uptime. There are a few free tools for that. If you use Jetpack the Monitor periodically pings the site (5 min intervals?) and that should also trigger wp-cron to run.