Created
July 9, 2015 13:34
-
-
Save rask/c20b195ce0cc71778894 to your computer and use it in GitHub Desktop.
WordPress transients
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/**================================================================================== | |
* WordPress transients example. | |
* | |
* This file shows the normal usage for WordPress transients with beefy comments. | |
* | |
* @author Otto J. Rask | |
*/ | |
/**================================================================================== | |
* INTRODUCTION | |
* | |
* WordPress transients are a way to cache temporary data, such as database queries, | |
* API request results, post likes, and even whole HTML templates. | |
* | |
* Transients are saved to a cache system, such as memcache or Redis. By default | |
* WordPress uses its own database, so transients work everywhere where WordPress | |
* does. | |
* | |
* Transients are not hard to use, but there are some good and bad use cases. Also a | |
* few gotchas that might make your site work strangely or break in a bad case. | |
*/ | |
/**================================================================================== | |
* STEP 1 | |
* Define the transient key to which to save and from which to load the transient | |
* cacheable value. | |
* | |
* Transient keys should be unique per value. Example for generating transient keys: | |
* | |
* - Grab a WP_Query and serialize its query variables, then pipe it through | |
* `md5()` to generate a simple alphanumeric string for the key. Then prefix it | |
* with a custom key, such as ` myquery_`, which results in | |
* `myquery_123456abcdef` or similar. | |
* | |
* The key (no pun intended) is to make the key unique regarding to what is being | |
* saved to the transient cache. | |
* | |
* Remember: as of now/as far as I know, the WP options table (where transients are | |
* stored by default) limits the key length. To be safe, limit the transient key | |
* length (including prefixes) to 45 characters. If you have a key, you can use | |
* `substr($key, 0, 45);` to trim excess characters. | |
*/ | |
// Custom hand-written key (remember 45 char limit!): | |
$custom_key = 'mykey_myuniqueidentifierhere' | |
// Or perhaps a generated key? Following are WP_Query arguments for some kind of | |
// posts that have a `post_thumbnail` set: | |
$query_args = array( | |
'post_type' => 'post', | |
'post_status' => 'publish', | |
'meta_key' => '_thumbnail_id', | |
'meta_value' => 0, | |
'meta_compare' => '>', | |
'posts_per_page' => 3 | |
); | |
// Serialize the arguments and generate a unique key for transients to use. | |
$query_key = 'myvalue_' . md5(serialize($query_args)); | |
// Following trims it down to 45 characters. | |
$query_key = substr($query_key, 0, 45); | |
/**================================================================================== | |
* STEP 2 | |
* Get the transient value from cache. | |
* | |
* The cache can be the WP database, memcache, Redis, or any other object cache that | |
* is handled through `wp-content/object-cache.php`. Some object caches need plugins | |
* or customizations to your web server (e.g. memcache needs the memcached service to | |
* run in order to function properly). | |
* | |
* You could even write your own implementation that saves the transient data to a | |
* CSV file (not recommended, as transients work best with fast systems, IO is not | |
* fast generally). | |
* | |
* WordPress defaults to using a database. It uses the `wp_options` table to save | |
* transients. | |
*/ | |
// Get transient cached data for the key we generated above. | |
$transient_value = get_transient($query_key); | |
// Oh wow was it that easy? | |
/**================================================================================== | |
* STEP 3 | |
* Validate if the value has been cached to the transient. | |
* | |
* If the value has been cached to the transient (using the unique key), | |
* `$transient_value` now contains the exact value that has been saved to it. | |
* | |
* If no value has been saved, `get_transient` will return `false`. Remember to | |
* validate the `false` value strictly (using `===` or `!==`), because a | |
* transient could contain a "falsy" value such as `0` or `''` (empty string), | |
* which evaluate to `false` when using loose checking (`==` or `!==`). | |
*/ | |
// Strict check for false (empty) transient. | |
if ($transient_value === false) { | |
// Oh no, transient value does not exist... | |
// ... fetch and save the transient here ... | |
} | |
// Strict check for existing transient value. | |
elseif ($transient_value !== false) { | |
// Transient value exists! | |
// ... do what you want with the value here ... | |
} | |
/**================================================================================== | |
* STEP 4 | |
* Save a transient in case it does not exist (`get_transient` returned `false`). | |
* | |
* Saving a transient looks a bit similar to loading a transient with | |
* `get_transient`. The difference is we're using `set_transient` and we need to have | |
* a value to save. | |
* | |
* Additionally `set_transient` accepts a third parameter, a timeout value. The | |
* timeout value determines how many **seconds** a transient is considered to be | |
* valid before destroying the value (in which case `get_transient` would return | |
* `false` again). | |
* | |
* If no timeout is set, the transient value is assumed to be valid **forever**. | |
* Don't do this, as then it would be the same to just use the Options API. Always | |
* define a time out, even if it is a whole year (`60*60*24*365` seconds). | |
*/ | |
// Transient returns false. | |
if ($transient_value === false) { | |
// We defined some query arguments earlier, use them to make a WP_Query. | |
$query = new WP_Query($query_args); | |
// WP_Query just did an expensive database request above. Now might be good spot | |
// to save the query so we do not have to access the database the next time. | |
// Save the query as a transient using the same transient key we used to get the | |
// `$transient_value` earlier. Set the query to invalidate in 30 minutes. | |
set_transient($query_key, $query, 60*60*30); | |
} | |
/**================================================================================== | |
* STEP 5 | |
* Use the transient value in case it returned something else than `false`. | |
* | |
* If `get_transient` didn't return `false`, it means that we could fetch the wanted | |
* transient value from the database or memcahce or Redis or something! | |
* | |
* If we follow the PHP file from top to bottom, the saved transient was saved to | |
* the cache in STEP 4 and now we fetched it. | |
* | |
* This means that `$transient_value` currently holds the same thing that was saved | |
* to the cache, so essentially we can now just do `$query = $transient_value`. | |
*/ | |
// We got some value from the cached transient. | |
elseif ($transient_value !== false) { | |
// Use the cached query. | |
$query = $transient_value; | |
} | |
/**================================================================================== | |
* STEP 6 | |
* Onwards! We have what we need, cached or not. | |
* | |
* Now the `$query` variable contains a WP_Query object regardless whether it was | |
* loaded from transient cache or not. Next up we can use it just like we're used to: | |
*/ | |
// Start Wordpress loop. `$query` contains a good-ole WP_Query for you to use. | |
// If we have any posts. | |
if ($query->have_posts()) : | |
// While we have any posts. | |
while ($query->have_posts()) : the_post(); | |
?> | |
<div class="post"> | |
<h2><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h2> | |
... | |
</div> | |
<?php | |
endwhile; | |
endif; | |
/**================================================================================== | |
* STEP 7 | |
* Destroy the cache if needed. | |
* | |
* One problem with caches is that sometimes they need to be cleared sooner than a | |
* default timeout value says. | |
* | |
* WordPress provides `delete_transient` for deleting transients. Just pass in the | |
* unique transient key and WP will take care of it. | |
*/ | |
// If we want to destroy the transient each time a logged in used visits the page: | |
if (is_user_logged_in()) { | |
delete_transient($transient_key); | |
} | |
// Done! | |
/**================================================================================== | |
* FINAL WORDS | |
* | |
* Be careful of what you save to transients. You should never save private data to a | |
* transient that could end up being shown publicly. | |
* | |
* Don't use transients to store permanent data. Transients and other caches are | |
* designed to store temporary data that could disappear at any time. Make sure your | |
* website works with and without transients (see the `if-else` checks above.). | |
*/ | |
/** | |
* By the way, this PHP file should work as is in a WordPress theme. Copy-paste it to | |
* Your theme and use PHP `include` in a template file such as `index.php`. | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment