Don’t do_shortcode

Shortcodes are pretty cool, and the do_shortcode function is pretty neat as it can parse and execute shortcode callbacks from arbitrary strings, but that function invokes a fairly large regex every time it is called.

That regex looks for all registered shortcodes within a string. For each match, it runs a replacement with a callback function, which also take the time to parse the shortcode attributes, before finally calling the actual callback function that’s been registered with add_shortcode.

Regular expressions are pretty fast in PHP, especially for short strings, but do we really have to have WordPress do all that extra work, when all we really intended was to call our shortcode callback function?

echo do_shortcode( '[foo]' ); // Boo
echo foo_shortcode_callback(); // Yey!

I ran a quick search in the plugins directory, using the following regex:

do_shortcode\(\s*['"]\[

Not the best crafted regex, but it’s supposed to look for calls to do_shortcode followed by a string literal starting with an opening square bracket. Obviously it might get a few false positives for special cases, but it also misses quite a few matches where the shortcode string is put into a variable first.

I found over 600 entries in over 270 plugins, including some of my own. Guilty! So the lesson I learned today is: don’t use do_shortcode when you can use your callback function directly, which is much more efficient.

About the author

Konstantin Kovshenin

WordPress Core Contributor, ex-Automattician, public speaker and consultant, enjoying life in Moscow. I blog about tech, WordPress and DevOps.

11 comments

  • There’s a gotcha though. I was using the callback instead of the do_shortcode for a little WooCommerce addon I created. Then WooCommerce reorganized their code, stuck the callback in a class and my addon stopped working. Had to troll through their code base to get it fixed.

    • That’s a good point Peter, though if I were working with other plugins’ callbacks, I’d rather grab them from the $shortcode_tags global and call them with call_user_func like Core does :) Thanks for your comment!

  • How do I use the callback function instead of using the do_shortcode() function. I am guilty of this. Use this in a theme already, have deleted it though.

    • You can look into how do_shortcode_tag does it in wp-includes/shortcodes.php. Basically just grab your shortcode tag callback from the $shortcode_tags global and run it with call_user_func.

  • Very interesting. Building my first mobile-first responsive site and I’m going to great lengths to trim the code down for 3G connections. This will certainly help!

    • Thanks for your comment Michael. However, this doesn’t have anything to do with code length. In fact, calling a callback function directly can be longer (in lines of code, not CPU cycles) than calling do_shortcode :)

  • Cross Post from my reply to this from reddit:

    In an ideal world would it be necessary to use it? No. Not even in most cases. But a lot of instances require you not to find the best solution, but to find a solution quickly, or cheaply. Is there a performance hit? Sure. Will it require you to upgrade your web server? Test it out and see. I know that on the particular site I was working on, in the particular set of circumstances I was handed, the budget of the client, the amount of frustration involved, the time required to tear down s2member and find the solution needed; the course of action I chose was easily defensible and in the eyes of all parties involved the correct one. This was actually one of my junior developers who I made the decision for. It was an important lesson in professional web development. You don’t always get to make the most elegant solution. In the eyes of the customer’s budget, s2member has saved a shit load of time (read money), and caused minimal frustration on our part. Preventing burn out and time-sucks hunting an elegant solution to a problem you can solve with a line of code is more important than following best practices.

    All of this aside, if you properly cache your site, it won’t fucking matter anyways.

    The other take away from this: if your webserver breaks because of running some regex, you might want to upgrade from a soupcan to a raspberry pi.

    • Hi Patrick, thanks for stopping by. I disagree, but I do appreciate you coming here to comment.

      All of this aside, if you properly cache your site, it won’t fucking matter anyways.

      This is true for many things, but not a reason to write shitty code, sloppy SQL queries with full table scans and everlasting HTTP requests. Besides, there are many environments where page cache won’t be there to save the day, like WordPress.com where cached pages are never served to logged in users.

      What I’m trying to say is, if you can call your shortcode callback without invoking the regex engine — do it. If you have to spend hours (read money) to determine which function you’d have to call, then I guess it’s time to look for a new WordPress engineer ;)

      Cheers and thanks again!

    • “If you have to spend hours (read money) to determine which function you’d have to call, then I guess it’s time to look for a new WordPress engineer”

      Sometimes its not as easy as “which function to call”. Complex classes with many many _many_ layers of unreadable abstraction means spending time finding your way through the mind of whoever wrote it and or extending the class to end up with your own result. Does this mean that our hypothetical WordPress engineer is a moron because he didn’t do this? Maybe. Depends on how we are measuring this.

    • Why would you read through endless layers of abstraction to call a shortcode callback function? If it’s a WordPress shortcode it’s registered with add_shortcode and if it’s registered with add_shortcode it’s in the $shortcode_tags global:

      global $shortcode_tags;
      $func = $shortcode_tags['gallery'];
      echo call_user_func( $func, array( 'columns' => 4 ) );
      

      In any case, I was mostly referring to shortcodes that are defined by the same plugin you’re writing, though cross-plugin calls will work perfectly fine with the above method. You’d only have to make sure the shortcode tag is registered before looking for it, but that’s also true for do_shortcode. WordPress 3.6 will (most likely) introduce a new function for this: shortcode_exists.

      Cheers!