WordPress Development – Shortcodes (tutorial)
In the previous couple tutorials our main focus was the hooks API, which we use to trigger actions or filter data from the platform responses.
We will switch our focus to the WordPress’ Shortcodes API, which allow us, inside the post or page content, to use a group of macro codes delimited by brackets ([]) and inspired by the BBCode formatting language. At the moment the visitor’s access that content, those macros are substituted by the final result returned by the method they abstract.
Originally WordPress includes some shortcodes. An example, the shortcode [gallery] will be substituted by a gallery of the post’s associated images. A shortcode is described very much like an ordinary HTML tag, allowing both opening and closing attributes and tags. An example, [gallery id="3"] or [block]Lorem ipsum[/block].
In addition to the original WordPress’ shortcodes, both themes and plugins can add new ones. This is what we will discover in this article.
My first shortcode
As in previous articles, we will focus our modifications inside the theme functions.php file, located in /wp-content/themes/theme folder/functions.php, and only later will we apply the acquired knowledge to the development of a new extension.
To create a new shortcode, simply declare a function that implements it and associate it with the desired name with add_shortcode(). The way to do this is similar to what we already did for hooks:
function tutorial_hello_world_shortcode ( $atts, $content = null ) {
return 'Hello World!';
}
add_shortcode( 'hello', 'tutorial_hello_world_shortcode' );
That’s it! Try inserting the shortcode [hello] into the content of any post or page, and this will be replaced by the expression “Hello World!”.
Shortcode Attributes
As we have already seen, it is possible to pass attributes to a shortcode, just like in BBcode or in HTML. The most attentive will have noticed that the tutorial_hello_world_shortcode() function accepts a $atts parameter, which receives precisely an array of parameters.
You do not have to do anything special to get these parameters, WordPress API abstracts the parsing process to extract the parameters. A shortcode with the following format:
[hello name="log" other="Other attribute"]
will invoke our tutorial_hello_world_shortcode() function with the following array passed in the parameter:
Array
(
[name] => log
[other] => Other attribute
)
This means that we can modify our function to give it a more personal feeling:
function tutorial_hello_world_shortcode ( $atts, $content = null ) {
if (empty( $atts['name'] ))
return 'Hello World!';
else
return 'Hello ' . $atts['name'] . '!';
}
add_shortcode( 'hello', 'tutorial_hello_world_shortcode' );
We now may type the following shortcode
[hello name="log"]
and obtein
Hello log!
Shortcode Content
The above shortcode is limited to replacing all the code returned by the associated function. However, you can also use shortcodes to filter content blocks without overwriting them.
Now it is time for the second parameter to take action, which stores all content between opening and closing a shortcode.
function tutorial_hello_world_shortcode ( $atts, $content = null ) {
$name = empty( $atts['name'] ) ? 'World' : $atts['name'];
$output = "Hello $name! ";
if (!is_null( $content )) {
$output .= $content;
$output .= " Goodbye $name!";
}
return $output;
}
add_shortcode( 'hello', 'tutorial_hello_world_shortcode' );
The shortcode inserted in our article could have the format
[hello name="log"]Lorem ipsum dolor sit amet...[/hello]
resulting in
Hello log! Lorem ipsum dolor sit amet… Goodbye log!
The previous execution, containing only a simple [hello] code, with no attributes, would continue to work in the same way, since the code in our function checks for the existence or non-existence of the name code> and the content.
Default values
In our previous example code, a prior checking of the name attribute and the assignment of the default "world" occurs if it is not defined.
There is, however, a more elegant and recommended way of setting default values and Extract values from a set of attributes. Thus, instead of
$name = empty( $atts['name'] ) ? 'mundo' : $atts['name'];
we may write
extract( shortcode_atts( array(
'name' => 'World',
'other' => 'XPTO' // default values to each attribute
), $atts ) );
Useful Example
Shortcodes have numerous practical applications, one of which being the possibility to limit the visualisation of content taking into account a set of conditions.
For example, we can create a new shortcode [access] that limits access by unauthenticated users to content delimited by the shortcode.
function shortcode_access_control ( $atts, $content = null ) {
extract( shortcode_atts( array(
'capability' => 'read',
'error' => 'This content is for authorised users only.',
), $atts ) );
if (current_user_can( $capability ) && !is_null( $content ))
return $content;
else
return $error;
}
add_shortcode( 'access', 'shortcode_access_control' );
The shortcode can be invoked by typing the following text somewhere in the contents of a page or post:
[access]TOP SECRET![/access]
An authorised user will see:
TOP SECRET!
The remaining users will reach the following message:
This content is for authorised users only.
By default, the function will verify if the user have the authorisation to read (or the capability specified in the capability parameter). If authorisation is granted, the content is presented, otherwise the error message is displayed (or the message specified in the message parameter).
Since this new shortcode allows us to parameterise the required capability of the user and the error message, we have some flexibility in the validation and presentation's options. For example:
[access capability="admin" error="This content is for administrators only."]TOP SECRET![/access]
Precautions
Although powerful, the shortcodes API has some limitations to consider.
One of these limitations has to do with the fact that the function that interprets shortcodes sweeps the contents of articles and pages only once. This means that nesting shortcodes inside each other may not have the desired effects. For example:
[access]
My first shortcode: [hello]
[/access]
This will present the message "My first shortcode: [hello]" to all authenticated users, without parsing the "[hello]" shortcode as expected.
One way to get around this limitation is to pass the shortcode $content to the do_shortcode() function before returning it. Once the change is made, the function will look like this:
function shortcode_access_control ( $atts, $content = null ) {
extract( shortcode_atts( array(
'capability' => 'read',
'error' => 'This content is to all authorised users only.',
), $atts ) );
if (current_user_can( $capability ) && !is_null( $content ))
return do_shortcode( $content ); // Sub-shortcodes parsing
else
return $error;
}
add_shortcode( 'access', 'shortcode_access_control' );
Here, the do_shortcode() function compels WordPress to recursively interpret content in search of new shortcodes to parse.
More problematic is the fact that WordPress interpreter does not recognise two or more chained instances of the same shortcode. For example:
[shortcode]
[shortcode][/shortcode]
[/shortcode]
This will not work, even if we use the do_shortcode() function, because the shortcodes' interpreter privileges the speed of processing and not the correct interpretation of complex structures of these codes.
You should avoid using hyphens in the shortcode's name, as these are not correctly recognised by the interpreter due to a bug in current versions of WordPress. It is preferred to use the underscore character. Also no brackets are accepted in the value of an attribute: something like [shortcode attribute = "[value]"] will fail.
In the next tutorial, we'll explain the process of developing new widgets to use on themes.