Transients API in WordPress

Transients API in WordPress

Online by JSC0d3
July 10, 2017 | | | 380 recognitions

WordPress provides us with a wonderful API for managing options. Options are ridiculously simple: they each have a name and a value and are stored in the WordPress database – in the options table – for safe keeping. Options can store anything from your Google Maps API key to an array of data or even the HTML code for your sidebar.

Transients are similar in all but one property; they have an expiration time. This makes them uniquely suited to act as a cache for appropriate data. In addition to reducing the number of queries our website utilizes, they can be further sped up by caching plugins which may make WordPress store transients in fast memory (like Memcached) instead of the database.

Transients API in WordPress

In this post I’ll take you through the code you need to know to use transients and show you some advanced examples of how they can be utilized. I’d like to focus on the fact that transients can be used as an easy cache mechanism for any object, not just simple strings or WordPress related objects.

Why would you want to use a transient?

In the context of WordPress you most often hear caching in the sense of “full page” caching. The classic example of a plugin like WP Super Cache does this — it caches full pages just about to be sent out to a visitor and stores it so that the next visitor to ask for that specific page will get the same version as the previous visitor without your server having to build it. In some situations this is ideal, and can have a really significant impact on site speed.

You can get performance gains from storing things other than full pages that will make the whole of your site faster to deliver. Common examples are the results of long-running computations, slow results from remote servers (a pulling of data from Twitter, or Facebook, for example), or large database queries.

But there’s also the idea of “partial” or “object” caching. Rather than storing and getting performance gains from saving a full page response, you can get performance gains from storing things other than full pages that will make the whole of your site faster to deliver. Common examples are the results of long-running computations, slow results from remote servers (a pulling of data from Twitter or Facebook, for example), or large database queries which are likely to be slow and change infrequently. In WordPress, the way to store this type of data with the Transients API.

What is the WordPress transients API?

Quite simply, the WordPress Transients API is an abstraction of the various means and methods of caching data in a server environment so that WordPress developers don’t have to worry too much about the specifics of each WordPress site’s operating environment.

Why this abstraction? At the most basic level, we just want to store a hunk of data that we give a name and be able to get it back quickly. This so-called “key value store” is exactly what in-memory caching systems — like Memcached — allow. If you’re not familiar, let’s just suffice to say that Memcached is a very simple and powerful way to store blobs of data and retrieve them later. It’s much faster than the MySQL database or the filesystem for data storage and retrieval, and skips the complexity of figuring out the specific mechanism. But, especially important in the world of WordPress, it’s not everywhere. In fact, for WordPress, it’s not even on most servers where WordPress runs.

So what the Transients API does is allow a WordPress programmer to think about the data they want to cache just like they would if Memcached or a similar key value storage system existed, but not have to worry or program around the fact that it’s not always there. It’s not essential that you know this, but when Memcached or other forms of caching aren’t available, WordPress uses its regular database, the optionstable to be precise, to provide the same functionality as you’d get with an in-memory cache. (Though unfortunately you don’t quite get the the same speed.)

The final thing to know is that these “caches” or “transients” aren’t meant to be permanent, the Options API — which we recently explained — is what we use for data we need to have on a WordPress site. (See Ryan McCue’s great post about that.) There’s a common saying among computer scientists:

There are only two hard things in Computer Science: cache invalidation and naming things. — Phil Karlton

We’ll leave off naming things for now, but the easiest way to deal with cache invalidation is to never do it. So when we cache, we just make our blob live for a certain period when we expect it will remain “fresh” enough that we still want to use it, and then silently let it disappear.

Transients provide us with ways to temporarily store cached information by providing a custom name (key-value pairs) and an expiration time. Once the defined timeframe is over, the transients expire and get deleted. These transients improve the performance and speed up the overall web app performance.

But a question arises: Is the expiration time the only reason for using the WP Transient API? 

The answer is no! Despite the fact that the Options API serves the same purpose of data storage, sanitization and retrieval, it may not provide the best possible performance with large sets of data.

With timeframe being added, transients become the most appropriate way of storing data temporarily. To ensure a lesser number of web queries, transients have the capability to store data in the fast memory, e.g. Memcached, instead of the traditional WordPress database. Also of note is that Transients are inherently sped up by caching plugins, where normal Options are not. As mentioned in the codex:

A Memcached plugin, for example, would make WordPress store transient values in fast memory instead of in the database. For this reason, transients should be used to store any data that is expected to expire, or which can expire at any time. Transients should also never be assumed to be in the database, since they may not be stored there at all.

Therefore, whenever you require a functionality which expires or gets deleted after a particular time span, use transients instead of options. More on that later.

Transients work with an incredibly simple interface. You can perform three basic functions with them:

  • creating/updating a transient via the set_transient() function
  • retrieving a transient via the get_transient() function
  • deleting a transient via the delete_transient() function

These three basic operations can help you speed up web performance.

Use the set_transient() function to create or update any transient. The function takes three parameters:

  • Key: (type string) Name of the transient. Must be 172 characters or fewer in length.
  • Value: (type mixed) It’s the data which needs to be stored. Can be a PHP variable or an array object.
  • Expiration: (type int) Max time until expiration in seconds. Default 0 (no expiration).

