Using WordPress Transients To Speed Up Your PHP-Heavy Templates: Introduction

This post serves as background to the post, “How To: Use Transients To Speed Up WordPress Templates.”

WordPress is a powerful content management system, and over the years its ability to render very complex data in pleasing, human- and machine-readable formats has been greatly improved.

Thanks to changes in the taxonomy model, as well as improvements in the core functions that surround templates / themes, a designer doesn’t run into nearly as many cases these days of having to run lots of PHP code in a template file.

Even so, sometimes you just can’t avoid having to use a template file to run a complex WP Query, or otherwise put together a bunch of PHP to get something onto your WordPress-based site.

As you’ve no doubt discovered, depending on how code-heavy your template proves, and what resources your Web server has, this can lead to very long processing times, meaning very slow page loads.

Worse, on-template PHP code often can’t be cached, especially if it isn’t centered around a single post / page or a particular taxonomy term.

Vanity URLs And Complex WP Queries

The best example of this problem is a vanity URL that builds a page around several post types, taxonomies or external resources.

kalender
Simple calendar by matzekatze on OpenClipArt.
Let’s suppose, for example, you want to build a calendar of events page. The data which powers this page isn’t based in WordPress; maybe it’s all in a MySQL database someplace.

You want your calendar to be at a vanity URL, such as /calendar. So you do what many people do:

  • You create a blank page named Calendar, to reserve that URL;
  • you create a template for that page; and
  • you code the template to render the data from your MySQL database.

That solves the immediate problem: Your /calendar page will, indeed, render your calendar. But it’s not clean.

It’s not clean because ideally, we manage all WordPress content from WordPress.

That is, ideally we would have custom WordPress post types that represent events, custom taxonomies that represent event categories, and they would all be tied together via taxonomies and metadata; the WordPress core could then be leveraged to produce our calendar page.

But maybe you just can’t do that easily, because your calendar must be administered someplace else.

Then again, there are cases when we have all our data in WordPress, but the page we want to produce is very complex, requiring a custom WP Query or a requery / subquery of a previous WP Query.

For example, maybe we’re building a very complex category page. On that page, as we go through The Loop, we want to query for additional posts, based on whether or not posts that aren’t part of our main, category-based query share a particular tag or metadata value with the post that’s about to be shown.

This series of subqueries makes the page far more computationally expensive; and it’s difficult to cache, especially if the affected content is changing all the time.

WordPress Caching

What is this “cache” of which I speak?

Basically, a cache is a way for a Web server to store the finished product of a specific request, so that it doesn’t have to keep crunching code, over and over again, to produce the same result for many different people.

In other words, if the content we’re going to show at http://www.example.com/calendar is the same for everyone, why should the Web server have to keep rebuilding that page for every visit? Why not build it just a few times a day, or maybe even only once a day, and store the result?

The way we do that is with a cache.

When we use a cache, the Web server builds our page. Then, it saves that output either as a file, or in memory someplace.

The Web server then sends that saved output to users, until the saved output expires; that can happen after a given amount of time, or because the content was updated.

Sending this saved output is way easier on the Web server — since it doesn’t have to create new markup on the fly, connect to the database, or do other expensive tasks — so pages load faster and weird errors are far less likely to occur.

For example, WordPress.com uses Batcache, which saves content into memory. I use a plugin called WP Super Cache, which uses files to store saved output (and Cloudflare, which is a content delivery network). There are lots of other options out there, including W3 Total Cache, each of which has different variations on memory and file caching, as well as different capabilities.

What’s true of almost every cache plugin out there is that it can’t easily cache content that isn’t directly produced by WordPress, or at least what isn’t produced within The Loop or by other, common tasks WordPress performs; and it’s especially difficult to cache content that changes among groups of users, or for specific users.

WP3.0-ERD
The database diagram for WordPress 3.0, via the WordPress Codex. As you can see, the parts that go into building a WordPress page are spread across a lot of tables, and can be quite a chore for a webserver to put together.

In some cases, some out-of-the-ordinary content can be cached. For example, it’s possible to serve up differently cached pages based on a user agent string, so you can show different, cached content to Googlebot, or a mobile browser.

