WordPress Code: Earlier Shortcodes

WordPress shortcodes run at priority 11 which is after wpautop(), wptexturize(), and other various default filters. While this can be desirable so that wpautop() doesn’t affect the output of the shortcode (the function is well known for not being perfect), it can also be a drawback as wptexturize() will malform your shortcode contents.

Take this shortcode usage for example:

This is some text.

[foobar]This is how you "quote" things.[/foobar]

This is some more text.

The “foobar” shortcode callback will receive the following string:

This is how you “quote” things.

Well that’s not obviously right. Okay, so you could fix it with some str_replace()‘es to reverse the fancy quotes and other changes, but why go to all the trouble?

Instead, let’s just make the shortcode run before priority 10 when wpautop(), wptexturize(), etc. mangle our shortcodes. That way we can process the shortcode as the user typed it and when once we’re done let wpautop() and it’s friends handle the content like they were designed to do.

First, here’s the code I use. I’ll explain it afterward.

function foobar_run_shortcode( $content ) {
	global $shortcode_tags;

	// Backup current registered shortcodes and clear them all out
	$orig_shortcode_tags = $shortcode_tags;
	remove_all_shortcodes();

	add_shortcode( 'foobar', 'shortcode_foobar' );

	// Do the shortcode (only the one above is registered)
	$content = do_shortcode( $content );

	// Put the original shortcodes back
	$shortcode_tags = $orig_shortcode_tags;

	return $content;
}

add_filter( 'the_content', 'foobar_run_shortcode', 7 );

The function starts by global’ing the variable $shortcode_tags. This is the variable that contains a list of all registered shortcodes. We then make a copy of that variable (so we can restore it later) and then empty it out so that no shortcodes are registered.

Now that there’s no shortcodes registered, we register our shortcode and call do_shortcode() which is the function that replaces shortcodes with their contents. Once that’s done, we restore all of the previously registered shortcodes (this also unregisters our shortcode so it doesn’t run again) and return the result of the do_shortcode() call.

Lastly we register that function as a filter but at an earlier priority than wptexturize() so that it will run first. Any number between 1 and 9 will do — I just use 7 as it’s fairly late but still leaves room for other filters to come after it but before wptexturize(). That and it’s my favorite number. ;)

Questions? Improvements? Then leave a comment. :)

19 comments to WordPress Code: Earlier Shortcodes

  1. Ozh says:

    What if some plugin registers shortcode earlier than 7 ? In such a case, the global $shortcode_tags would not be empty and the early do_shortcode would actually run twice those shortcodes, right?

  2. Viper007Bond says:

    $shortcode_tags is almost certainly not empty by the time my filter comes along. The gallery shortcode for example is registered. That’s why we back it up before we clear it out.

    Then once it’s empty and no shortcodes are registered, we register the shortcodes we want to parse early. Once those shortcodes are processed (only those will be because they’re the only ones registered), we restore the original $shortcode_tags from the backup we made earlier which puts everything back exactly as it was and in turn removes our early shortcode.

    When my filter runs doesn’t matter as it puts things back exactly as they were before it ran.

    By the way, I use this trick in my SyntaxHighlighter plugin so that I can replace my shortcodes with pre tags before wpautop() adds paragraphs to the insides of my shortcodes (once the shortcode content is wrapped in a pre, wpautop() won’t touch it).

  3. Ozh says:

    Doh, me silly, I missed the clearing line of course.

  4. Very neat hack. Sometimes I’m glad WordPress is a silly mess of non-OO global variables :)

  5. Viper007Bond says:

    Rewrote the post to use a better example of why you may want to run earlier shortcodes, namely wptexturize() wreaking havok on your shortcode contents.

  6. [...] Shared WordPress Code: Earlier Shortcodes « Viper007Bond.com [...]

  7. Alex Leonard says:

    Hey Viper,

    Just came across this post and it totally saved my wrecked head!

    Was having issues where WP was inserting p elements in my shortcode content.

    I’m trying to make things easier for my brother to put a special offer box on his new website but want to maintain flexibility. This way he can do

    [special title="blah"]
    // put some content here, most likely a list
    [/special]

    Interestingly the only thing that was a little strange was if, in my shortcode function, I placed HTML comments after closing divs (something I do to make it easier to find what’s going on where in my source), I still had WP trying to encapsulate the HTML comments in a p element. When I removed the HTML comments everything worked fine!

    Thanks dude.

  8. OddOneOut says:

    Hi, this is a very nice approach. I know this is an old post, but I still want to ask something. You said that “While this can be desirable so that wpautop() doesn’t affect the output of the shortcode (the function is well known for not being perfect)”, but could you please tell me how wpautop can affect the shortcodes? I find wpautop affects the shortcodes even if it is called before. For example,

    <div>test</div>

    will result in test. P tags are never closed. Now, if wpautop is called after shortcodes, it would just be like this test, which is what we want.

    Do you have any suggestions? Thank you!

  9. OddOneOut says:

    I’m sorry, forgot to code the codes.

    I mean this

    [code]<div>test</div>

    becomes this

    </p><div>test</div><p>
  10. Lucas says:

    Thank you for this!

  11. [...] before priority 10? This approach was introduced by Viper, a well-known WordPress developer, on his blog. I will merge his codes into our codes (with some minor modifications) and now we have this: [...]

  12. Ciantic says:

    I love workarounds, it avoids hassles of patches.

    But needless to say, should not we promote WordPress patch and syntax like:

    add_shortcode("foo", "foo_shortcode", 7)

    Where last attribute defines the priority?

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

If you wish to post code, write it like [code]blah[/code] so it will display properly.