Point to Ponder: The expiration date that you set is the maximum amount of time for which a transient will be stored. After that time, the transient gets deleted. But it can also get deleted before that time. Since it is part of the cache, it can be deleted by the user before that time. So, always think of the expiration time as the maximum amount of time for the life of a transient with only the guarantee that it will be deleted after that.

The three basic operations available are: getset and delete. Each operation has a site-specific version and a network-wide version which is used if a transient should be made available to the whole multisite network. Based on this extremely simple interface we can build pretty sophisticated systems to speed up our website considerably.

Setting Transients

By using the set_transient() function we can create a transient. The function takes two required and one optional parameter. The first one is the name of the transient, the second is the value, the third is the expiration time.

set_transient( 'js_mood', 'J&S CODE', 28800 ); // Site Transient
set_site_transient( 'js_mood', 'J&S CODE', 28800 ); // Multisite Transient

When the code above is executed it stored my current mood for 28800 seconds (8 hours). Note that you can not retrieve the value of the transient after the expiration date, it will no longer exist.

Getting Transients

Retrieving the value of transients is even simpler than setting them. By utilizing the get_transient() functions and passing the transient name as the first – and only – parameter we can retrieve the value of our transient.

$js_mood = get_transient( 'js_mood' ); // Site Transient
$js_mood = get_site_transient( 'js_mood' ); // Multisite Transient

Be mindful of the return value when creating your transients! If the transient has expired, doesn’t exist or doesn’t have a value the function returns ‘false’. This means that you should use the identity operator (===) instead of the equality operator (==) when checking the value. It also means that you should not store plain boolean values, convert them to integers instead.

Deleting Transients

In many cases you’ll want to delete transients before they expire which is when the delete_transient() function comes in handy. It takes one parameter, the name of the transient.

delete_transient( 'js_mood' ); // Site Transient
delete_site_transient( 'js_mood' ); // Multisite Transient

Expiration In-Depth

The most important thing to keep in mind is that transients which never expire – have an expiration set to 0 – are autoloaded, other transients are not autoloaded. We can use this to our advantage by figuring out which transients are used frequently.

Never Expiring Transients

So what is the point of a transient which never expires? You could use it to store a custom recent posts widget for example. This transient can be deleted and re-set when a new post is published so we don’t need to give it an expiration. Since the expiration is set to 0 the transient is autoloaded which is perfect if we want to show it on all pages.

If you only show the widget on your about page there really is no need to autoload the transient. In this case it is better to give it an extremely large expiration date like 3153600000 which would be 100 years.

Time Constants

Since WordPress 3.5 time constants have been available to give developers easy access to time in seconds.

set_transient( 'js_mood_today', 'J&S CODE', DAY_IN_SECONDS );

The following five constants can be used:

set_transient( 'js_mood_today', 'J&S CODE', MINUTE_IN_SECONDS );
set_transient( 'js_mood_today', 'J&S CODE', HOUR_IN_SECONDS );
set_transient( 'js_mood_today', 'J&S CODE', DAY_IN_SECONDS );
set_transient( 'js_mood_today', 'J&S CODE', WEEK_IN_SECONDS );
set_transient( 'js_mood_today', 'J&S CODE', YEAR_IN_SECONDS );

The Uses Of Transients

When talking about transients the biggest error people make is that they seem to think it is for discreet data only. My mood, time of day, current temperature. All good, all time-sensitive so all ripe for transiency.

Looking beyond simple data you can figure out tons of great ways to use transients. Think about data that changes on your site, but not too frequently. A widget or a whole sidebar could be a good example. Is it really necessary to perform database queries each time your sidebar is shown?

Creating the navigation menu is also a pretty database-intensive task. Once the website admin has assembled the required menu structure it rarely changes. Why not load the whole thing from a transient? Using hooks we can still force a re-load if menu items change.

How about a section at the bottom of each single post which shows a couple of interesting posts from your archives. Why pull this from the database on every single page load when you could cache it as a transient and re-load it no more than once every hour?

Caching The Navigation Menu

A theme usually uses wp_nav_menu() to output a menu. Parameters are passed to this function and the correct menu is displayed. This function returns the menu in HTML form which is great, it’s a string we can easily store as a transient. Let’s create a function which will replace wp_nav_menu() in our theme.

function js_transient_menu( $args = array() ) {
    $defaults = array(
        'menu' => '',
        'theme_location' => '',
        'echo' => true,
    );

    $args = wp_parse_args( $args, $defaults );

    $transient_name = 'js_menu-' . $args['menu'] . '-' . $args['theme_location'];
    $menu = get_transient( $transient_name );

    if ( false === $menu ) {
        $menu_args = $args;
        $menu_args['echo'] = false;
        $menu = wp_nav_menu( $menu_args );
        set_transient( $transient_name, $menu, 0 );
    }

    if( false === $args['echo'] ) {
        return $menu;
    }

    echo $menu;

}