But usually, the more content you render in template-based PHP code, the less likely it is you can easily cache the results.

This is really, really true if your page requires some superglobal data about your user — such as reading a cookie, or a querystring / POST variable.

For example, the way WordPress knows you’re logged in is to check for the presence of a cookie. But because of the way caching works, we can’t always be sure that cookie gets read, before the cached content is sent to the user.

Couple the need to read a superglobal, along with dynamically building content based on that variable: Trying to cache that content is nearly always impossible via any method.

Transients: An Alternative To Caching

Fortunately, WordPress has a built-in method that allows us to store the results of some server-generated code in a cache-like way: transients.

A WordPress transient is basically a way to cache snippets of HTML markup for some period of time in the WordPress database.

In other words, it’s like a mini-cache, one that operates not on the whole-page level, but the parts-of-a-page level.

More often than not, the PHP code we have on a WordPress template page is really just a small building block of the page. When that’s the case, transients are often a better choice for improving page performance than is a whole-page cache.

Transients are also more flexible than a cached page. For example:

We can “selectively cache” parts of a page, and expire those parts at different times.

Suppose we have a page that contains three blocks of HTML markup. One block needs to be refreshed every 5 minutes; another, every hour; and the third, only once per day.

We can store all three of those blocks as transients, setting their expiration to be 5 minutes, 1 hour and 24 hours, respectively.

Our template would then check to see if the transient exists; if it does not (because it has expired), the template would then only regenerate the section that’s expired, rather than the entire page.

If we used a cache in this case, we’d have to regenerate the whole page every 5 minutes; if the content that expires every hour or every day is very expensive to compute, using transients instead of a whole-page cache significantly improves the performance of our page.

Transients make working with superglobals easier.

That’s because by using transients, we can free the parts of the page that need to examine a superglobal value to run with every request, and use transients to selectively pull in markup relevant to the value of that superglobal.

Let’s suppose you have a page that shows content to both authenticated (logged-in) users and the general public. If the user is authenticated, he sees some content, tagged “notice,” in addition to what the general public sees.

Traditionally that would be handled by enclosing a Loop, inside a current_user_can() if statement, to show that content:

//is user logged in?
if(current_user_can('read')) {
	//yes; get all published posts that are marked private and have the tag "notice"
	$authposts = new WP_Query(array('post_status' => array('publish', 'private'), 'post_tag' => 'notice'));
	//run The Loop to output titles of all such posts
	if($authposts->have_posts()) {
		while($authposts->have_posts()) {
			$authposts->the_post();
			echo get_the_title . "<br />";
		}
	}
}

//code for what public sees would appear here

But if the content to be show to all logged-in users is the same, we can simply build that content once, put it in as a transient, and then have the current_user_can function display the transient, rather than going through all the overhead of running a WP Query.

That still means the server has to do some extra work, to determine if the transient is needed and to go get it; but it’s not as difficult a job for the webserver as processing a WP Query like the one above.

Transients are optimized by some caching plugins. If you’re using Batcache, W3 Super Cache or a similar memory-based caching plugin, then your transient will be stored in memory, alongside the page(s) that use it. This will make loading the transient code go even faster.

How To Use Transients

In an upcoming post, I’ll feature the three ways that transients can improve your pages: as a way to save an entire block of code that normally would be written on a template; as a selective-expiration cache; and as a means to cache markup that relies on a superglobal.

The good news is, transients are super-easy to use: If you know how to make a WordPress theme, and you have a clear understanding of how often your content needs to refresh, it’s just a matter of using four, built-in WordPress functions.

All links in this post on delicious: https://delicious.com/dougvdotcom/using-wordpress-transients-to-speed-up-your-php-heavy-templates-introduction

Featured photo: “Dusk over the highway” (Portland, ME), by Ben McCanna via Flickr. © The Forecaster. Used by permission, all rights reserved.

Leave a Reply

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

  • Check out the Commenting Guidelines before commenting, please!
  • Want to share code? Please put it into a GitHub Gist, CodePen or pastebin and link to that in your comment.
  • Just have a line or two of markup? Wrap them in an appropriate SyntaxHighlighter Evolved shortcode for your programming language, please!