The WordPress Shortcode API is going through a great deal of a refactoring process, which was necessary since a long time ago. Though still in its initial stages, one of the main goals is to provide more strict guidelines about the way shortcodes should be used, specially regarding what can and what cannot be passed as attributes, being HTML code the more complicated case. This is part of an ongoing discussion that began when WordPress 4.2.3 was launched, and lots of sites broke because they were using shortcodes in a way the update didn’t support anymore.
I’ll skip my point of view about the way the update was managed by the Core team, and I won’t dare to say that there’s a “wrong” way to use shortcodes, since I think that any provided tool should be used in any possible way that’s allowed by its internal logic. I’m just gonna stick to talk about a practice that can help to prevent some issues with the Shortcode API.
What do I mean with this? I know for a fact (because I published a couple of plugins and people often ask me about this) that a lot of developers write shortcodes right inside their template files. For example, I’m used to see things like this inside templates:
<div id="post-content"> <?php do_shortcode( '[shortcode arg1="value1" arg2="value2"]' . get_the_content() . '[/shortcode]' ); ?> </div>
For most cases, something like that will work fine. That’s something that both the Shortcode API and the templating system allow to do, so it’s OK to do it. But you need to keep in mind that this is an approach that comes with a lot of possible issues, the most important being that PHP files are not so easy to be updated as posts are (at least for non-tech-savvy users) in case the way of parsing shortcodes changes (as it happened in 4.2.3). There are other issues, and I’m gonna mention some of them, but I think this one should be our main focus.
What has a lot more sense in the context of templates is to just not use shortcodes at all, and just leave them for the post editor. Inside PHP files we have a lot more control over the output we’re generating, and we should take advantage of that as much as we can (and as long as our code doesn’t end up being a mess). By using shortcodes inside templates we lost a lot of control and internal consistency, since shortcodes are not exactly logic, and not exactly markup. They fall in the middle of both categories, and that’s great while we are generating content, but not while we’re defining how that content is gonna be served to the user. Shortcodes are just a quick front-end for internal functionality that allows to manage that functionality without having to write actual code, but often there’s not really a good reason to use them while we’re actually coding something.
Shortcodes actually work as “masks” for functions and class methods that are defined somewhere within the code of our application, being that a plugin, a theme or the WordPress Core itself. These functions and class methods are accessible and working code that we can use and handle directly instead of using the shortcodes that call them. This way we can get rid of all the parsing-related problems we could step into while using shortcodes, and keep internal consistency inside our PHP files at the same time.
So, how can you know what functions should be used? Functions become shortcodes by calling register_shortcode()
. If you have a shortcode called [shortcode]
, you should look for the line that registers it with register_shortcode()
. So you’re gonna find something similar to this in some part of your application:
register_shortcode( 'shortcode', 'my_shortcode_handler' );
The first parameter is the name of the shortcode, and the second one is the name of the function that handles it. So inside your template files, as they’re written using PHP, you can use that function instead of parsing the shortcode with do_shortcode()
. If you look for the declaration of my_shortcode_handler()
, you’ll find something like this:
function my_shortcode_handler( $attr, $content ) { // some PHP code ... }
Notice that the first parameter, $attr
, is the list of arguments that you’re passing to the shortcode, while the second, $content
, is whatever you put inside the opening and closing tags. So we can refactor our template this way:
<div id='post-content'> <?php echo my_shortcode_handler( array( 'arg1' => 'value1', 'arg2' => 'value2' ), get_the_content() ); ?> </div>
By doing this, we make sure the function always receives what we need to be processed without worrying for possible modifications to the Shortcode API that don’t support the current implementation of our shortcode.
There are, however, moments when a call to a shortcode can indeed be a good solution to a problem. For example, when we’re working with a shortcode that can be overridden by many plugins, and/or we don’t really care about what function is the one that’s operating behind the it. We just need the markup printed by the shortcode, whatever that markup is. Such can be the case of [gallery]
, which we may want to use to show a gallery for our plugin or theme without caring about other plugins that may be redefining or extending the shortcode. We just want it to be displayed with the markup and styles of the current gallery functionality, so it’s OK to do something like this:
$image_ids = array( 20, 38, 24, 78, 93 ); ?> <div id='post-gallery'> <?php echo do_shortcode( '' ); ?> </div>
In this case, we sacrifice some internal consistency in order to get a very specific functionality, and that’s fine for some situations. However, as every recommended practice, the decision about how it should be applied depends on every particular context, but I think it’s always good to know the possible alternatives that we can count on, and have them present when we need to make decisions.