Our js_transient_menu() function takes the same arguments as wp_nav_menu(). I’ve made sure that the menutheme_location and echo parameters have default values. Since we use outside of passing them to wp_nav_menu() they must have a value, otherwise a PHP notice will be thrown.

For each menu we create a transient with the naming scheme of scotch_menu-[menu]-[location]. This allows for flexible use within themes that may call for multiple menus. If you have a single menu you don’t need to get fancy like this, you can just hard-code this with it’s menu ID.

We try and retrieve the menu using the get_transient() function. Remember that this should return false if the transient has expired, has no value or doesn’t exist. We use this to determine if we need to refresh our cache.

If the $menu variable is false, I create a new array of options to pass to the wp_nav_menus() function. The reason for this is that we need to make sure the echoparameter is false while retrieving the menu. We may echo it later, but we need the return data first to store as the transient.

The next step is to set the transient. Since the menu is likely to be shown on every page I thought giving it an expiration of 0 would be best, since it would be autoloaded.

So far so good, our menu is now cached and served with the minimum amount of fuss. The problem we’re facing now is that changes in our menus will never be reflected on our site. This is where hooks come in which allow us to update our cached value.

The wp_update_nav_menu action is fired whenever a menu item is updated. If we create a function that updates our cached value each time we are in the clear.

add_action( 'wp_update_nav_menu', 'js_update_menus' );
function js_update_menus() {
    global $wpdb;
    $menus = $wpdb->get_col( 'SELECT option_name FROM $wpdb->options WHERE option_name LIKE "js_menu-%" ' );
    foreach( $menus as $menu ) {
        delete_transient( $menu );
    }
}

This is a bit crude but it does the job. Since we need to find all menus possible we need to manually look through the database to match our naming convention. This would also be a lot easier if you just need to cater to a specific menu.

In conclusion our menus our now highly optimized. They are served from an autoloaded transient so no database queries are necessary apart from loading the option. Whenever a menu item chnges our transient is deleted and the menu will be rebuilt whenever someone accesses the site.

Creating A Recent Projects Section

Suppose you have a personal site an on your about page you list your recent projects. The list comes from a custom post type, the code looks something like this:

$args = array(
    'post_type'   => 'project',
    'post_status' => 'publish'
);
$projects = new WP_Query( $args );

if ( $projects->have_posts() ) {
    echo '<ul>';
    while( $projects->have_posts() ) {
        echo '<li><a href="' . get_permalink() . '">' . the_title( '', '', false ) . '</a></li>';
    }
    echo '</ul>';
}

Each time the page is loaded the database query is made, even if you take on a new project every 5 years. What a waste! Let’s fix this shall we? We’ll use the same thinking is we did in our previous example, with two small changes

$projects = get_transient( 'js_cached_projects' );
if ( false === $projects ) {
    $args = array(
        'post_type'   => 'project',
        'post_status' => 'publish'
    );

    $projects = new WP_Query( $args );

    set_transient( 'js_cached_projects', $projects, DAY_IN_SECONDS );
}

if( $projects->have_posts() ) {
    echo '<ul>';
    while( $projects->have_posts() ) {
        echo '<li><a href="' . get_permalink() . '">' . the_title( '', '', false ) . '</a></li>';
    }
    echo '</ul>';
}

In this instance we used an actual expiration time. This would be appropriate if you really don’t care if your new project shows up a day after you add it to your site.

If making sure that the project shows up immediately is important then I suggest adding a large expiration time. While we could set it to 0, this would make this transient autoload and we don’t really need it on every page. So in this case we’ll give it a far future expiration and create a function to flush our transient when a new project is published.

add_action( 'publish_post', 'jsh_purge_project_transient' );
function js_purge_project_transient( $ID, $post ) {
    if ( 'project' == $post->post_type ) {
        delete_transient( 'js_cached_projects' );
    }
}

Wrap up

I hope it is clear from this article that transients are much more than a cute little companion to the options API. When used correctly they provide a huge speed benefit to your website or at least decrease the resources used by your server.

Transients can store anything from an array of your most active commentors to whole post objects if needed.

Always keep in mind that caching can lead to a decrease in usability. Just because something can be stored as a transient doesn’t mean it should be stored there.

If you use the transients API for something particularly awesome let us know in the comments!

JSC0d3's Logo
About JSC0d3

JSC0d3 is an entrepreneur, online marketer, and an employee of an IT company. When not building websites, creating content, or helping customers improve their online business, spend time with their wife and two beautiful children. Although he still feels new in WordPress, he enjoys sharing what he has learned with all of you! If you want to get in touch with him, you can do this through this website.

On the same idea

Posted by | April 1, 2019

Images are vital components of every website Before you start questioning the importance of images, just try to imagine your favorite blog or website...

Posted by | March 5, 2019

I’ve been loosely following the noise and #wpdrama surrounding Gutenberg for as long as it has been around and honestly for the most part I’ve...

Posted by | February 24, 2019

To ensure that your site ranks highly in Search Engine Result Pages (SERPs), you’ll need to make it easy for search engine ‘bots’ to explore...

Previous PostBackNext Post

Leave here an impression