Improve the shortcode performance with the WordPress Transients API

March 24, 2021 Posted by Editorial Staff

This article analyses how to cache the output of shortcodes with the WordPress Transients API, to improve the website’s speed and reduce the weight on the server.

With this technique, you can quickly improve existing plugins, enhance the shortcode defined by your theme, or create new super-optimized WordPress plugins.

Cache the output of a simple shortcode

Our strategy is to generate only one single time the shortcode output, save this string in a transient, and reuse the output string an unlimited number of times.

Implementing this idea with a single shortcode that has no parameters is pretty straightforward. Technically you only need to use get_transient() to retrieve the transient content at the beginning of the function and set_transient() to set the transient content at the end of the function. 

function simple_shortcode_callback() {

    //Get the transient value
    $data = get_transient( 'my-transient' );

    //Return the transient value without executing the rest of the shortcode callback
    if ( $data !== false ) {
        return $data;
    }

    /**
     * Generate the shortcode code.
     *
     * In a real context here is where you generate the shortcode output with potentially heavy or slow operations like
     * database calls, analysis on existing data, HTTP requests to other website, etc.
     */
     $data = "<p>Simple shortcode content</p>";

    //Save the shortcode output in a transient
    set_transient( 'my-transient', $data, 10 );

    //Return the shortcode output
    return $data;

}

add_shortcode( 'simple-shortcode', 'simple_shortcode_callback' );

Delete the transient of a simple shortcode

It’s sometimes important to delete the shortcode transient, especially for those set with a long expiration. To perform this task, use the delete_transient() function with the transient’s name as the first argument.

delete_transient('my-transient');

Cache the output of a parameterized shortcode

A parametrized shortcode doesn’t have a unique output because it generates as many outputs as the possible combinations of parameter values. For this reason, we have to store each parameter combination in a transient that has a unique name based on the parameter combination.

We can generate a unique name based on the shortcode parameters by first serializing the shortcode parameters and then hashing them with the hash() function. Let’s first consider the serialization functions, PHP functions like serialize() or json_encode() are suitable for this purpose. In terms of hashing to ensure the best almost-unique string based on the shortcode parameters, I recommend using the sha512 algorithm.

Note that our function generates the unique transient name each time the shortcode runs. The transient name is used the first time at the beginning of the function to verify if the transient already existing, and the second time at the end of the function to save the shortcode output in the transient.

function parameterized_shortcode_callback( $atts ) {

    /*
    * Give the transient a unique name by concatenating the plugin or theme prefix (this part will be used to
    * distinguish the transient generated with this plugin or theme) with a hashed version of the serialized name of
    * the current function plus all the function parameters.
    *
    */
    $transient_name = 'prefix_' . hash( 'sha512', __FUNCTION__ . json_encode( $atts ) );

    //Get the transient value
    $data = get_transient( $transient_name );

    //Return the transient value without executing the rest of the shortcode callback
    if ( $data !== false ) {
        return $data;
    }

    /**
     * Generate the shortcode code by using the provided parameters.
     */
     $data = "<p>Parameterized shortcode content " . esc_html( $atts['first-parameter'] ) . "</p>";

    //Save the shortcode output in a transient
    set_transient( $transient_name, $data, 10 );

    //Return the shortcode output
    return $data;

}

add_shortcode( 'parameterized-shortcode', 'parameterized_shortcode_callback' );

Handle the shortcode content

If a shortcode has content, its value is available in the second argument of the shortcode callback. To create a transient that supports this type of shortcode, add the content variable in the line used to generate the transient name.

$transient_name = 'prefix_' . hash( 'sha512', __FUNCTION__ . json_encode( $atts ) . $content );

Delete the transients of a parameterized shortcode

In this case, we have to delete all the transients that start with our specified prefix.

WordPress normally saves the transients in the options database table (yes, the same database table used to save the plugin options), so we have to query this table and use the MySQL LIKE operator to find only the transients that start with our prefix. Note that in the database, the beginning of the transient name is composed of the string “_transient_” concatenated with our prefix.

/**
 * Delete the transients with the 'myprefix' prefix.
 *
 * @return bool True if the transients have been deleted, otherwise returns false.
 */
function delete_plugin_transients() {

    /*
    * Get all the transients used by our plugin/theme with a query that looks for transients that start with a query
    * specific string.
    */
    global $wpdb;
    $prefix = '_transient_myprefix_';
    $safe_sql = $wpdb->prepare( "SELECT option_name FROM $wpdb->options WHERE option_name LIKE %s", $prefix . '%' );
    $transients = $wpdb->get_results( $safe_sql, ARRAY_A );

    //Iterate through the retrieved transient and delete them
    if ( count( $transients ) > 0 ) {
        foreach ( $transients as $transient ) {
            delete_transient( str_replace( '_transient_', '', $transient['option_name'] ) );
        }

        return true;
    }

    return false;

}

When this technique completely transforms the shortcode performance

Imagine the amount of time saved and CPU resources released in the following scenarios.

Shortcodes that run complex or multiple database queries

It’s easy to spend too much time querying a database with PHP. For example, in a query used to retrieved thousands of rows for a shortcode-based table.

Shortcodes that run data analysis tasks

When you have to read a significant amount of data, change the shape of the data, or perform calculations. For example, in Soccer Engine, a plugin used by clubs and sport-related websites to store and display soccer data, transients are used to store the shortcode output of many shortcodes used to generates statistics. 

Shortcodes that perform HTTPS requests to REST API

Sometimes you have to retrieve the information displayed in the shortcode from a REST API. In these situations, the requests are performed with CURL, and the time required to complete the requests has a detrimental impact on page loading time.