In this post, I’ll be going over what transients are, how to use them, and why you should use them.

What are Transients?

Transients are a way to cache information in WordPress.  They are very similar to WordPress Options with one key difference: Transients include a lifespan.  We’ll get into what that means in the next section.

How do I use Transients?

The Transient API in WordPress is pretty straightforward.  If you’ve ever used the Options API, this will look very similar.

Setting a Transient

To set a transient, you use:

set_transient($transient, $value, $expiration);

$transient is a string you want to use as the unique identifier for this transient.

$value is the data you want to store in the transient.

$expiration is the maximum amount of seconds to keep the data.  We’ll come back to why maximum is important in a bit.

WordPress comes with some useful helper constants that you can use to set the expiration time:

MINUTE_IN_SECONDS = 60 (seconds)
HOUR_IN_SECONDS = 60 * MINUTE_IN_SECONDS
DAY_IN_SECONDS = 24 * HOUR_IN_SECONDS
WEEK_IN_SECONDS = 7 * DAY_IN_SECONDS
YEAR_IN_SECONDS = 365 * DAY_IN_SECONDS

So if you wanted the transient to expire in 3 hours, you would use 3 * HOUR_IN_SECONDS

Getting a Transient

To retrieve the value of a transient, you use:

get_transient( $transient );

$transient is the unique identifier you used in set_transient() above.

This function returns false if the transient isn’t set, so a common pattern to use looks something like this:

if ( false === ($value = get_transient( 'value' ))) {
  //do stuff to set the transient
}

Deleting a Transient

To delete a transient, you use:

delete_transient( $transient );

$transient is again the unique identifier for the the transient

Use this function when you want to unset a transient before its expiration time.

Other Methods

If you are using WordPress Multisite, you can take advantage of these functions to set network wide transients that can be used on any site.

set_site_transient()
get_site_transient()
delete_site_transient()

Why should I use Transients?

Transients are useful for caching the results of complicated queries or queries that take a long time to run.  For example, if you have a page on your site where you are making lots of API requests for data from something like the Github API, then you could use transients to store the results of those requests and only refresh them once a day or so instead of on every page load.  I’ll show you an example of this in the next section.

Code

Let’s write some code so we can see exactly how transients work.  If you want to follow along, run this command in your wp-content/plugins directory:

git clone -b 00-initial-widget https://github.com/ryanplasma/transient-demo-widget.git

That will pull down the initial widget code we’ll be working with.

Initial Widget Code

What we’re going to build is a quick sidebar widget that displays our Github repos.  Here’s what we have stubbed out so far:

public function widget( $args, $instance ) {
    $output = '';
    $output .= '<h1>Github Repos</h1>';
    $output .= '<ul>';
    $output .= '<li><a href=#>Repo 1</a></li>';
    $output .= '<li><a href=#>Repo 2</a></li>';
    $output .= '<li><a href=#>Repo 3</a></li>';
    $output .= '</ul>';

    echo $output;
}

If we enable the plugin and add the widget to the sidebar, we get something that looks like this (I’m using the stock Twenty Sixteen theme):

screen-shot-2016-10-20-at-1-48-46-pm

This looks alright, but it would be nice if we could pull in our data from github so we have a real list of our repos.

Getting the data from the API

Since it’s a bit outside the scope of this post, I’ll just quickly explain the code used to get the data from the Github API.  It’s going to look something like this:

$url = "https://api.github.com/users/ryanplasma/repos";
$response = wp_remote_get( $url );
$body = wp_remote_retrieve_body( $response );
$repos = json_decode( $body );

$repos will then contain an array of repo data.

There are many pieces of information that come with that request, but the ones we’re looking for are the name of the repo and the link.  The properties names for these are name and html_url respectively.

Knowing this, we can edit the output to show our actual repos.  Our full widget output method will look something like this:

public function widget( $args, $instance ) {
  $url = "https://api.github.com/users/ryanplasma/repos";
  $response = wp_remote_get( $url );
  $body = wp_remote_retrieve_body( $response );
  $repos = json_decode( $body );

  $output = '';
  $output .= '<h1>Github Repos</h1>';
  $output .= '<ul>';

  foreach ($repos as $repo) {
    $output .= '<li>';
    $output .= '<a href=' . $repo->html_url . '>';
    $output .= $repo->name;
    $output .= '</a>';
    $output .= '</li>';
  }

  $output .= '</ul>';

  echo $output;
}

and when we save it, our widget now looks something like this:

screen-shot-2016-10-20-at-2-58-52-pm

Much better! However, we have a few problems:

  1. If you’ve been following along, point your browser at https://api.github.com/rate_limit and you’ll see that we’ve made a dent in our rate limit for the Github API.  Your site may not get much traffic, but you’ll run into issues if you refresh the page more than 60 times in an hour, which really isn’t that much.
  2. It’s may be negligible at this point, but making the request to the github API adds 200 or so milliseconds to our page load time.  Imagine if we started adding features to this widget that required more API calls like getting links to the 5 most recent issues for each repo.  That time adds up quick.

Using Transients

Let’s negate those issues by using what we learned about transients above.  If you’ve gotten lost, here’s where we’re at so far: https://github.com/ryanplasma/transient-demo-widget/tree/01-data-from-API

First let’s keep track of how long getting the repo data takes, so we have a benchmark for when we start using transients. That’s going to look like this:

public function widget( $args, $instance ) {
  $time1 = microtime();

  $url = "https://api.github.com/users/ryanplasma/repos";
  $response = wp_remote_get( $url );
  $body = wp_remote_retrieve_body( $response );
  $repos = json_decode( $body );

  $time2 = microtime();

  $output = '';
  $output .= '<h1>Github Repos</h1>';
  $output .= '<ul>';

  foreach ($repos as $repo) {
    $output .= '<li>';
    $output .= '<a href=' . $repo->html_url . '>';
    $output .= $repo->name;
    $output .= '</a>';
    $output .= '</li>';
  }

  $output .= '</ul>';

  $benchmark = $time2 - $time1;
  $output .= '<p>Time: ' . $benchmark . ' seconds';

  echo $output;
}

I’ve added 2 microtime() functions to time how long getting the data takes and then I have it output to the bottom of the widget.  I’m consistently getting about .2 seconds

screen-shot-2016-10-20-at-3-24-56-pm

Let’s now take advantage of the pattern for seeing if a transient is set that we looked at earlier.

if ( false === ($value = get_transient( 'value' ))) {
  //do stuff to set the transient
}

This is going to end up looking something like this:

if ( false === ($repos = get_transient( 'github_repos' ))) {
    $url = "https://api.github.com/users/ryanplasma/repos";
    $response = wp_remote_get( $url );
    $body = wp_remote_retrieve_body( $response );
    $repos = json_decode( $body );

    set_transient( 'github_repos', $repos, DAY_IN_SECONDS);
}

After this runs, $repos will either be retrieved from the API or from the transient if it is set.

The first time we refresh the page, the content is still coming from the api and still takes about 200 miliseconds to fetch:

 

screen-shot-2016-10-21-at-11-16-45-pm

but after refreshing again, we see a considerable speed difference:

screen-shot-2016-10-21-at-11-17-07-pm

This is .3 milliseconds, which is practically a 100% decrease in time to fetch the repo data – almost instantaneous.

Other Resources:

If you want to learn more about transients, here are some helpful resources:

Leave a comment

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.