Unleash the Power of the WordPress Shortcode API

    Jérémy Heleine
    Share

    WordPress introduced the Shortcode API in its version 2.5. This API allows developers to add some more or less complex features in their plugins or themes without the user having to insert any HTML code.

    The advantage of the Shortcode API is that developers do not need to use regular expressions to detect if the user included their shortcodes into their posts: WordPress automatically detects the registered shortcodes.

    In this tutorial, we will learn how to use the Shortcode API. We will create our own shortcodes, see how to use classic and unnamed attributes and also how to handle content.

    How Does the Shortcode API Work?

    Before using the Shortcode API, it is important to know how it works for the developer and for the end user.

    First, the developer registers their shortcode with the WordPress function add_shortcode(). Then, WordPress will automatically detect when this shortcode is used and call the corresponding function which will replace the shortcode with specific text or HTML code.

    For the user, it is really simple: they insert the shortcode into their post and this shortcode will be replaced each time the post is displayed.

    Shortcodes can be more or less complex. For example, we can find simple shortcodes like [contact] inserting a contact form in place of the tag. Other shortcodes can be filled with some content, like [hello]Me[/hello], others again can use attributes, like [sayhello to="You"]. These attributes can even be unnamed: [sphere 42] is for example a shortcode using unnamed attributes, added by the WP Photo Sphere plugin. Finally, some shortcodes can combine all of that, so this API can really make some powerful stuff.

    Shortcodes Without Attributes

    Before creating more complex shortcodes, we will begin with some examples without attributes. As usual, the code presented here should be put in a file of your project (e.g. functions.php for a theme).

    Create a Shortcode

    WordPress provides us with a specific function to register a shortcode: add_shortcode(). This function requires two parameters which are the shortcode’s name and the function to call each time this shortcode is found.

    add_shortcode('myshortcode', 'replace_my_shortcode');

    Basically, the callback function only requires one thing: the content replacing the shortcode must be indicated in the return instruction.

    function replace_my_shortcode() {
        return 'Hello World!';
    }

    This example works. If you write a post and insert into it the shortcode [myshortcode], it will be replaced by the text Hello World!. So it works. But it is limited and not very useful.

    That’s why using the content of the shortcode can be a good idea. The question is: how to retrieve this content if, for example, the user wrote [myshortcode]You[/myshortcode]?

    To get this content, we don’t need to change anything in the add_shortcode() call, but we will add two parameters to our replace_my_shortcode() function.

    function replace_my_shortcode($atts, $content = '') {
        return 'Hello ' . $content . '!';
    }

    In the first parameter, we can retrieve the attributes used by the author of the post, but we will see that in the next part. Here, we are more interested in the second parameter which contains the text between the two tags of our shortcode.

    If the user included some content in the shortcode, it will be in the $content variable. Otherwise, this variable will be an empty string so we can detect this case with a condition using, for example, the empty() function.

    function replace_my_shortcode($atts, $content = '') {
        if (empty($content))
            return 'Hello World!';
        else
            return 'Hello ' . $content . '!';
    }

    Shortcodes in Other Shortcodes

    We know how to create a shortcode and how to manage its content. But what if this content contains other shortcodes? For example, assume that we have two shortcodes: [name] which displays the author’s name, and [myshortcode] with the callback function above. Then, if the user writes [myshortcode][name][/myshortcode], the result will be “Hello [name]!”, which is surely not what we wanted here.

    Asking WordPress to parse shortcodes present in the content of another shortcode is possible and can be done with the do_shortcode() function, as you can see in the code below.

    function replace_my_shortcode($atts, $content = '') {
        return 'Hello ' . do_shortcode($content) . '!';
    }

    With this new callback function, our previous example gives us “Hello Jeremy” as a result (yes, we all have the same name!).

    However, it is not the default behavior, and there is a reason for that. In fact, if the user inserts, for example, a shortcode which displays an image in our myshortcode shortcode, the result will not be very pretty. But there is another problem, described below.

    With the do_shortcode() function, things like this:

    [myshortcode]
        [name]
    [/myshortcode]

    or:

    [myshortcode]
        [name][/name]
    [/shortcode]

    will be parsed without any problem. But other things like this:

    [myshortcode]
        [myshortcode]You[/myshortcode]
    [/myshortcode]

    are problematic. With the last replace_my_shortcode() function shown above, the result will be “Hello Hello !You![/myshortcode]”. In other words, the first [/myshortcode] tag found closes the first [myshortcode] tag. The other [myshortcode] tag become an empty shortcode and the last [/myshortcode] tag is simply ignored.

    Currently, there is no way to prevent this behavior using WordPress functions: do_shortcode() do not let us filter the shortcodes to search, so be careful if you intend to parse shortcodes into yours.

    Shortcodes with Attributes

    Attributes are useful to add some features to your shortcodes. With attributes, you will be able to get an image ID and other additional data like a name or whatever you might need to complete the shortcode’s content (if you handle content).

    Handle Attributes

    As we mentioned before, if WordPress finds attributes in the shortcode inserted by the user, it stores it in the first parameter of our callback function. This parameter is then an associative array containing all the used attributes.

    The structure of this array is exactly as we expected: its keys are the attributes’ names and its values are theirs. For example, if the user inserted the shortcode [myshortcode name="Here is my name" n="5"], then we will find this array:

    Array
    (
        [name] => Here is my name
        [n] => 5
    )

    in the first parameter of our function.

    All of the attributes used in the shortcode are stored in this array. However, if you use attributes, you may want to make some of them optional, with default values. That’s the reason why the WordPress function shortcode_atts() exists.

    function replace_my_shortcode($atts, $content = '') {
        $atts = shortcode_atts(array(
                'name' => 'World',
                'n' => 1
            ), $atts);
            
    	return 'Hello ' . $atts['name'] . '!';
    }

    This function will merge the $atts array provided by the user and the array given in first parameter which contains your default values. In the above example, if the user doesn’t fill the name attribute, we display “Hello World!”. Otherwise, the indicated name will be used.

    Unnamed Attributes

    Sometimes, an unnamed attribute can be a better solution than a traditional one. For example, to provide the ID of an image: [myimage 7] instead of [myimage id="7"]. The problem comes when we try to retrieve these special attributes. Without a name to search, which key should we choose?

    As for the others, unnamed attributes can be found in the first parameter of our callback function, with numbered keys. An example will be clearer than a long explanation, so assume that the user gives us the shortcode [myshortcode 7 name="My name"]. Then, we find the following array in the first parameter of our callback function.

    Array
    Array
    (
        [0] => 7
        [name] => My name
    )

    If there is more than one unnamed attribute, the only way to differentiate them from each other is their order. For example, we have the following array if the user inserts [myshortcode 7 name="My name" display test="value" 22].

    Array
    (
        [0] => 7
        [name] => My name
        [1] => display
        [test] => value
        [2] => 22
    )

    Often named attributes are preferable because they are easier to retrieve. However, unnamed attributes are not a big problem if you limit them to a small number or if they only serve as booleans. In fact, a shortcode like [myshortcode name="Me" show] is surely better for the user to type than [myshortcode name="Me" show="true"].

    For booleans, unnamed attributes are a good idea: we set them as true if they are present, and as false otherwise. However, a question remains here: what about default values?

    If you apply the shortcode_atts() function, the returned array will lose the numbered keys. In other words, you will not be able to retrieve unnamed attributes after applying the shortcode_atts() function.

    That’s why, if you only use unnamed attributes for booleans, you should create a function to normalize your attributes. You look over the array of attributes and, if the key is a number, you change the attribute to a named one, setting its value to true to indicate that the parameter is asked.

    function normalize_attributes($atts) {
        foreach ($atts as $key => $value) {
        	if (is_int($key)) {
                $atts[$value] = true;
                unset($atts[$key]);
        	}
    	}
    	
    	return $atts;
    }

    Then all you need to do now is to use this function before applying shortcode_atts(). That way, your unnamed attributes will still be there, and you can even give them default values, as you can see in the example below.

    function replace_my_shortcode($atts, $content = '') {
        $atts = normalize_attributes($atts);
        $atts = shortcode_atts(array(
                'name' => 'World',
                'hide' => false
            ), $atts);
        
        if (!$atts['hide'])
            return 'Hello ' . $atts['name'] . '!';
        else
            return '';
    }

    In this example, if the user inserts [myshortcode name="You"], we display “Hello You!”. However, if the shortcode retrieved is [myshortcode hide], then we display nothing. Absurd, but you understood the idea!

    In Conclusion

    The Shortcode API is a powerful tool for developers.

    It can do a lot of different things, from inserting a floating blockquote to displaying a video with many options.

    Using attributes lets your users set various details, but make sure to document these options to let the user find all the power of your shortcode.

    To make their life easier, you can also add a button to the WordPress editor to automatically insert your shortcode into a post. For example, if your shortcode uses media files, you might be interested in this recent article on adding a media button.

    Some examples of shortcodes inspired by the functions described in this tutorial are grouped in a test plugin which you can download here.